Merge develop into main (#339)

- Update ILI9341 driver to v2.0.1
- Lots of code cleanup for apps
- Refactor app "type" into "category" and added flags to the manifest (for show/hide statusbar and for hidden apps)
- Rename some ElfApp-related functionality and improved the way the static data was managed
- Rename "filebrowser" to "files"
- Added cstring functions to tt_init.cpp
- Minor fix in Boot app
- Updated external apps for SDK changes
This commit is contained in:
Ken Van Hoeylandt 2025-09-17 23:42:49 +02:00 committed by GitHub
parent a2af95b92d
commit faab6d825f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
82 changed files with 349 additions and 360 deletions

View File

@ -1,5 +1,5 @@
dependencies:
espressif/esp_lcd_ili9341: "2.0.0"
espressif/esp_lcd_ili9341: "2.0.1"
atanisoft/esp_lcd_ili9488: "1.0.10"
espressif/esp_lcd_touch: "1.1.2"
atanisoft/esp_lcd_touch_xpt2046: "1.0.5"

View File

@ -48,13 +48,6 @@ if (DEFINED ENV{ESP_IDF_VERSION})
set(EXCLUDE_COMPONENTS "Simulator")
# LVGL
# set(LV_CONF_PATH Libraries/lvgl/src/lv_conf_kconfig.h)
# get_filename_component(
# LV_CONF_PATH Libraries/lvgl/src/lv_conf_kconfig.h ABSOLUTE
# )
# add_compile_definitions(LV_CONF_PATH="${LVGL_CONFIG_FULL_PATH}")
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_write" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_button_create" APPEND)

View File

@ -3,20 +3,18 @@
## Higher Priority
- Show a warning in the web installer when flashing CYD 28R board regarding v1/v2/v3
- Fix Development service: when no SD card is present, the app fails to install. Consider installing to `/data`
Note: Change app install to "transfer file" functionality. We can have a proper install when we have app packaging.
Note: Consider installation path option in interface
- External app loading: Check the version of Tactility and check ESP target hardware to check for compatibility.
- Make a URL handler. Use it for handling local files. Match file types with apps.
Create some kind of "intent" handler like on Android.
The intent can have an action (e.g. view), a URL and an optional bundle.
The manifest can provide the intent handler
- Update ILI934x to v2.0.1
- Apps with update timer in onCreate() should check `lvgl::isStarted()`
- CrowPanel Basic 3.5": check why GraphicsDemo fails
- CrowPanel Basic 3.5": check why System Info doesn't show storage info
- Update to LVGL v9.3 stable
- Create `app::getSettingsPath()` to get paths to properties files by first trying sd card and then trying `/data`
- When an SD card is detected, check if it has been initialized and assigned as data partition.
If the user choses to select it, then copy files from /data over to it.
Write the user choice to a file on the card.
File contains 3 statuses: ignore, data, .. initdata?
The latter is used for auto-selecting it as data partition.
## Medium Priority
@ -33,7 +31,6 @@
## Lower Priority
- Rename `filebrowser` to `files` and `FileBrowser.cpp` to `Files.cpp`
- Implement system suspend that turns off the screen
- The boot button on some devices can be used as GPIO_NUM_0 at runtime
- Localize all apps
@ -95,3 +92,24 @@
- Display touch calibration
- RSS reader
- Static file web server (with option to specify path and port)
- Diceware
# App Store
- Register user
- Name
- Company
- Email
- Password
- Create/destroy session
- List apps
- Install app
- App ID
- App version
- Add/remove API key
- Upload app
- Category
- File
- Name
- Description
- List apps

View File

@ -14,7 +14,6 @@ static void destroyApp(void* app) {
}
ExternalAppManifest manifest = {
.name = "Hello World",
.createData = createApp,
.destroyData = destroyApp,
.onShow = onShow,

View File

@ -1,7 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.5.0
sdk=0.6.0-SNAPSHOT1
platforms=esp32,esp32s3
[app]
id=com.bytewelder.calculator

View File

@ -14,7 +14,7 @@ import shutil
import configparser
ttbuild_path = ".tactility"
ttbuild_version = "2.1.0"
ttbuild_version = "2.1.1"
ttbuild_cdn = "https://cdn.tactility.one"
ttbuild_sdk_json_validity = 3600 # seconds
ttport = 6666
@ -618,7 +618,7 @@ if __name__ == "__main__":
print_help()
exit_with_error("Commandline parameter missing")
uninstall_action(manifest, sys.argv[2])
elif action_arg == "bir":
elif action_arg == "bir" or action_arg == "brrr":
if len(sys.argv) < 3:
print_help()
exit_with_error("Commandline parameter missing")

View File

@ -89,7 +89,6 @@ static void onDestroy(AppHandle appHandle, void* data) {
}
ExternalAppManifest manifest = {
.name = "Hello World",
.onCreate = onCreate,
.onDestroy = onDestroy
};

View File

@ -1,7 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.5.0
sdk=0.6.0-SNAPSHOT1
platforms=esp32,esp32s3
[app]
id=com.bytewelder.graphicsdemo

View File

@ -14,7 +14,7 @@ import shutil
import configparser
ttbuild_path = ".tactility"
ttbuild_version = "2.1.0"
ttbuild_version = "2.1.1"
ttbuild_cdn = "https://cdn.tactility.one"
ttbuild_sdk_json_validity = 3600 # seconds
ttport = 6666
@ -618,7 +618,7 @@ if __name__ == "__main__":
print_help()
exit_with_error("Commandline parameter missing")
uninstall_action(manifest, sys.argv[2])
elif action_arg == "bir":
elif action_arg == "bir" or action_arg == "brrr":
if len(sys.argv) < 3:
print_help()
exit_with_error("Commandline parameter missing")

View File

@ -15,7 +15,6 @@ static void onShow(AppHandle app, void* data, lv_obj_t* parent) {
}
ExternalAppManifest manifest = {
.name = "Hello World",
.onShow = onShow
};

View File

@ -1,7 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.5.0
sdk=0.6.0-SNAPSHOT1
platforms=esp32,esp32s3
[app]
id=com.bytewelder.helloworld

View File

@ -14,7 +14,7 @@ import shutil
import configparser
ttbuild_path = ".tactility"
ttbuild_version = "2.1.0"
ttbuild_version = "2.1.1"
ttbuild_cdn = "https://cdn.tactility.one"
ttbuild_sdk_json_validity = 3600 # seconds
ttport = 6666
@ -618,7 +618,7 @@ if __name__ == "__main__":
print_help()
exit_with_error("Commandline parameter missing")
uninstall_action(manifest, sys.argv[2])
elif action_arg == "bir":
elif action_arg == "bir" or action_arg == "brrr":
if len(sys.argv) < 3:
print_help()
exit_with_error("Commandline parameter missing")

View File

@ -64,14 +64,14 @@ public:
auto lock = getMutex().asScopedLock();
lock.lock();
if (resultHolder != nullptr) {
if (resultHolder == nullptr) {
return false;
}
outResult = resultHolder->result;
outBundle = std::move(resultHolder->resultData);
resultHolder = nullptr;
return true;
} else {
return false;
}
}
};

View File

@ -13,7 +13,7 @@ enum class Result;
typedef union {
struct {
bool showStatusbar : 1;
bool hideStatusbar : 1;
};
unsigned char flags;
} Flags;

View File

@ -1,6 +1,6 @@
#pragma once
#include "Tactility/app/AppRegistration.h"
#include <Tactility/app/AppRegistration.h>
#include <string>
@ -10,13 +10,7 @@ class App;
class AppContext;
/** Application types */
enum class Type {
/** Boot screen, shown before desktop is launched. */
Boot,
/** A launcher app sits at the root of the app stack after the boot splash is finished */
Launcher,
/** Apps that generally aren't started from the desktop (e.g. image viewer) */
Hidden,
enum class Category {
/** Standard apps, provided by the system. */
System,
/** The apps that are launched/shown by the Settings app. The Settings app itself is of type AppTypeSystem. */
@ -60,6 +54,15 @@ public:
typedef std::shared_ptr<App>(*CreateApp)();
struct AppManifest {
struct Flags {
constexpr static uint32_t None = 0;
/** Don't show the statusbar */
constexpr static uint32_t HideStatusBar = 1 << 0;
/** Hint to other systems to not show this app (e.g. in launcher or settings) */
constexpr static uint32_t Hidden = 1 << 1;
};
/** The identifier by which the app is launched by the system and other apps. */
std::string id = {};
@ -69,12 +72,15 @@ struct AppManifest {
/** Optional icon. */
std::string icon = {};
/** App type affects launch behaviour. */
Type type = Type::User;
/** App category helps with listing apps in Launcher, app list or settings apps. */
Category category = Category::User;
/** Where the app is located */
Location location = Location::internal();
/** Controls various settings */
uint32_t flags = Flags::None;
/** Create the instance of the app */
CreateApp createApp = nullptr;
};

View File

@ -14,9 +14,7 @@ typedef void (*OnShow)(void* appContext, void* _Nullable data, lv_obj_t* parent)
typedef void (*OnHide)(void* appContext, void* _Nullable data);
typedef void (*OnResult)(void* appContext, void* _Nullable data, LaunchId launchId, Result result, Bundle* resultData);
void setElfAppManifest(
const char* name,
const char* _Nullable icon,
void setElfAppParameters(
CreateData _Nullable createData,
DestroyData _Nullable destroyData,
OnCreate _Nullable onCreate,

View File

@ -1 +0,0 @@
#pragma once

View File

@ -37,4 +37,5 @@ namespace tt::app::alertdialog {
* @return a value greater than 0 when a selection was done, or -1 when the app was closed clicking one of the selection buttons.
*/
int32_t getResultIndex(const Bundle& bundle);
}

View File

@ -1,6 +1,6 @@
#pragma once
namespace tt::app::filebrowser {
namespace tt::app::files {
void start();

View File

@ -3,7 +3,6 @@
#include <Tactility/Bundle.h>
#include <string>
#include <vector>
/**
* Start the app by its ID and provide:

View File

@ -23,4 +23,5 @@ namespace tt::app::selectiondialog {
* @return a value greater than 0 when a selection was done, or -1 when the app was closed without selecting an item.
*/
int32_t getResultIndex(const Bundle& bundle);
}

View File

@ -1,8 +1,8 @@
#pragma once
#include "Tactility/app/AppContext.h"
#include "Tactility/app/AppManifest.h"
#include "Tactility/app/ElfApp.h"
#include <Tactility/app/AppContext.h>
#include <Tactility/app/AppManifest.h>
#include <Tactility/app/ElfApp.h>
#include <Tactility/Bundle.h>
#include <Tactility/Mutex.h>
@ -29,7 +29,7 @@ class AppInstance : public AppContext {
const std::shared_ptr<AppManifest> manifest;
State state = State::Initial;
LaunchId launchId;
Flags flags = { .showStatusbar = true };
Flags flags = { .hideStatusbar = true };
/** @brief Optional parameters to start the app with
* When these are stored in the app struct, the struct takes ownership.
* Do not mutate after app creation.

View File

@ -6,9 +6,9 @@
#include <vector>
#include <dirent.h>
namespace tt::app::filebrowser {
namespace tt::app::files {
class State {
class State final {
public:
@ -44,7 +44,7 @@ public:
template <std::invocable<const std::vector<dirent> &> Func>
void withEntries(Func&& onEntries) const {
mutex.withLock([&]() {
mutex.withLock([&] {
std::invoke(std::forward<Func>(onEntries), dir_entries);
});
}
@ -61,13 +61,9 @@ public:
std::string getSelectedChildPath() const;
PendingAction getPendingAction() const {
return action;
}
PendingAction getPendingAction() const { return action; }
void setPendingAction(PendingAction newAction) {
action = newAction;
}
void setPendingAction(PendingAction newAction) { action = newAction; }
};
}

View File

@ -2,7 +2,7 @@
#include <string>
namespace tt::app::filebrowser {
namespace tt::app::files {
bool isSupportedAppFile(const std::string& filename);
bool isSupportedImageFile(const std::string& filename);

View File

@ -2,14 +2,14 @@
#include "./State.h"
#include "Tactility/app/AppManifest.h"
#include <Tactility/app/AppManifest.h>
#include <lvgl.h>
#include <memory>
namespace tt::app::filebrowser {
namespace tt::app::files {
class View {
class View final {
std::shared_ptr<State> state;
lv_obj_t* dir_entry_list = nullptr;

View File

@ -8,7 +8,7 @@
namespace tt::app::fileselection {
class State {
class State final {
Mutex mutex = Mutex(Mutex::Type::Recursive);
std::vector<dirent> dir_entries;

View File

@ -3,14 +3,14 @@
#include "./State.h"
#include "./FileSelectionPrivate.h"
#include "Tactility/app/AppManifest.h"
#include <Tactility/app/AppManifest.h>
#include <lvgl.h>
#include <memory>
namespace tt::app::fileselection {
class View {
class View final {
std::shared_ptr<State> state;
lv_obj_t* dir_entry_list = nullptr;

View File

@ -1,12 +1,5 @@
#pragma once
#include <Tactility/hal/i2c/I2c.h>
#include <Tactility/Mutex.h>
#include <Tactility/TactilityCore.h>
#include <Tactility/Thread.h>
#include <Tactility/Timer.h>
#include <lvgl.h>
#include <memory>
namespace tt::app::i2cscanner {

View File

@ -1,6 +1,6 @@
#pragma once
#include "Tactility/i18n/TextResources.h"
#include <Tactility/i18n/TextResources.h>
// WARNING: This file is auto-generated. Do not edit manually.

View File

@ -1,6 +1,6 @@
#pragma once
#include "Tactility/i18n/TextResources.h"
#include <Tactility/i18n/TextResources.h>
// WARNING: This file is auto-generated. Do not edit manually.

View File

@ -1,13 +1,13 @@
#pragma once
#include "./View.h"
#include "View.h"
#include "Tactility/Preferences.h"
#include "Tactility/Tactility.h"
#include "Tactility/app/alertdialog/AlertDialog.h"
#include "Tactility/hal/uart/Uart.h"
#include "Tactility/lvgl/LvglSync.h"
#include "Tactility/lvgl/Style.h"
#include <Tactility/Preferences.h>
#include <Tactility/Tactility.h>
#include <Tactility/app/alertdialog/AlertDialog.h>
#include <Tactility/hal/uart/Uart.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/lvgl/Style.h>
#include <Tactility/StringUtils.h>
#include <string>
@ -127,7 +127,7 @@ public:
lv_obj_add_event_cb(connect_button, onConnectCallback, LV_EVENT_SHORT_CLICKED, this);
}
void onStop() {
void onStop() override {
int speed = getSpeedInput();
if (speed > 0) {
preferences.putInt32("speed", speed);

View File

@ -1,21 +1,18 @@
#pragma once
#include "./View.h"
#include "Tactility/Timer.h"
#include <Tactility/app/serialconsole/View.h>
#include <Tactility/Timer.h>
#include <cstring>
#include <sstream>
#define TAG "SerialConsole"
namespace tt::app::serialconsole {
constexpr auto* TAG = "SerialConsole";
constexpr size_t receiveBufferSize = 512;
constexpr size_t renderBufferSize = receiveBufferSize + 2; // Leave space for newline at split and null terminator at the end
class ConsoleView final : public View {
private:
lv_obj_t* _Nullable parent = nullptr;
lv_obj_t* _Nullable logTextarea = nullptr;
lv_obj_t* _Nullable inputTextarea = nullptr;
@ -215,7 +212,7 @@ public:
viewThread = std::make_unique<Thread>(
"SerConsView",
4096,
[this]() {
[this] {
return this->viewThreadMain();
}
);
@ -276,7 +273,7 @@ public:
startViews(parent);
}
void onStop() final {
void onStop() override {
stopViews();
stopLogic();
stopUart();

View File

@ -1,7 +1,5 @@
#pragma once
#include <lvgl.h>
namespace tt::app::serialconsole {
class View {

View File

@ -1,8 +1,6 @@
#pragma once
#include <Tactility/Bundle.h>
#include <Tactility/Mutex.h>
#include <Tactility/service/wifi/Wifi.h>
#include <string>
namespace tt::app::wifiapsettings {

View File

@ -5,7 +5,7 @@
namespace tt::app::wificonnect {
class State {
class State final {
Mutex lock;
service::wifi::settings::WifiApSettings apSettings;
bool connectionError = false;

View File

@ -3,7 +3,7 @@
#include "./Bindings.h"
#include "./State.h"
#include "Tactility/app/AppContext.h"
#include <Tactility/app/AppContext.h>
#include <lvgl.h>
@ -11,7 +11,7 @@ namespace tt::app::wificonnect {
class WifiConnect;
class View {
class View final {
Bindings* bindings;
State* state;

View File

@ -1,16 +1,16 @@
#pragma once
#include "Tactility/app/App.h"
#include "Tactility/app/wificonnect/Bindings.h"
#include "Tactility/app/wificonnect/State.h"
#include "Tactility/app/wificonnect/View.h"
#include <Tactility/app/App.h>
#include <Tactility/app/wificonnect/Bindings.h>
#include <Tactility/app/wificonnect/State.h>
#include <Tactility/app/wificonnect/View.h>
#include <Tactility/Mutex.h>
#include <Tactility/service/wifi/Wifi.h>
namespace tt::app::wificonnect {
class WifiConnect : public App {
class WifiConnect final : public App {
Mutex mutex;
State state;
@ -27,7 +27,7 @@ class WifiConnect : public App {
public:
WifiConnect();
~WifiConnect();
~WifiConnect() override;
void lock();
void unlock();
@ -39,7 +39,6 @@ public:
Bindings& getBindings() { return bindings; }
View& getView() { return view; }
void requestViewUpdate();
};

View File

@ -8,7 +8,7 @@ namespace tt::app::wifimanage {
/**
* View's state
*/
class State {
class State final {
Mutex mutex = Mutex(Mutex::Type::Recursive);
bool scanning = false;
@ -32,7 +32,7 @@ public:
template <std::invocable<const std::vector<service::wifi::ApRecord>&> Func>
void withApRecords(Func&& onApRecords) const {
mutex.withLock([&]() {
mutex.withLock([&] {
std::invoke(std::forward<Func>(onApRecords), apRecords);
});
}

View File

@ -3,19 +3,17 @@
#include "./Bindings.h"
#include "./State.h"
#include "Tactility/app/AppContext.h"
#include <Tactility/app/AppContext.h>
#include <lvgl.h>
namespace tt::app::wifimanage {
class View {
private:
class View final {
Bindings* bindings;
State* state;
std::unique_ptr<app::Paths> paths;
std::unique_ptr<Paths> paths;
lv_obj_t* root = nullptr;
lv_obj_t* enable_switch = nullptr;
lv_obj_t* enable_on_boot_switch = nullptr;

View File

@ -3,7 +3,7 @@
#include "./View.h"
#include "./State.h"
#include "Tactility/app/App.h"
#include <Tactility/app/App.h>
#include <Tactility/PubSub.h>
#include <Tactility/Mutex.h>
@ -11,7 +11,7 @@
namespace tt::app::wifimanage {
class WifiManage : public App {
class WifiManage final : public App {
PubSub<service::wifi::WifiEvent>::SubscriptionHandle wifiSubscription = nullptr;
Mutex mutex;

View File

@ -60,7 +60,7 @@ namespace app {
namespace chat { extern const AppManifest manifest; }
namespace development { extern const AppManifest manifest; }
namespace display { extern const AppManifest manifest; }
namespace filebrowser { extern const AppManifest manifest; }
namespace files { extern const AppManifest manifest; }
namespace fileselection { extern const AppManifest manifest; }
namespace gpio { extern const AppManifest manifest; }
namespace gpssettings { extern const AppManifest manifest; }
@ -102,7 +102,7 @@ static void registerSystemApps() {
addApp(app::applist::manifest);
addApp(app::calculator::manifest);
addApp(app::display::manifest);
addApp(app::filebrowser::manifest);
addApp(app::files::manifest);
addApp(app::fileselection::manifest);
addApp(app::gpio::manifest);
addApp(app::imageviewer::manifest);
@ -178,7 +178,7 @@ static void registerInstalledApp(std::string path) {
app::addApp({
.id = app_id_entry->second,
.name = app_name_entry->second,
.type = app::Type::User,
.category = app::Category::User,
.location = app::Location::external(path)
});
}

View File

@ -216,7 +216,7 @@ bool install(const std::string& path) {
addApp({
.id = app_id_iterator->second,
.name = app_name_entry->second,
.type = Type::User,
.category = Category::User,
.location = Location::external(renamed_target_path)
});

View File

@ -1,41 +1,22 @@
#ifdef ESP_PLATFORM
#include "Tactility/app/ElfApp.h"
#include "Tactility/file/File.h"
#include "Tactility/file/FileLock.h"
#include "Tactility/service/loader/Loader.h"
#include <Tactility/app/alertdialog/AlertDialog.h>
#include <Tactility/app/ElfApp.h>
#include <Tactility/file/File.h>
#include <Tactility/file/FileLock.h>
#include <Tactility/Log.h>
#include <Tactility/service/loader/Loader.h>
#include <Tactility/StringUtils.h>
#include "esp_elf.h"
#include <esp_elf.h>
#include <string>
#include <utility>
#include <Tactility/app/alertdialog/AlertDialog.h>
namespace tt::app {
constexpr auto* TAG = "ElfApp";
struct ElfManifest {
/** The user-readable name of the app. Used in UI. */
std::string name;
/** Optional icon. */
std::string icon;
CreateData _Nullable createData = nullptr;
DestroyData _Nullable destroyData = nullptr;
OnCreate _Nullable onCreate = nullptr;
OnDestroy _Nullable onDestroy = nullptr;
OnShow _Nullable onShow = nullptr;
OnHide _Nullable onHide = nullptr;
OnResult _Nullable onResult = nullptr;
};
static size_t elfManifestSetCount = 0;
static ElfManifest elfManifest;
static std::shared_ptr<Lock> elfManifestLock = std::make_shared<Mutex>();
static std::string getErrorCodeString(int error_code) {
switch (error_code) {
case ENOMEM:
@ -47,7 +28,30 @@ static std::string getErrorCodeString(int error_code) {
}
}
class ElfApp : public App {
class ElfApp final : public App {
public:
struct Parameters {
CreateData _Nullable createData = nullptr;
DestroyData _Nullable destroyData = nullptr;
OnCreate _Nullable onCreate = nullptr;
OnDestroy _Nullable onDestroy = nullptr;
OnShow _Nullable onShow = nullptr;
OnHide _Nullable onHide = nullptr;
OnResult _Nullable onResult = nullptr;
};
static void setParameters(const Parameters& parameters) {
staticParameters = parameters;
staticParametersSetCount++;
}
private:
static Parameters staticParameters;
static size_t staticParametersSetCount;
static std::shared_ptr<Lock> staticParametersLock;
const std::string appPath;
std::unique_ptr<uint8_t[]> elfFileData;
@ -60,7 +64,7 @@ class ElfApp : public App {
.entry = nullptr
};
bool shouldCleanupElf = false; // Whether we have to clean up the above "elf" object
std::unique_ptr<ElfManifest> manifest;
std::unique_ptr<Parameters> manifest;
void* data = nullptr;
std::string lastError = "";
@ -128,10 +132,10 @@ public:
void onCreate(AppContext& appContext) override {
// Because we use global variables, we have to ensure that we are not starting 2 apps in parallel
// We use a ScopedLock so we don't have to safeguard all branches
auto lock = elfManifestLock->asScopedLock();
auto lock = staticParametersLock->asScopedLock();
lock.lock();
elfManifestSetCount = 0;
staticParametersSetCount = 0;
if (!startElf()) {
service::loader::stopApp();
auto message = lastError.empty() ? "Application failed to start." : std::format("Application failed to start: {}", lastError);
@ -139,13 +143,13 @@ public:
return;
}
if (elfManifestSetCount == 0) {
if (staticParametersSetCount == 0) {
service::loader::stopApp();
alertdialog::start("Error", "Application failed to start: application failed to register itself");
return;
}
manifest = std::make_unique<ElfManifest>(elfManifest);
manifest = std::make_unique<Parameters>(staticParameters);
lock.unlock();
if (manifest->createData != nullptr) {
@ -192,9 +196,11 @@ public:
}
};
void setElfAppManifest(
const char* name,
const char* _Nullable icon,
ElfApp::Parameters ElfApp::staticParameters;
size_t ElfApp::staticParametersSetCount = 0;
std::shared_ptr<Lock> ElfApp::staticParametersLock = std::make_shared<Mutex>();
void setElfAppParameters(
CreateData _Nullable createData,
DestroyData _Nullable destroyData,
OnCreate _Nullable onCreate,
@ -203,9 +209,7 @@ void setElfAppManifest(
OnHide _Nullable onHide,
OnResult _Nullable onResult
) {
elfManifest = ElfManifest {
.name = name ? name : "",
.icon = icon ? icon : "",
ElfApp::setParameters({
.createData = createData,
.destroyData = destroyData,
.onCreate = onCreate,
@ -213,8 +217,7 @@ void setElfAppManifest(
.onShow = onShow,
.onHide = onHide,
.onResult = onResult
};
elfManifestSetCount++;
});
}
std::shared_ptr<App> createElfApp(const std::shared_ptr<AppManifest>& manifest) {

View File

@ -175,7 +175,8 @@ extern const AppManifest manifest = {
.id = "AddGps",
.name = "Add GPS",
.icon = LV_SYMBOL_GPS,
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<AddGpsApp>
};

View File

@ -128,7 +128,8 @@ public:
extern const AppManifest manifest = {
.id = "AlertDialog",
.name = "Alert Dialog",
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<AlertDialogApp>
};

View File

@ -40,7 +40,9 @@ public:
std::ranges::sort(manifests, SortAppManifestByName);
for (const auto& manifest: manifests) {
if (manifest->type == Type::User || manifest->type == Type::System) {
bool is_valid_category = (manifest->category == Category::User) || (manifest->category == Category::System);
bool is_visible = (manifest->flags & AppManifest::Flags::Hidden) == 0u;
if (is_valid_category && is_visible) {
createAppWidget(manifest, list);
}
}
@ -50,7 +52,8 @@ public:
extern const AppManifest manifest = {
.id = "AppList",
.name = "Apps",
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<AppListApp>,
};

View File

@ -124,7 +124,6 @@ class BootApp : public App {
settings::BootSettings boot_properties;
if (!settings::loadBootSettings(boot_properties) || boot_properties.launcherAppId.empty()) {
TT_LOG_E(TAG, "Launcher not configured");
stop();
return;
}
@ -164,7 +163,8 @@ public:
extern const AppManifest manifest = {
.id = "Boot",
.name = "Boot",
.type = Type::Boot,
.category = Category::System,
.flags = AppManifest::Flags::HideStatusBar | AppManifest::Flags::Hidden,
.createApp = create<BootApp>
};

View File

@ -118,7 +118,8 @@ public:
extern const AppManifest manifest = {
.id = "CrashDiagnostics",
.name = "Crash Diagnostics",
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<CrashDiagnosticsApp>
};

View File

@ -164,7 +164,7 @@ public:
extern const AppManifest manifest = {
.id = "Development",
.name = "Development",
.type = Type::Settings,
.category = Category::Settings,
.createApp = create<DevelopmentApp>
};

View File

@ -167,7 +167,7 @@ extern const AppManifest manifest = {
.id = "Display",
.name = "Display",
.icon = TT_ASSETS_APP_ICON_DISPLAY_SETTINGS,
.type = Type::Settings,
.category = Category::Settings,
.createApp = create<DisplayApp>
};

View File

@ -1,24 +1,24 @@
#include "Tactility/app/filebrowser/View.h"
#include "Tactility/app/filebrowser/State.h"
#include "Tactility/app/AppContext.h"
#include <Tactility/app/files/View.h>
#include <Tactility/app/files/State.h>
#include <Tactility/app/AppContext.h>
#include <Tactility/Assets.h>
#include <Tactility/service/loader/Loader.h>
#include <memory>
namespace tt::app::filebrowser {
constexpr auto* TAG = "FileBrowser";
namespace tt::app::files {
extern const AppManifest manifest;
class FileBrowser : public App {
class FilesApp final : public App {
std::unique_ptr<View> view;
std::shared_ptr<State> state;
public:
FileBrowser() {
FilesApp() {
state = std::make_shared<State>();
view = std::make_unique<View>(state);
}
@ -36,8 +36,9 @@ extern const AppManifest manifest = {
.id = "Files",
.name = "Files",
.icon = TT_ASSETS_APP_ICON_FILES,
.type = Type::Hidden,
.createApp = create<FileBrowser>
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<FilesApp>
};
void start() {

View File

@ -1,7 +1,8 @@
#include "Tactility/app/filebrowser/State.h"
#include <Tactility/app/files/State.h>
#include <Tactility/file/File.h>
#include "Tactility/hal/sdcard/SdCardDevice.h"
#include <Tactility/file/FileLock.h>
#include <Tactility/hal/sdcard/SdCardDevice.h>
#include <Tactility/Log.h>
#include <Tactility/MountPoints.h>
#include <Tactility/kernel/Kernel.h>
@ -10,11 +11,10 @@
#include <unistd.h>
#include <vector>
#include <dirent.h>
#include <Tactility/file/FileLock.h>
namespace tt::app::filebrowser {
namespace tt::app::files {
constexpr auto* TAG = "FileBrowser";
constexpr auto* TAG = "Files";
State::State() {
if (kernel::getPlatform() == kernel::PlatformSimulator) {

View File

@ -1,9 +1,9 @@
#include <Tactility/StringUtils.h>
#include <Tactility/TactilityCore.h>
namespace tt::app::filebrowser {
namespace tt::app::files {
constexpr auto* TAG = "FileBrowser";
constexpr auto* TAG = "Files";
bool isSupportedAppFile(const std::string& filename) {
return filename.ends_with(".app");

View File

@ -1,13 +1,13 @@
#include "Tactility/app/filebrowser/View.h"
#include "Tactility/app/filebrowser/SupportedFiles.h"
#include <Tactility/app/files/View.h>
#include <Tactility/app/files/SupportedFiles.h>
#include "Tactility/app/alertdialog/AlertDialog.h"
#include "Tactility/app/imageviewer/ImageViewer.h"
#include "Tactility/app/inputdialog/InputDialog.h"
#include "Tactility/app/notes/Notes.h"
#include "Tactility/app/ElfApp.h"
#include "Tactility/lvgl/Toolbar.h"
#include "Tactility/lvgl/LvglSync.h"
#include <Tactility/app/alertdialog/AlertDialog.h>
#include <Tactility/app/imageviewer/ImageViewer.h>
#include <Tactility/app/inputdialog/InputDialog.h>
#include <Tactility/app/notes/Notes.h>
#include <Tactility/app/ElfApp.h>
#include <Tactility/lvgl/Toolbar.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/Tactility.h>
#include <Tactility/file/File.h>
@ -19,17 +19,17 @@
#include <Tactility/file/FileLock.h>
#ifdef ESP_PLATFORM
#include "Tactility/service/loader/Loader.h"
#include <Tactility/service/loader/Loader.h>
#endif
namespace tt::app::filebrowser {
namespace tt::app::files {
constexpr auto* TAG = "FileBrowser";
constexpr auto* TAG = "Files";
// region Callbacks
static void dirEntryListScrollBeginCallback(lv_event_t* event) {
auto* view = static_cast<View*>(lv_event_get_user_data(event));
auto* view = static_cast<files::View*>(lv_event_get_user_data(event));
view->onDirEntryListScrollBegin();
}

View File

@ -59,7 +59,8 @@ extern const AppManifest manifest = {
.id = "FileSelection",
.name = "File Selection",
.icon = TT_ASSETS_APP_ICON_FILES,
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<FileSelection>
};

View File

@ -191,7 +191,7 @@ extern const AppManifest manifest = {
.id = "Gpio",
.name = "GPIO",
.icon = TT_ASSETS_APP_ICON_GPIO,
.type = Type::System,
.category = Category::System,
.createApp = create<GpioApp>
};

View File

@ -19,12 +19,12 @@ extern AppManifest manifest;
namespace tt::app::gpssettings {
constexpr const char* TAG = "GpsSettings";
extern const AppManifest manifest;
class GpsSettingsApp final : public App {
static constexpr auto* TAG = "GpsSettings";
std::unique_ptr<Timer> timer;
std::shared_ptr<GpsSettingsApp*> appReference = std::make_shared<GpsSettingsApp*>(this);
lv_obj_t* statusWrapper = nullptr;
@ -268,13 +268,13 @@ class GpsSettingsApp final : public App {
public:
GpsSettingsApp() {
timer = std::make_unique<Timer>(Timer::Type::Periodic, [this]() {
timer = std::make_unique<Timer>(Timer::Type::Periodic, [this] {
updateViews();
});
service = service::gps::findGpsService();
}
void onShow(AppContext& app, lv_obj_t* parent) final {
void onShow(AppContext& app, lv_obj_t* parent) override {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
@ -337,7 +337,7 @@ public:
updateViews();
}
void onHide(AppContext& app) final {
void onHide(AppContext& app) override {
service->getStatePubsub()->unsubscribe(serviceStateSubscription);
serviceStateSubscription = nullptr;
}
@ -347,7 +347,7 @@ extern const AppManifest manifest = {
.id = "GpsSettings",
.name = "GPS",
.icon = LV_SYMBOL_GPS,
.type = Type::Settings,
.category = Category::Settings,
.createApp = create<GpsSettingsApp>
};

View File

@ -1,13 +1,12 @@
#include "Tactility/app/i2cscanner/I2cScannerPrivate.h"
#include "Tactility/app/i2cscanner/I2cScannerThread.h"
#include "Tactility/app/i2cscanner/I2cHelpers.h"
#include <Tactility/app/i2cscanner/I2cScannerPrivate.h>
#include <Tactility/app/i2cscanner/I2cHelpers.h>
#include "Tactility/Preferences.h"
#include "Tactility/app/AppContext.h"
#include "Tactility/hal/i2c/I2cDevice.h"
#include "Tactility/lvgl/LvglSync.h"
#include "Tactility/lvgl/Toolbar.h"
#include "Tactility/service/loader/Loader.h"
#include <Tactility/Preferences.h>
#include <Tactility/app/AppContext.h>
#include <Tactility/hal/i2c/I2cDevice.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/lvgl/Toolbar.h>
#include <Tactility/service/loader/Loader.h>
#include <Tactility/Assets.h>
#include <Tactility/Tactility.h>
@ -15,14 +14,14 @@
#include <format>
#define START_SCAN_TEXT "Scan"
#define STOP_SCAN_TEXT "Stop scan"
namespace tt::app::i2cscanner {
extern const AppManifest manifest;
class I2cScannerApp : public App {
class I2cScannerApp final : public App {
static constexpr auto* START_SCAN_TEXT = "Scan";
static constexpr auto* STOP_SCAN_TEXT = "Stop scan";
// Core
Mutex mutex = Mutex(Mutex::Type::Recursive);
@ -181,13 +180,6 @@ void I2cScannerApp::onPressScanCallback(lv_event_t* event) {
}
}
void I2cScannerApp::onScanTimerCallback() {
auto app = optApp();
if (app != nullptr) {
app->onScanTimer();
}
}
// endregion Callbacks
bool I2cScannerApp::getPort(i2c_port_t* outPort) {
@ -285,8 +277,8 @@ void I2cScannerApp::startScanning() {
lv_obj_clean(scanListWidget);
scanState = ScanStateScanning;
scanTimer = std::make_unique<Timer>(Timer::Type::Once, []{
onScanTimerCallback();
scanTimer = std::make_unique<Timer>(Timer::Type::Once, [this]{
onScanTimer();
});
scanTimer->start(10);
mutex.unlock();
@ -414,7 +406,7 @@ extern const AppManifest manifest = {
.id = "I2cScanner",
.name = "I2C Scanner",
.icon = TT_ASSETS_APP_ICON_I2C_SETTINGS,
.type = Type::System,
.category = Category::System,
.createApp = create<I2cScannerApp>
};

View File

@ -96,7 +96,7 @@ extern const AppManifest manifest = {
.id = "I2cSettings",
.name = "I2C",
.icon = TT_ASSETS_APP_ICON_I2C_SETTINGS,
.type = Type::Settings,
.category = Category::Settings,
.createApp = create<I2cSettingsApp>
};

View File

@ -62,7 +62,8 @@ class ImageViewerApp : public App {
extern const AppManifest manifest = {
.id = "ImageViewer",
.name = "Image Viewer",
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<ImageViewerApp>
};

View File

@ -120,7 +120,8 @@ public:
extern const AppManifest manifest = {
.id = "InputDialog",
.name = "Input Dialog",
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<InputDialogApp>
};

View File

@ -138,7 +138,7 @@ public:
extern const AppManifest manifest = {
.id = "Launcher",
.name = "Launcher",
.type = Type::Launcher,
.category = Category::System,
.createApp = create<LauncherApp>
};

View File

@ -165,7 +165,7 @@ extern const AppManifest manifest = {
.id = "LocaleSettings",
.name = "Region & Language",
.icon = TT_ASSETS_APP_ICON_TIME_DATE_SETTINGS,
.type = Type::Settings,
.category = Category::Settings,
.createApp = create<LocaleSettingsApp>
};

View File

@ -123,7 +123,7 @@ extern const AppManifest manifest = {
.id = "Log",
.name = "Log",
.icon = LV_SYMBOL_LIST,
.type = Type::System,
.category = Category::System,
.createApp = create<LogApp>
};

View File

@ -192,7 +192,7 @@ extern const AppManifest manifest = {
.id = "Power",
.name = "Power",
.icon = TT_ASSETS_APP_ICON_POWER_SETTINGS,
.type = Type::Settings,
.category = Category::Settings,
.createApp = create<PowerApp>
};

View File

@ -281,7 +281,7 @@ extern const AppManifest manifest = {
.id = "Screenshot",
.name = "Screenshot",
.icon = LV_SYMBOL_IMAGE,
.type = Type::System,
.category = Category::System,
.createApp = create<ScreenshotApp>
};

View File

@ -114,7 +114,8 @@ public:
extern const AppManifest manifest = {
.id = "SelectionDialog",
.name = "Selection Dialog",
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<SelectionDialogApp>
};

View File

@ -1,4 +1,4 @@
#include "Tactility/app/serialconsole/ConnectView.h"
#include "../../../Private/Tactility/app/serialconsole/ConnectView.h"
#include "Tactility/app/serialconsole/ConsoleView.h"
#include "Tactility/lvgl/Style.h"
@ -88,7 +88,7 @@ extern const AppManifest manifest = {
.id = "SerialConsole",
.name = "Serial Console",
.icon = LV_SYMBOL_LIST,
.type = Type::System,
.category = Category::System,
.createApp = create<SerialConsoleApp>
};

View File

@ -38,7 +38,7 @@ class SettingsApp : public App {
auto manifests = getApps();
std::sort(manifests.begin(), manifests.end(), SortAppManifestByName);
for (const auto& manifest: manifests) {
if (manifest->type == Type::Settings) {
if (manifest->category == Category::Settings) {
createWidget(manifest, list);
}
}
@ -49,7 +49,8 @@ extern const AppManifest manifest = {
.id = "Settings",
.name = "Settings",
.icon = TT_ASSETS_APP_ICON_SETTINGS,
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<SettingsApp>
};

View File

@ -295,7 +295,7 @@ extern const AppManifest manifest = {
.id = "SystemInfo",
.name = "System Info",
.icon = TT_ASSETS_APP_ICON_SYSTEM_INFO,
.type = Type::System,
.category = Category::System,
.createApp = create<SystemInfoApp>
};

View File

@ -60,7 +60,7 @@ extern const AppManifest manifest = {
.id = "TimeDateSettings",
.name = "Time & Date",
.icon = TT_ASSETS_APP_ICON_TIME_DATE_SETTINGS,
.type = Type::Settings,
.category = Category::Settings,
.createApp = create<TimeDateSettingsApp>
};

View File

@ -1,9 +1,9 @@
#include "Tactility/app/AppContext.h"
#include "Tactility/app/AppManifest.h"
#include "Tactility/app/timezone/TimeZone.h"
#include "Tactility/lvgl/Toolbar.h"
#include "Tactility/lvgl/LvglSync.h"
#include "Tactility/service/loader/Loader.h"
#include <Tactility/app/AppContext.h>
#include <Tactility/app/AppManifest.h>
#include <Tactility/app/timezone/TimeZone.h>
#include <Tactility/lvgl/Toolbar.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/service/loader/Loader.h>
#include <Tactility/MountPoints.h>
#include <Tactility/StringUtils.h>
@ -15,9 +15,8 @@
namespace tt::app::timezone {
constexpr auto* TAG = "TimeZone";
#define RESULT_BUNDLE_CODE_INDEX "code"
#define RESULT_BUNDLE_NAME_INDEX "name"
constexpr auto* RESULT_BUNDLE_CODE_INDEX = "code";
constexpr auto* RESULT_BUNDLE_NAME_INDEX = "name";
extern const AppManifest manifest;
@ -113,14 +112,6 @@ class TimeZoneApp final : public App {
lv_obj_add_event_cb(btn, &onListItemSelectedCallback, LV_EVENT_SHORT_CLICKED, (void*)index);
}
static void updateTimerCallback() {
auto appContext = getCurrentAppContext();
if (appContext != nullptr && appContext->getManifest().id == manifest.id) {
auto app = std::static_pointer_cast<TimeZoneApp>(appContext->getApp());
app->updateList();
}
}
void readTimeZones(std::string filter) {
auto path = std::string(file::MOUNT_POINT_SYSTEM) + "/timezones.csv";
auto* file = fopen(path.c_str(), "rb");
@ -228,14 +219,17 @@ public:
}
void onCreate(AppContext& app) override {
updateTimer = std::make_unique<Timer>(Timer::Type::Once, [] { updateTimerCallback(); });
updateTimer = std::make_unique<Timer>(Timer::Type::Once, [this] {
updateList();
});
}
};
extern const AppManifest manifest = {
.id = "TimeZone",
.name = "Select timezone",
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<TimeZoneApp>
};

View File

@ -45,7 +45,7 @@ extern const AppManifest manifest = {
.id = "UsbSettings",
.name = "USB",
.icon = LV_SYMBOL_USB,
.type = Type::Settings,
.category = Category::Settings,
.createApp = create<UsbSettingsApp>
};

View File

@ -1,20 +1,18 @@
#include "Tactility/app/wifiapsettings/WifiApSettings.h"
#include "Tactility/app/App.h"
#include "Tactility/app/AppContext.h"
#include "Tactility/app/AppManifest.h"
#include "Tactility/app/alertdialog/AlertDialog.h"
#include "Tactility/lvgl/Style.h"
#include "Tactility/lvgl/Toolbar.h"
#include <Tactility/service/wifi/WifiApSettings.h>
#include <Tactility/service/wifi/Wifi.h>
#include <Tactility/app/App.h>
#include <Tactility/app/AppContext.h>
#include <Tactility/app/AppManifest.h>
#include <Tactility/app/alertdialog/AlertDialog.h>
#include <Tactility/lvgl/Style.h>
#include <Tactility/lvgl/Toolbar.h>
#include <Tactility/TactilityCore.h>
#include <Tactility/service/wifi/WifiSettings.h>
#include <lvgl.h>
namespace tt::app::wifiapsettings {
#define TAG "wifi_ap_settings"
constexpr auto* TAG = "WifiApSettings";
extern const AppManifest manifest;
@ -146,7 +144,8 @@ extern const AppManifest manifest = {
.id = "WifiApSettings",
.name = "Wi-Fi AP Settings",
.icon = LV_SYMBOL_WIFI,
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<WifiApSettings>
};

View File

@ -97,7 +97,8 @@ extern const AppManifest manifest = {
.id = "WifiConnect",
.name = "Wi-Fi Connect",
.icon = LV_SYMBOL_WIFI,
.type = Type::Hidden,
.category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<WifiConnect>
};

View File

@ -136,7 +136,7 @@ extern const AppManifest manifest = {
.id = "WifiManage",
.name = "Wi-Fi",
.icon = LV_SYMBOL_WIFI,
.type = Type::Settings,
.category = Category::Settings,
.createApp = create<WifiManage>
};

View File

@ -1,7 +1,6 @@
#include "Tactility/service/gui/GuiService.h"
#include "Tactility/lvgl/LvglSync.h"
#include "Tactility/lvgl/Statusbar.h"
#include "Tactility/lvgl/Style.h"
#include "Tactility/service/loader/Loader.h"
#include <Tactility/Tactility.h>
@ -92,10 +91,10 @@ void GuiService::redraw() {
lv_group_set_default(group);
app::Flags flags = std::static_pointer_cast<app::AppInstance>(appToRender)->getFlags();
if (flags.showStatusbar) {
lv_obj_remove_flag(statusbarWidget, LV_OBJ_FLAG_HIDDEN);
} else {
if (flags.hideStatusbar) {
lv_obj_add_flag(statusbarWidget, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_remove_flag(statusbarWidget, LV_OBJ_FLAG_HIDDEN);
}
lv_obj_t* container = createAppViews(appRootWidget);

View File

@ -103,7 +103,7 @@ void LoaderService::onStartAppMessage(const std::string& id, app::LaunchId launc
auto previous_app = !appStack.empty() ? appStack.top() : nullptr;
auto new_app = std::make_shared<app::AppInstance>(app_manifest, launchId, parameters);
new_app->mutableFlags().showStatusbar = (app_manifest->type != app::Type::Boot);
new_app->mutableFlags().hideStatusbar = (app_manifest->flags & app::AppManifest::Flags::HideStatusBar);
appStack.push(new_app);
transitionAppToState(new_app, app::State::Initial);
@ -141,7 +141,7 @@ void LoaderService::onStopAppMessage(const std::string& id) {
return;
}
if (original_stack_size == 1 && app_to_stop->getManifest().type != app::Type::Boot) {
if (original_stack_size == 1 && app_to_stop->getManifest().name != "Boot") {
TT_LOG_E(TAG, "Stop app: can't stop root app");
return;
}

View File

@ -1,9 +1,10 @@
#include "Tactility/lvgl/Statusbar.h"
#include "Tactility/lvgl/LvglSync.h"
#include <Tactility/lvgl/Statusbar.h>
#include <Tactility/lvgl/LvglSync.h>
#include "Tactility/hal/power/PowerDevice.h"
#include "Tactility/hal/sdcard/SdCardDevice.h"
#include "Tactility/service/gps/GpsService.h"
#include <Tactility/hal/power/PowerDevice.h>
#include <Tactility/hal/sdcard/SdCardDevice.h>
#include <Tactility/lvgl/Lvgl.h>
#include <Tactility/service/gps/GpsService.h>
#include <Tactility/Mutex.h>
#include <Tactility/Tactility.h>
#include <Tactility/Timer.h>
@ -16,31 +17,31 @@ namespace tt::service::statusbar {
constexpr auto* TAG = "StatusbarService";
// SD card status
#define STATUSBAR_ICON_SDCARD "sdcard.png"
#define STATUSBAR_ICON_SDCARD_ALERT "sdcard_alert.png"
constexpr auto* STATUSBAR_ICON_SDCARD = "sdcard.png";
constexpr auto* STATUSBAR_ICON_SDCARD_ALERT = "sdcard_alert.png";
// Wifi status
#define STATUSBAR_ICON_WIFI_OFF_WHITE "wifi_off_white.png"
#define STATUSBAR_ICON_WIFI_SCAN_WHITE "wifi_scan_white.png"
#define STATUSBAR_ICON_WIFI_SIGNAL_WEAK_WHITE "wifi_signal_weak_white.png"
#define STATUSBAR_ICON_WIFI_SIGNAL_MEDIUM_WHITE "wifi_signal_medium_white.png"
#define STATUSBAR_ICON_WIFI_SIGNAL_STRONG_WHITE "wifi_signal_strong_white.png"
constexpr auto* STATUSBAR_ICON_WIFI_OFF_WHITE = "wifi_off_white.png";
constexpr auto* STATUSBAR_ICON_WIFI_SCAN_WHITE = "wifi_scan_white.png";
constexpr auto* STATUSBAR_ICON_WIFI_SIGNAL_WEAK_WHITE = "wifi_signal_weak_white.png";
constexpr auto* STATUSBAR_ICON_WIFI_SIGNAL_MEDIUM_WHITE = "wifi_signal_medium_white.png";
constexpr auto* STATUSBAR_ICON_WIFI_SIGNAL_STRONG_WHITE = "wifi_signal_strong_white.png";
// Power status
#define STATUSBAR_ICON_POWER_0 "power_0.png"
#define STATUSBAR_ICON_POWER_10 "power_10.png"
#define STATUSBAR_ICON_POWER_20 "power_20.png"
#define STATUSBAR_ICON_POWER_30 "power_30.png"
#define STATUSBAR_ICON_POWER_40 "power_40.png"
#define STATUSBAR_ICON_POWER_50 "power_50.png"
#define STATUSBAR_ICON_POWER_60 "power_60.png"
#define STATUSBAR_ICON_POWER_70 "power_70.png"
#define STATUSBAR_ICON_POWER_80 "power_80.png"
#define STATUSBAR_ICON_POWER_90 "power_90.png"
#define STATUSBAR_ICON_POWER_100 "power_100.png"
constexpr auto* STATUSBAR_ICON_POWER_0 = "power_0.png";
constexpr auto* STATUSBAR_ICON_POWER_10 = "power_10.png";
constexpr auto* STATUSBAR_ICON_POWER_20 = "power_20.png";
constexpr auto* STATUSBAR_ICON_POWER_30 = "power_30.png";
constexpr auto* STATUSBAR_ICON_POWER_40 = "power_40.png";
constexpr auto* STATUSBAR_ICON_POWER_50 = "power_50.png";
constexpr auto* STATUSBAR_ICON_POWER_60 = "power_60.png";
constexpr auto* STATUSBAR_ICON_POWER_70 = "power_70.png";
constexpr auto* STATUSBAR_ICON_POWER_80 = "power_80.png";
constexpr auto* STATUSBAR_ICON_POWER_90 = "power_90.png";
constexpr auto* STATUSBAR_ICON_POWER_100 = "power_100.png";
// GPS
#define STATUSBAR_ICON_GPS "location.png"
constexpr auto* STATUSBAR_ICON_GPS = "location.png";
extern const ServiceManifest manifest;
@ -222,15 +223,15 @@ class StatusbarService final : public Service {
}
void update() {
// TODO: Make thread-safe for LVGL
if (lvgl::isStarted()) {
if (lvgl::lock(100)) {
updateGpsIcon();
updateWifiIcon();
updateSdCardIcon();
updatePowerStatusIcon();
lvgl::unlock();
}
}
static void onUpdate(const std::shared_ptr<StatusbarService>& service) {
service->update();
}
public:
@ -262,10 +263,10 @@ public:
auto service = findServiceById<StatusbarService>(manifest.id);
assert(service);
onUpdate(service);
service->update();
updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, [service] {
onUpdate(service);
service->update();
});
// We want to try and scan more often in case of startup or scan lock failure

View File

@ -28,10 +28,6 @@ typedef void (*AppOnHide)(AppHandle app, void* _Nullable data);
typedef void (*AppOnResult)(AppHandle app, void* _Nullable data, AppLaunchId launchId, AppResult result, BundleHandle resultData);
typedef struct {
/** The application's human-readable name */
const char* name;
/** The application icon (you can use LV_SYMBOL_* too) */
const char* _Nullable icon;
/** The application can allocate data to re-use later (e.g. struct with state) */
AppCreateData _Nullable createData;
/** If createData is specified, this one must be specified too */

View File

@ -3,18 +3,16 @@
#include <Tactility/Check.h>
#include <Tactility/app/ElfApp.h>
#define TAG "tt_app"
extern "C" {
constexpr auto TAG = "tt_app";
void tt_app_register(
const ExternalAppManifest* manifest
) {
#ifdef ESP_PLATFORM
assert((manifest->createData == nullptr) == (manifest->destroyData == nullptr));
setElfAppManifest(
manifest->name,
manifest->icon,
tt::app::setElfAppParameters(
manifest->createData,
manifest->destroyData,
manifest->onCreate,

View File

@ -90,6 +90,9 @@ const esp_elfsym elf_symbols[] {
ESP_ELFSYM_EXPORT(strstr),
ESP_ELFSYM_EXPORT(memset),
ESP_ELFSYM_EXPORT(memcpy),
ESP_ELFSYM_EXPORT(memcmp),
ESP_ELFSYM_EXPORT(memchr),
ESP_ELFSYM_EXPORT(memmove),
// ctype
ESP_ELFSYM_EXPORT(isalnum),
ESP_ELFSYM_EXPORT(isalpha),