App and Service improvements (#106)

This commit is contained in:
Ken Van Hoeylandt
2024-12-05 22:46:27 +01:00
committed by GitHub
parent e86a11b7e2
commit 50ee77d572
69 changed files with 692 additions and 596 deletions
-30
View File
@@ -1,30 +0,0 @@
#pragma once
#include "Manifest.h"
#include "Bundle.h"
namespace tt::app {
typedef union {
struct {
bool showStatusbar : 1;
};
unsigned char flags;
} Flags;
/**
* A limited representation of the application instance.
* Do not store references or pointers to these!
*/
class App {
public:
virtual ~App() {};
virtual const Manifest& getManifest() const = 0;
virtual _Nullable void* getData() const = 0;
virtual void setData(void* data) = 0;
virtual const Bundle& getParameters() const = 0;
virtual Flags getFlags() const = 0;
virtual void setResult(Result result, const Bundle& bundle = Bundle()) = 0;
};
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include "AppManifest.h"
#include "Bundle.h"
#include <memory>
namespace tt::app {
typedef union {
struct {
bool showStatusbar : 1;
};
unsigned char flags;
} Flags;
/**
* A limited representation of the application instance.
* Do not store references or pointers to these!
*/
class AppContext {
protected:
virtual ~AppContext() = default;
public:
[[nodiscard]] virtual const AppManifest& getManifest() const = 0;
[[nodiscard]] virtual std::shared_ptr<void> _Nullable getData() const = 0;
virtual void setData(std::shared_ptr<void> data) = 0;
[[nodiscard]] virtual std::shared_ptr<const Bundle> getParameters() const = 0;
[[nodiscard]] virtual Flags getFlags() const = 0;
virtual void setResult(Result result) = 0;
virtual void setResult(Result result, std::shared_ptr<const Bundle> bundle)= 0;
[[nodiscard]] virtual bool hasResult() const = 0;
};
}
+26 -7
View File
@@ -23,7 +23,7 @@ State AppInstance::getState() const {
* Consider creating MutableBundle vs Bundle.
* Consider not exposing bundle, but expose `app_get_bundle_int(key)` methods with locking in it.
*/
const Manifest& AppInstance::getManifest() const {
const AppManifest& AppInstance::getManifest() const {
return manifest;
}
@@ -40,26 +40,45 @@ void AppInstance::setFlags(Flags newFlags) {
mutex.release();
}
_Nullable void* AppInstance::getData() const {
std::shared_ptr<void> _Nullable AppInstance::getData() const {
mutex.acquire(TtWaitForever);
auto result = data;
mutex.release();
return result;
}
void AppInstance::setData(void* newData) {
void AppInstance::setData(std::shared_ptr<void> newData) {
mutex.acquire(TtWaitForever);
data = newData;
mutex.release();
}
const Bundle& AppInstance::getParameters() const {
return parameters;
std::shared_ptr<const Bundle> AppInstance::getParameters() const {
mutex.acquire(TtWaitForever);
std::shared_ptr<const Bundle> result = parameters;
mutex.release();
return result;
}
void AppInstance::setResult(Result result, const Bundle& bundle) {
std::unique_ptr<ResultHolder> new_holder(new ResultHolder(result, bundle));
void AppInstance::setResult(Result result) {
std::unique_ptr<ResultHolder> new_holder(new ResultHolder(result));
mutex.acquire(TtWaitForever);
resultHolder = std::move(new_holder);
mutex.release();
}
void AppInstance::setResult(Result result, std::shared_ptr<const Bundle> bundle) {
std::unique_ptr<ResultHolder> new_holder(new ResultHolder(result, std::move(bundle)));
mutex.acquire(TtWaitForever);
resultHolder = std::move(new_holder);
mutex.release();
}
bool AppInstance::hasResult() const {
mutex.acquire(TtWaitForever);
bool has_result = resultHolder != nullptr;
mutex.release();
return has_result;
}
} // namespace
@@ -9,7 +9,7 @@ typedef struct _lv_obj_t lv_obj_t;
namespace tt::app {
class App;
class AppContext;
typedef enum {
/** Boot screen, shown before desktop is launched. */
@@ -32,13 +32,13 @@ typedef enum {
ResultError
} Result;
typedef void (*AppOnStart)(App& app);
typedef void (*AppOnStop)(App& app);
typedef void (*AppOnShow)(App& app, lv_obj_t* parent);
typedef void (*AppOnHide)(App& app);
typedef void (*AppOnResult)(App& app, Result result, const Bundle& resultData);
typedef void (*AppOnStart)(AppContext& app);
typedef void (*AppOnStop)(AppContext& app);
typedef void (*AppOnShow)(AppContext& app, lv_obj_t* parent);
typedef void (*AppOnHide)(AppContext& app);
typedef void (*AppOnResult)(AppContext& app, Result result, const Bundle& resultData);
struct Manifest {
struct AppManifest {
/**
* The identifier by which the app is launched by the system and other apps.
*/
@@ -86,7 +86,7 @@ struct Manifest {
};
struct {
bool operator()(const Manifest* left, const Manifest* right) const { return left->name < right->name; }
bool operator()(const AppManifest* left, const AppManifest* right) const { return left->name < right->name; }
} SortAppManifestByName;
} // namespace
+6 -6
View File
@@ -7,12 +7,12 @@
namespace tt::app {
typedef std::unordered_map<std::string, const Manifest*> AppManifestMap;
typedef std::unordered_map<std::string, const AppManifest*> AppManifestMap;
static AppManifestMap app_manifest_map;
static Mutex hash_mutex(MutexTypeNormal);
void addApp(const Manifest* manifest) {
void addApp(const AppManifest* manifest) {
TT_LOG_I(TAG, "Registering manifest %s", manifest->id.c_str());
hash_mutex.acquire(TtWaitForever);
@@ -20,16 +20,16 @@ void addApp(const Manifest* manifest) {
hash_mutex.release();
}
_Nullable const Manifest * findAppById(const std::string& id) {
_Nullable const AppManifest * findAppById(const std::string& id) {
hash_mutex.acquire(TtWaitForever);
auto iterator = app_manifest_map.find(id);
_Nullable const Manifest* result = iterator != app_manifest_map.end() ? iterator->second : nullptr;
_Nullable const AppManifest* result = iterator != app_manifest_map.end() ? iterator->second : nullptr;
hash_mutex.release();
return result;
}
std::vector<const Manifest*> getApps() {
std::vector<const Manifest*> manifests;
std::vector<const AppManifest*> getApps() {
std::vector<const AppManifest*> manifests;
hash_mutex.acquire(TtWaitForever);
for (const auto& item: app_manifest_map) {
manifests.push_back(item.second);
+4 -4
View File
@@ -1,13 +1,13 @@
#pragma once
#include "Manifest.h"
#include "AppManifest.h"
#include <string>
#include <vector>
namespace tt::app {
void addApp(const Manifest* manifest);
const Manifest _Nullable* findAppById(const std::string& id);
std::vector<const Manifest*> getApps();
void addApp(const AppManifest* manifest);
const AppManifest _Nullable* findAppById(const std::string& id);
std::vector<const AppManifest*> getApps();
} // namespace
+8 -10
View File
@@ -2,7 +2,7 @@
#include <Thread.h>
#include <Kernel.h>
#include "Assets.h"
#include "app/App.h"
#include "app/AppContext.h"
#include "lvgl.h"
#include "hal/Display.h"
#include "service/loader/Loader.h"
@@ -48,8 +48,8 @@ static int32_t threadCallback(TT_UNUSED void* context) {
return 0;
}
static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
Data* data = (Data*)app.getData();
static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) {
auto data = std::static_pointer_cast<Data>(app.getData());
lv_obj_t* image = lv_image_create(parent);
lv_obj_set_size(image, LV_PCT(100), LV_PCT(100));
@@ -59,19 +59,17 @@ static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
data->thread.start();
}
static void onStart(App& app) {
Data* data = new Data();
static void onStart(AppContext& app) {
auto data = std::shared_ptr<Data>(new Data());
app.setData(data);
}
static void onStop(App& app) {
Data* data = (Data*)app.getData();
static void onStop(AppContext& app) {
auto data = std::static_pointer_cast<Data>(app.getData());
data->thread.join();
tt_assert(data);
delete data;
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "Boot",
.name = "Boot",
.type = TypeBoot,
+5 -5
View File
@@ -10,12 +10,12 @@ namespace tt::app::desktop {
static void onAppPressed(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
const auto* manifest = static_cast<const Manifest*>(lv_event_get_user_data(e));
service::loader::startApp(manifest->id, false, Bundle());
const auto* manifest = static_cast<const AppManifest*>(lv_event_get_user_data(e));
service::loader::startApp(manifest->id, false);
}
}
static void createAppWidget(const Manifest* manifest, void* parent) {
static void createAppWidget(const AppManifest* manifest, void* parent) {
tt_check(parent);
auto* list = static_cast<lv_obj_t*>(parent);
const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK;
@@ -23,7 +23,7 @@ static void createAppWidget(const Manifest* manifest, void* parent) {
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest);
}
static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) {
lv_obj_t* list = lv_list_create(parent);
lv_obj_set_size(list, LV_PCT(100), LV_PCT(100));
lv_obj_center(list);
@@ -46,7 +46,7 @@ static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
}
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "Desktop",
.name = "Desktop",
.type = TypeDesktop,
+4 -4
View File
@@ -1,4 +1,4 @@
#include "app/App.h"
#include "app/AppContext.h"
#include "Assets.h"
#include "DisplaySettings.h"
#include "Tactility.h"
@@ -70,7 +70,7 @@ static void onOrientationSet(lv_event_t* event) {
}
}
static void onShow(App& app, lv_obj_t* parent) {
static void onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app);
@@ -121,13 +121,13 @@ static void onShow(App& app, lv_obj_t* parent) {
lv_dropdown_set_selected(orientation_dropdown, orientation_selected);
}
static void onHide(TT_UNUSED App& app) {
static void onHide(TT_UNUSED AppContext& app) {
if (backlight_duty_set) {
setBacklightDuty(backlight_duty);
}
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "Display",
.name = "Display",
.icon = TT_ASSETS_APP_ICON_DISPLAY_SETTINGS,
+40 -28
View File
@@ -1,6 +1,6 @@
#include "FilesData.h"
#include "Tactility.h"
#include "app/App.h"
#include "app/AppContext.h"
#include "Assets.h"
#include "Check.h"
#include "FileUtils.h"
@@ -17,6 +17,18 @@ namespace tt::app::files {
#define TAG "files_app"
extern const AppManifest manifest;
/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */
std::shared_ptr<Data> _Nullable optData() {
app::AppContext* app = service::loader::getCurrentApp();
if (app->getManifest().id == manifest.id) {
return std::static_pointer_cast<Data>(app->getData());
} else {
return nullptr;
}
}
/**
* Case-insensitive check to see if the given file matches the provided file extension.
* @param path the full path to the file
@@ -57,10 +69,14 @@ static bool isSupportedTextFile(const char* filename) {
// region Views
static void updateViews(Data* data);
static void updateViews(std::shared_ptr<Data> data);
static void onNavigateUpPressed(TT_UNUSED lv_event_t* event) {
auto files_data = optData();
if (files_data == nullptr) {
return;
}
static void onNavigateUpPressed(lv_event_t* event) {
auto* files_data = (Data*)lv_event_get_user_data(event);
if (strcmp(files_data->current_path, "/") != 0) {
TT_LOG_I(TAG, "Navigating upwards");
char new_absolute_path[MAX_PATH_LENGTH];
@@ -68,6 +84,7 @@ static void onNavigateUpPressed(lv_event_t* event) {
data_set_entries_for_path(files_data, new_absolute_path);
}
}
updateViews(files_data);
}
@@ -103,16 +120,16 @@ static void viewFile(const char* path, const char* filename) {
TT_LOG_I(TAG, "Clicked %s", filepath);
if (isSupportedImageFile(filename)) {
Bundle bundle;
bundle.putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath);
auto bundle = std::shared_ptr<Bundle>(new Bundle());
bundle->putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath);
service::loader::startApp("ImageViewer", false, bundle);
} else if (isSupportedTextFile(filename)) {
Bundle bundle;
auto bundle = std::shared_ptr<Bundle>(new Bundle());
if (get_platform() == PlatformEsp) {
bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath);
bundle->putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath);
} else {
// Remove forward slash, because we need a relative path
bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath + 1);
bundle->putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath + 1);
}
service::loader::startApp("TextViewer", false, bundle);
} else {
@@ -123,11 +140,13 @@ static void viewFile(const char* path, const char* filename) {
}
static void onFilePressed(lv_event_t* event) {
auto files_data = optData();
if (files_data == nullptr) {
return;
}
lv_event_code_t code = lv_event_get_code(event);
if (code == LV_EVENT_CLICKED) {
lv_obj_t* button = lv_event_get_current_target_obj(event);
auto* files_data = static_cast<Data*>(lv_obj_get_user_data(button));
auto* dir_entry = static_cast<dirent*>(lv_event_get_user_data(event));
TT_LOG_I(TAG, "Pressed %s %d", dir_entry->d_name, dir_entry->d_type);
@@ -152,7 +171,7 @@ static void onFilePressed(lv_event_t* event) {
}
}
static void createFileWidget(Data* files_data, lv_obj_t* parent, struct dirent* dir_entry) {
static void createFileWidget(lv_obj_t* parent, struct dirent* dir_entry) {
tt_check(parent);
auto* list = (lv_obj_t*)parent;
const char* symbol;
@@ -166,15 +185,14 @@ static void createFileWidget(Data* files_data, lv_obj_t* parent, struct dirent*
symbol = LV_SYMBOL_FILE;
}
lv_obj_t* button = lv_list_add_button(list, symbol, dir_entry->d_name);
lv_obj_set_user_data(button, files_data);
lv_obj_add_event_cb(button, &onFilePressed, LV_EVENT_CLICKED, (void*)dir_entry);
}
static void updateViews(Data* data) {
static void updateViews(std::shared_ptr<Data> data) {
lv_obj_clean(data->list);
for (int i = 0; i < data->dir_entries_count; ++i) {
TT_LOG_I(TAG, "Entry: %s %d", data->dir_entries[i]->d_name, data->dir_entries[i]->d_type);
createFileWidget(data, data->list, data->dir_entries[i]);
createFileWidget(data->list, data->dir_entries[i]);
}
}
@@ -182,14 +200,14 @@ static void updateViews(Data* data) {
// region Lifecycle
static void onShow(App& app, lv_obj_t* parent) {
auto* data = static_cast<Data*>(app.getData());
static void onShow(AppContext& app, lv_obj_t* parent) {
auto data = std::static_pointer_cast<Data>(app.getData());
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_t* toolbar = lvgl::toolbar_create(parent, "Files");
lvgl::toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, &onExitAppPressed, nullptr);
lvgl::toolbar_add_action(toolbar, LV_SYMBOL_UP, &onNavigateUpPressed, data);
lvgl::toolbar_add_action(toolbar, LV_SYMBOL_UP, &onNavigateUpPressed, nullptr);
data->list = lv_list_create(parent);
lv_obj_set_width(data->list, LV_PCT(100));
@@ -198,8 +216,8 @@ static void onShow(App& app, lv_obj_t* parent) {
updateViews(data);
}
static void onStart(App& app) {
auto* data = data_alloc();
static void onStart(AppContext& app) {
auto data = std::shared_ptr<Data>(new Data());
// PC platform is bound to current work directory because of the LVGL file system mapping
if (get_platform() == PlatformSimulator) {
char cwd[PATH_MAX];
@@ -216,20 +234,14 @@ static void onStart(App& app) {
app.setData(data);
}
static void onStop(App& app) {
auto* data = static_cast<Data*>(app.getData());
data_free(data);
}
// endregion Lifecycle
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "Files",
.name = "Files",
.icon = TT_ASSETS_APP_ICON_FILES,
.type = TypeSystem,
.onStart = onStart,
.onStop = onStop,
.onShow = onShow,
};
+4 -28
View File
@@ -26,40 +26,16 @@ static bool get_child_path(char* base_path, const char* child_path, char* out_pa
}
}
Data* data_alloc() {
auto* data = static_cast<Data*>(malloc(sizeof(Data)));
*data = (Data) {
.current_path = { 0x00 },
.dir_entries = nullptr,
.dir_entries_count = 0
};
return data;
}
void data_free(Data* data) {
data_free_entries(data);
free(data);
}
void data_free_entries(Data* data) {
for (int i = 0; i < data->dir_entries_count; ++i) {
free(data->dir_entries[i]);
}
free(data->dir_entries);
data->dir_entries = nullptr;
data->dir_entries_count = 0;
}
static void data_set_entries(Data* data, struct dirent** entries, int count) {
static void data_set_entries(std::shared_ptr<Data> data, struct dirent** entries, int count) {
if (data->dir_entries != nullptr) {
data_free_entries(data);
data->freeEntries();
}
data->dir_entries = entries;
data->dir_entries_count = count;
}
bool data_set_entries_for_path(Data* data, const char* path) {
bool data_set_entries_for_path(std::shared_ptr<Data> data, const char* path) {
TT_LOG_I(TAG, "Changing path: %s -> %s", data->current_path, path);
/**
@@ -100,7 +76,7 @@ bool data_set_entries_for_path(Data* data, const char* path) {
}
}
bool data_set_entries_for_child_path(Data* data, const char* child_path) {
bool data_set_entries_for_child_path(std::shared_ptr<Data> data, const char* child_path) {
char new_absolute_path[MAX_PATH_LENGTH + 1];
if (get_child_path(data->current_path, child_path, new_absolute_path, MAX_PATH_LENGTH)) {
TT_LOG_I(TAG, "Navigating from %s to %s", data->current_path, new_absolute_path);
+25 -12
View File
@@ -1,23 +1,36 @@
#pragma once
#include <dirent.h>
#include "lvgl.h"
#include <dirent.h>
#include <memory>
namespace tt::app::files {
#define MAX_PATH_LENGTH 256
typedef struct {
char current_path[MAX_PATH_LENGTH];
struct dirent** dir_entries;
int dir_entries_count;
lv_obj_t* list;
} Data;
struct Data {
char current_path[MAX_PATH_LENGTH] = { 0 };
struct dirent** dir_entries = nullptr;
int dir_entries_count = 0;
lv_obj_t* list = nullptr;
Data* data_alloc();
void data_free(Data* data);
void data_free_entries(Data* data);
bool data_set_entries_for_child_path(Data* data, const char* child_path);
bool data_set_entries_for_path(Data* data, const char* path);
void freeEntries() {
for (int i = 0; i < dir_entries_count; ++i) {
free(dir_entries[i]);
}
free(dir_entries);
dir_entries = nullptr;
dir_entries_count = 0;
}
~Data() {
freeEntries();
}
};
void data_free(std::shared_ptr<Data> data);
void data_free_entries(std::shared_ptr<Data> data);
bool data_set_entries_for_child_path(std::shared_ptr<Data> data, const char* child_path);
bool data_set_entries_for_path(std::shared_ptr<Data> data, const char* path);
} // namespace
+13 -19
View File
@@ -29,8 +29,8 @@ public:
tt_check(mutex.release() == TtStatusOk);
}
void onShow(App& app, lv_obj_t* parent);
void onHide(App& app);
void onShow(AppContext& app, lv_obj_t* parent);
void onHide(AppContext& app);
void startTask();
void stopTask();
@@ -135,8 +135,8 @@ void Gpio::stopTask() {
// endregion Task
void Gpio::onShow(App& app, lv_obj_t* parent) {
auto* gpio = (Gpio*)app.getData();
void Gpio::onShow(AppContext& app, lv_obj_t* parent) {
auto gpio = std::static_pointer_cast<Gpio>(app.getData());
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_t* toolbar = lvgl::toolbar_create(parent, app);
@@ -191,42 +191,36 @@ void Gpio::onShow(App& app, lv_obj_t* parent) {
gpio->startTask();
}
void Gpio::onHide(App& app) {
auto* gpio = (Gpio*)app.getData();
void Gpio::onHide(AppContext& app) {
auto gpio = std::static_pointer_cast<Gpio>(app.getData());
gpio->stopTask();
}
// region App lifecycle
static void onShow(App& app, lv_obj_t* parent) {
auto* gpio = (Gpio*)app.getData();
static void onShow(AppContext& app, lv_obj_t* parent) {
auto gpio = std::static_pointer_cast<Gpio>(app.getData());
gpio->onShow(app, parent);
}
static void onHide(App& app) {
auto* gpio = (Gpio*)app.getData();
static void onHide(AppContext& app) {
auto gpio = std::static_pointer_cast<Gpio>(app.getData());
gpio->onHide(app);
}
static void onStart(App& app) {
auto* gpio = new Gpio();
static void onStart(AppContext& app) {
auto gpio = std::shared_ptr<Gpio>(new Gpio());
app.setData(gpio);
}
static void onStop(App& app) {
auto* gpio = (Gpio*)app.getData();
delete gpio;
}
// endregion App lifecycle
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "Gpio",
.name = "GPIO",
.type = TypeSystem,
.onStart = onStart,
.onStop = onStop,
.onShow = onShow,
.onHide = onHide
};
@@ -14,7 +14,6 @@ std::string getAddressText(uint8_t address) {
return stream.str();
}
std::string getPortNamesForDropdown() {
std::vector<std::string> config_names;
size_t port_index = 0;
+39 -27
View File
@@ -4,21 +4,37 @@
#include "Assets.h"
#include "Tactility.h"
#include "app/App.h"
#include "app/AppContext.h"
#include "lvgl/LvglSync.h"
#include "lvgl/Toolbar.h"
#include "service/loader/Loader.h"
#define START_SCAN_TEXT "Scan"
#define STOP_SCAN_TEXT "Stop scan"
namespace tt::app::i2cscanner {
static void updateViews(Data* data);
static void updateViews(std::shared_ptr<Data> data);
extern const AppManifest manifest;
/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */
std::shared_ptr<Data> _Nullable optData() {
app::AppContext* app = service::loader::getCurrentApp();
if (app->getManifest().id == manifest.id) {
return std::static_pointer_cast<Data>(app->getData());
} else {
return nullptr;
}
}
static void onSelectBus(lv_event_t* event) {
auto data = optData();
if (data == nullptr) {
return;
}
auto* dropdown = static_cast<lv_obj_t*>(lv_event_get_target(event));
uint32_t selected = lv_dropdown_get_selected(dropdown);
Data* data = (Data*) lv_event_get_user_data(event);
auto i2c_devices = tt::getConfiguration()->hardware->i2c;
assert(selected < i2c_devices.size());
@@ -35,16 +51,18 @@ static void onSelectBus(lv_event_t* event) {
}
static void onPressScan(lv_event_t* event) {
auto* data = (Data*)lv_event_get_user_data(event);
if (data->scanState == ScanStateScanning) {
stopScanning(data);
} else {
startScanning(data);
auto data = optData();
if (data != nullptr) {
if (data->scanState == ScanStateScanning) {
stopScanning(data);
} else {
startScanning(data);
}
updateViews(data);
}
updateViews(data);
}
static void updateViews(Data* data) {
static void updateViews(std::shared_ptr<Data> data) {
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
if (data->scanState == ScanStateScanning) {
lv_label_set_text(data->scanButtonLabelWidget, STOP_SCAN_TEXT);
@@ -75,7 +93,7 @@ static void updateViews(Data* data) {
}
}
static void updateViewsSafely(Data* data) {
static void updateViewsSafely(std::shared_ptr<Data> data) {
if (lvgl::lock(100 / portTICK_PERIOD_MS)) {
updateViews(data);
lvgl::unlock();
@@ -84,7 +102,7 @@ static void updateViewsSafely(Data* data) {
}
}
void onThreadFinished(Data* data) {
void onThreadFinished(std::shared_ptr<Data> data) {
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
if (data->scanState == ScanStateScanning) {
data->scanState = ScanStateStopped;
@@ -96,8 +114,8 @@ void onThreadFinished(Data* data) {
}
}
static void onShow(App& app, lv_obj_t* parent) {
auto* data = (Data*)app.getData();
static void onShow(AppContext& app, lv_obj_t* parent) {
auto data = std::static_pointer_cast<Data>(app.getData());
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app);
@@ -116,7 +134,7 @@ static void onShow(App& app, lv_obj_t* parent) {
lv_obj_t* scan_button = lv_button_create(wrapper);
lv_obj_set_width(scan_button, LV_PCT(48));
lv_obj_align(scan_button, LV_ALIGN_TOP_LEFT, 0, 1); // Shift 1 pixel to align with selection box
lv_obj_add_event_cb(scan_button, &onPressScan, LV_EVENT_CLICKED, data);
lv_obj_add_event_cb(scan_button, &onPressScan, LV_EVENT_CLICKED, nullptr);
lv_obj_t* scan_button_label = lv_label_create(scan_button);
lv_obj_align(scan_button_label, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(scan_button_label, START_SCAN_TEXT);
@@ -127,7 +145,7 @@ static void onShow(App& app, lv_obj_t* parent) {
lv_dropdown_set_options(port_dropdown, dropdown_items.c_str());
lv_obj_set_width(port_dropdown, LV_PCT(48));
lv_obj_align(port_dropdown, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_add_event_cb(port_dropdown, onSelectBus, LV_EVENT_VALUE_CHANGED, data);
lv_obj_add_event_cb(port_dropdown, onSelectBus, LV_EVENT_VALUE_CHANGED, nullptr);
lv_dropdown_set_selected(port_dropdown, 0);
data->portDropdownWidget = port_dropdown;
@@ -139,30 +157,24 @@ static void onShow(App& app, lv_obj_t* parent) {
data->scanListWidget = scan_list;
}
static void onHide(App& app) {
auto* data = (Data*)app.getData();
static void onHide(AppContext& app) {
auto data = std::static_pointer_cast<Data>(app.getData());
if (hasScanThread(data)) {
stopScanning(data);
}
}
static void onStart(App& app) {
Data* data = new Data();
static void onStart(AppContext& app) {
auto data = std::shared_ptr<Data>(new Data());
app.setData(data);
}
static void onStop(App& app) {
Data* data = (Data*)app.getData();
delete data;
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "I2cScanner",
.name = "I2C Scanner",
.icon = TT_ASSETS_APP_ICON_I2C_SETTINGS,
.type = TypeSystem,
.onStart = onStart,
.onStop = onStop,
.onShow = onShow,
.onHide = onHide
};
+2 -1
View File
@@ -5,6 +5,7 @@
#include <Thread.h>
#include "lvgl.h"
#include "hal/i2c/I2c.h"
#include <memory>
namespace tt::app::i2cscanner {
@@ -30,6 +31,6 @@ struct Data {
lv_obj_t* scanListWidget = nullptr;
};
void onThreadFinished(Data* data);
void onThreadFinished(std::shared_ptr<Data> data);
}
@@ -1,11 +1,12 @@
#include "I2cScannerThread.h"
#include "lvgl.h"
#include "service/gui/Gui.h"
#include <iomanip>
#include "service/loader/Loader.h"
namespace tt::app::i2cscanner {
static bool shouldStopScanThread(Data* data) {
std::shared_ptr<Data> _Nullable optData();
static bool shouldStopScanThread(std::shared_ptr<Data> data) {
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
bool is_scanning = data->scanState == ScanStateScanning;
tt_check(data->mutex.release() == TtStatusOk);
@@ -15,7 +16,7 @@ static bool shouldStopScanThread(Data* data) {
}
}
static bool getPort(Data* data, i2c_port_t* port) {
static bool getPort(std::shared_ptr<Data> data, i2c_port_t* port) {
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
*port = data->port;
tt_assert(data->mutex.release() == TtStatusOk);
@@ -26,7 +27,7 @@ static bool getPort(Data* data, i2c_port_t* port) {
}
}
static bool addAddressToList(Data* data, uint8_t address) {
static bool addAddressToList(std::shared_ptr<Data> data, uint8_t address) {
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
data->scannedAddresses.push_back(address);
tt_assert(data->mutex.release() == TtStatusOk);
@@ -37,8 +38,12 @@ static bool addAddressToList(Data* data, uint8_t address) {
}
}
static int32_t scanThread(void* context) {
Data* data = (Data*) context;
static int32_t scanThread(TT_UNUSED void* context) {
auto data = optData();
if (data == nullptr) {
return -1;
}
TT_LOG_I(TAG, "Scan thread started");
for (uint8_t address = 0; address < 128; ++address) {
@@ -69,7 +74,7 @@ static int32_t scanThread(void* context) {
return 0;
}
bool hasScanThread(Data* data) {
bool hasScanThread(std::shared_ptr<Data> data) {
bool has_thread;
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
has_thread = data->scanThread != nullptr;
@@ -82,7 +87,7 @@ bool hasScanThread(Data* data) {
}
}
void startScanning(Data* data) {
void startScanning(std::shared_ptr<Data> data) {
if (hasScanThread(data)) {
stopScanning(data);
}
@@ -99,7 +104,7 @@ void startScanning(Data* data) {
"i2c scanner",
4096,
scanThread,
data
nullptr
);
data->scanThread->start();
tt_check(data->mutex.release() == TtStatusOk);
@@ -108,7 +113,7 @@ void startScanning(Data* data) {
}
}
void stopScanning(Data* data) {
void stopScanning(std::shared_ptr<Data> data) {
bool sent_halt;
if (data->mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) {
tt_assert(data->scanThread != nullptr);
@@ -1,11 +1,12 @@
#pragma once
#include "I2cScanner.h"
#include <memory>
namespace tt::app::i2cscanner {
bool hasScanThread(Data* data);
void startScanning(Data* data);
void stopScanning(Data* data);
bool hasScanThread(std::shared_ptr<Data> data);
void startScanning(std::shared_ptr<Data> data);
void stopScanning(std::shared_ptr<Data> data);
}
@@ -69,7 +69,7 @@ static void show(lv_obj_t* parent, const hal::i2c::Configuration& configuration)
}
}
static void onShow(App& app, lv_obj_t* parent) {
static void onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app);
@@ -85,7 +85,7 @@ static void onShow(App& app, lv_obj_t* parent) {
}
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "I2cSettings",
.name = "I2C",
.icon = TT_ASSETS_APP_ICON_I2C_SETTINGS,
@@ -1,5 +1,5 @@
#include <TactilityCore.h>
#include "ImageViewer.h"
#include "Log.h"
#include "lvgl.h"
#include "lvgl/Style.h"
#include "lvgl/Toolbar.h"
@@ -8,7 +8,7 @@ namespace tt::app::imageviewer {
#define TAG "image_viewer"
static void onShow(App& app, lv_obj_t* parent) {
static void onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app);
@@ -22,16 +22,17 @@ static void onShow(App& app, lv_obj_t* parent) {
lv_obj_t* image = lv_img_create(wrapper);
lv_obj_align(image, LV_ALIGN_CENTER, 0, 0);
const Bundle& bundle = app.getParameters();
std::shared_ptr<const Bundle> bundle = app.getParameters();
tt_check(bundle != nullptr, "Parameters not set");
std::string file_argument;
if (bundle.optString(IMAGE_VIEWER_FILE_ARGUMENT, file_argument)) {
if (bundle->optString(IMAGE_VIEWER_FILE_ARGUMENT, file_argument)) {
std::string prefixed_path = "A:" + file_argument;
TT_LOG_I(TAG, "Opening %s", prefixed_path.c_str());
lv_img_set_src(image, prefixed_path.c_str());
}
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "ImageViewer",
.name = "Image Viewer",
.type = TypeHidden,
+42 -27
View File
@@ -1,4 +1,4 @@
#include "app/App.h"
#include "app/AppContext.h"
#include "Assets.h"
#include "lvgl.h"
#include "Tactility.h"
@@ -6,22 +6,35 @@
#include "lvgl/LvglSync.h"
#include "lvgl/Style.h"
#include "lvgl/Toolbar.h"
#include "service/loader/Loader.h"
namespace tt::app::power {
#define TAG "power"
typedef struct {
Timer* update_timer;
extern const AppManifest manifest;
static void on_timer(TT_UNUSED void* context);
struct Data {
std::unique_ptr<Timer> update_timer = std::unique_ptr<Timer>(new Timer(Timer::TypePeriodic, &on_timer, nullptr));
const hal::Power* power;
lv_obj_t* enable_switch;
lv_obj_t* charge_state;
lv_obj_t* charge_level;
lv_obj_t* current;
} Data;
};
static void updateUi(void* callbackContext) {
auto* data = (Data*)callbackContext;
/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */
std::shared_ptr<Data> _Nullable optData() {
app::AppContext* app = service::loader::getCurrentApp();
if (app->getManifest().id == manifest.id) {
return std::static_pointer_cast<Data>(app->getData());
} else {
return nullptr;
}
}
static void updateUi(std::shared_ptr<Data> data) {
bool charging_enabled = data->power->isChargingEnabled();
const char* charge_state = data->power->isCharging() ? "yes" : "no";
uint8_t charge_level = data->power->getChargeLevel();
@@ -40,20 +53,30 @@ static void updateUi(void* callbackContext) {
lvgl::unlock();
}
static void on_timer(TT_UNUSED void* context) {
auto data = optData();
if (data != nullptr) {
updateUi(data);
}
}
static void onPowerEnabledChanged(lv_event_t* event) {
lv_event_code_t code = lv_event_get_code(event);
auto* enable_switch = static_cast<lv_obj_t*>(lv_event_get_target(event));
if (code == LV_EVENT_VALUE_CHANGED) {
bool is_on = lv_obj_has_state(enable_switch, LV_STATE_CHECKED);
auto* data = static_cast<Data*>(lv_event_get_user_data(event));
if (data->power->isChargingEnabled() != is_on) {
data->power->setChargingEnabled(is_on);
updateUi(data);
auto data = optData();
if (data != nullptr) {
if (data->power->isChargingEnabled() != is_on) {
data->power->setChargingEnabled(is_on);
updateUi(data);
}
}
}
}
static void onShow(App& app, lv_obj_t* parent) {
static void onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app);
@@ -64,7 +87,7 @@ static void onShow(App& app, lv_obj_t* parent) {
lv_obj_set_flex_grow(wrapper, 1);
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
auto* data = static_cast<Data*>(app.getData());
auto data = std::static_pointer_cast<Data>(app.getData());
// Top row: enable/disable
lv_obj_t* switch_container = lv_obj_create(wrapper);
@@ -78,7 +101,7 @@ static void onShow(App& app, lv_obj_t* parent) {
lv_obj_set_align(enable_label, LV_ALIGN_LEFT_MID);
lv_obj_t* enable_switch = lv_switch_create(switch_container);
lv_obj_add_event_cb(enable_switch, onPowerEnabledChanged, LV_EVENT_VALUE_CHANGED, data);
lv_obj_add_event_cb(enable_switch, onPowerEnabledChanged, LV_EVENT_VALUE_CHANGED, nullptr);
lv_obj_set_align(enable_switch, LV_ALIGN_RIGHT_MID);
data->enable_switch = enable_switch;
@@ -90,32 +113,24 @@ static void onShow(App& app, lv_obj_t* parent) {
data->update_timer->start(ms_to_ticks(1000));
}
static void onHide(TT_UNUSED App& app) {
auto* data = static_cast<Data*>(app.getData());
static void onHide(TT_UNUSED AppContext& app) {
auto data = std::static_pointer_cast<Data>(app.getData());
data->update_timer->stop();
}
static void onStart(App& app) {
auto* data = new Data();
data->update_timer = new Timer(Timer::TypePeriodic, &updateUi, data);
static void onStart(AppContext& app) {
auto data = std::shared_ptr<Data>(new Data());
app.setData(data);
data->power = getConfiguration()->hardware->power;
assert(data->power != nullptr); // The Power app only shows up on supported devices
app.setData(data);
}
static void onStop(App& app) {
auto* data = static_cast<Data*>(app.getData());
delete data->update_timer;
delete data;
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "Power",
.name = "Power",
.icon = TT_ASSETS_APP_ICON_POWER_SETTINGS,
.type = TypeSettings,
.onStart = onStart,
.onStop = onStop,
.onShow = onShow,
.onHide = onHide
};
+7 -12
View File
@@ -1,29 +1,24 @@
#include "ScreenshotUi.h"
#include <memory>
namespace tt::app::screenshot {
static void onShow(App& app, lv_obj_t* parent) {
auto* ui = static_cast<ScreenshotUi*>(app.getData());
static void onShow(AppContext& app, lv_obj_t* parent) {
auto ui = std::static_pointer_cast<ScreenshotUi>(app.getData());
create_ui(app, ui, parent);
}
static void onStart(App& app) {
auto* ui = static_cast<ScreenshotUi*>(malloc(sizeof(ScreenshotUi)));
app.setData(ui);
static void onStart(AppContext& app) {
auto ui = std::shared_ptr<ScreenshotUi>(new ScreenshotUi());
app.setData(ui); // Ensure data gets deleted when no more in use
}
static void onStop(App& app) {
auto* ui = static_cast<ScreenshotUi*>(app.getData());
free(ui);
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "Screenshot",
.name = "_Screenshot", // So it gets put at the bottom of the desktop and becomes less visible on small screen devices
.icon = LV_SYMBOL_IMAGE,
.type = TypeSystem,
.onStart = onStart,
.onStop = onStop,
.onShow = onShow,
};
@@ -3,6 +3,7 @@
#include "TactilityCore.h"
#include "hal/sdcard/Sdcard.h"
#include "service/gui/Gui.h"
#include "service/loader/Loader.h"
#include "service/screenshot/Screenshot.h"
#include "lvgl/Toolbar.h"
@@ -10,29 +11,24 @@ namespace tt::app::screenshot {
#define TAG "screenshot_ui"
static void update_mode(ScreenshotUi* ui) {
lv_obj_t* label = ui->start_stop_button_label;
if (service::screenshot::isStarted()) {
lv_label_set_text(label, "Stop");
} else {
lv_label_set_text(label, "Start");
}
extern AppManifest manifest;
static void update_mode(std::shared_ptr<ScreenshotUi> ui);
uint32_t selected = lv_dropdown_get_selected(ui->mode_dropdown);
if (selected == 0) { // Timer
lv_obj_remove_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN);
/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */
std::shared_ptr<ScreenshotUi> _Nullable optScreenshotUi() {
app::AppContext* app = service::loader::getCurrentApp();
if (app->getManifest().id == manifest.id) {
return std::static_pointer_cast<ScreenshotUi>(app->getData());
} else {
lv_obj_add_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN);
return nullptr;
}
}
static void on_mode_set(lv_event_t* event) {
auto* ui = (ScreenshotUi*)lv_event_get_user_data(event);
update_mode(ui);
}
static void on_start_pressed(lv_event_t* event) {
auto* ui = static_cast<ScreenshotUi*>(lv_event_get_user_data(event));
auto ui = optScreenshotUi();
if (ui == nullptr) {
return;
}
if (service::screenshot::isStarted()) {
TT_LOG_I(TAG, "Stop screenshot");
@@ -58,7 +54,30 @@ static void on_start_pressed(lv_event_t* event) {
update_mode(ui);
}
static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) {
static void update_mode(std::shared_ptr<ScreenshotUi> ui) {
lv_obj_t* label = ui->start_stop_button_label;
if (service::screenshot::isStarted()) {
lv_label_set_text(label, "Stop");
} else {
lv_label_set_text(label, "Start");
}
uint32_t selected = lv_dropdown_get_selected(ui->mode_dropdown);
if (selected == 0) { // Timer
lv_obj_remove_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN);
}
}
static void on_mode_set(lv_event_t* event) {
auto ui = optScreenshotUi();
if (ui != nullptr) {
update_mode(ui);
}
}
static void create_mode_setting_ui(std::shared_ptr<ScreenshotUi> ui, lv_obj_t* parent) {
lv_obj_t* mode_wrapper = lv_obj_create(parent);
lv_obj_set_size(mode_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(mode_wrapper, 0, 0);
@@ -71,7 +90,7 @@ static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) {
lv_obj_t* mode_dropdown = lv_dropdown_create(mode_wrapper);
lv_dropdown_set_options(mode_dropdown, "Timer\nApp start");
lv_obj_align_to(mode_dropdown, mode_label, LV_ALIGN_OUT_RIGHT_MID, 8, 0);
lv_obj_add_event_cb(mode_dropdown, on_mode_set, LV_EVENT_VALUE_CHANGED, ui);
lv_obj_add_event_cb(mode_dropdown, on_mode_set, LV_EVENT_VALUE_CHANGED, nullptr);
ui->mode_dropdown = mode_dropdown;
service::screenshot::Mode mode = service::screenshot::getMode();
if (mode == service::screenshot::ScreenshotModeApps) {
@@ -83,10 +102,10 @@ static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) {
lv_obj_t* button_label = lv_label_create(button);
lv_obj_align(button_label, LV_ALIGN_CENTER, 0, 0);
ui->start_stop_button_label = button_label;
lv_obj_add_event_cb(button, &on_start_pressed, LV_EVENT_CLICKED, ui);
lv_obj_add_event_cb(button, &on_start_pressed, LV_EVENT_CLICKED, nullptr);
}
static void create_path_ui(ScreenshotUi* ui, lv_obj_t* parent) {
static void create_path_ui(std::shared_ptr<ScreenshotUi> ui, lv_obj_t* parent) {
lv_obj_t* path_wrapper = lv_obj_create(parent);
lv_obj_set_size(path_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(path_wrapper, 0, 0);
@@ -116,7 +135,7 @@ static void create_path_ui(ScreenshotUi* ui, lv_obj_t* parent) {
}
}
static void create_timer_settings_ui(ScreenshotUi* ui, lv_obj_t* parent) {
static void create_timer_settings_ui(std::shared_ptr<ScreenshotUi> ui, lv_obj_t* parent) {
lv_obj_t* timer_wrapper = lv_obj_create(parent);
lv_obj_set_size(timer_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(timer_wrapper, 0, 0);
@@ -153,7 +172,7 @@ static void create_timer_settings_ui(ScreenshotUi* ui, lv_obj_t* parent) {
lv_label_set_text(delay_unit_label, "seconds");
}
void create_ui(const App& app, ScreenshotUi* ui, lv_obj_t* parent) {
void create_ui(const AppContext& app, std::shared_ptr<ScreenshotUi> ui, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_t* toolbar = lvgl::toolbar_create(parent, app);
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
@@ -1,6 +1,6 @@
#pragma once
#include "app/App.h"
#include "app/AppContext.h"
#include "lvgl.h"
namespace tt::app::screenshot {
@@ -13,6 +13,6 @@ typedef struct {
lv_obj_t* delay_textarea;
} ScreenshotUi;
void create_ui(const App& app, ScreenshotUi* ui, lv_obj_t* parent);
void create_ui(const AppContext& app, std::shared_ptr<ScreenshotUi> ui, lv_obj_t* parent);
} // namespace
@@ -1,9 +1,9 @@
#include "SelectionDialog.h"
#include "Log.h"
#include "lvgl.h"
#include "lvgl/Toolbar.h"
#include "service/loader/Loader.h"
#include <StringUtils.h>
#include <TactilityCore.h>
namespace tt::app::selectiondialog {
@@ -27,17 +27,17 @@ int32_t getResultIndex(const Bundle& bundle) {
return index;
}
void setResultIndex(Bundle& bundle, int32_t index) {
bundle.putInt32(RESULT_BUNDLE_KEY_INDEX, index);
void setResultIndex(std::shared_ptr<Bundle> bundle, int32_t index) {
bundle->putInt32(RESULT_BUNDLE_KEY_INDEX, index);
}
void setTitleParameter(Bundle& bundle, const std::string& title) {
bundle.putString(PARAMETER_BUNDLE_KEY_TITLE, title);
void setTitleParameter(std::shared_ptr<Bundle> bundle, const std::string& title) {
bundle->putString(PARAMETER_BUNDLE_KEY_TITLE, title);
}
static std::string getTitleParameter(const Bundle& bundle) {
static std::string getTitleParameter(std::shared_ptr<const Bundle> bundle) {
std::string result;
if (bundle.optString(PARAMETER_BUNDLE_KEY_TITLE, result)) {
if (bundle->optString(PARAMETER_BUNDLE_KEY_TITLE, result)) {
return result;
} else {
return DEFAULT_TITLE;
@@ -49,8 +49,8 @@ static void onListItemSelected(lv_event_t* e) {
if (code == LV_EVENT_CLICKED) {
size_t index = (size_t)(e->user_data);
TT_LOG_I(TAG, "Selected item at index %d", index);
tt::app::App* app = service::loader::getCurrentApp();
Bundle bundle;
tt::app::AppContext* app = service::loader::getCurrentApp();
auto bundle = std::shared_ptr<Bundle>(new Bundle());
setResultIndex(bundle, (int32_t)index);
app->setResult(app::ResultOk, bundle);
service::loader::stopApp();
@@ -63,7 +63,7 @@ static void createChoiceItem(void* parent, const std::string& title, size_t inde
lv_obj_add_event_cb(btn, &onListItemSelected, LV_EVENT_CLICKED, (void*)index);
}
static void onShow(App& app, lv_obj_t* parent) {
static void onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
std::string title = getTitleParameter(app.getParameters());
lvgl::toolbar_create(parent, title);
@@ -72,16 +72,17 @@ static void onShow(App& app, lv_obj_t* parent) {
lv_obj_set_width(list, LV_PCT(100));
lv_obj_set_flex_grow(list, 1);
const Bundle& parameters = app.getParameters();
auto parameters = app.getParameters();
tt_check(parameters != nullptr, "No parameters");
std::string items_concatenated;
if (parameters.optString(PARAMETER_BUNDLE_KEY_ITEMS, items_concatenated)) {
if (parameters->optString(PARAMETER_BUNDLE_KEY_ITEMS, items_concatenated)) {
std::vector<std::string> items = string_split(items_concatenated, PARAMETER_ITEM_CONCATENATION_TOKEN);
if (items.empty() || items.front().empty()) {
TT_LOG_E(TAG, "No items provided");
app.setResult(ResultError);
service::loader::stopApp();
} else if (items.size() == 1) {
Bundle result_bundle;
auto result_bundle = std::shared_ptr<Bundle>(new Bundle());
setResultIndex(result_bundle, 0);
app.setResult(ResultOk, result_bundle);
service::loader::stopApp();
@@ -99,7 +100,7 @@ static void onShow(App& app, lv_obj_t* parent) {
}
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "SelectionDialog",
.name = "Selection Dialog",
.type = TypeHidden,
+4 -4
View File
@@ -11,12 +11,12 @@ namespace tt::app::settings {
static void onAppPressed(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
const auto* manifest = static_cast<const Manifest*>(lv_event_get_user_data(e));
const auto* manifest = static_cast<const AppManifest*>(lv_event_get_user_data(e));
service::loader::startApp(manifest->id);
}
}
static void createWidget(const Manifest* manifest, void* parent) {
static void createWidget(const AppManifest* manifest, void* parent) {
tt_check(parent);
auto* list = (lv_obj_t*)parent;
const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK;
@@ -24,7 +24,7 @@ static void createWidget(const Manifest* manifest, void* parent) {
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest);
}
static void onShow(App& app, lv_obj_t* parent) {
static void onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app);
@@ -42,7 +42,7 @@ static void onShow(App& app, lv_obj_t* parent) {
}
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "Settings",
.name = "Settings",
.icon = TT_ASSETS_APP_ICON_SETTINGS,
@@ -108,7 +108,7 @@ static void addRtosTasks(lv_obj_t* parent) {
#endif
static void onShow(App& app, lv_obj_t* parent) {
static void onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app);
@@ -151,7 +151,7 @@ static void onShow(App& app, lv_obj_t* parent) {
#endif
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "SystemInfo",
.name = "System Info",
.icon = TT_ASSETS_APP_ICON_SYSTEM_INFO,
@@ -1,4 +1,4 @@
#include "Log.h"
#include <TactilityCore.h>
#include "TextViewer.h"
#include "lvgl.h"
#include "lvgl/LabelUtils.h"
@@ -9,7 +9,7 @@
namespace tt::app::textviewer {
static void onShow(App& app, lv_obj_t* parent) {
static void onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app);
@@ -22,9 +22,10 @@ static void onShow(App& app, lv_obj_t* parent) {
lv_obj_t* label = lv_label_create(wrapper);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
const Bundle& bundle = app.getParameters();
auto parameters = app.getParameters();
tt_check(parameters != nullptr, "No parameters");
std::string file_argument;
if (bundle.optString(TEXT_VIEWER_FILE_ARGUMENT, file_argument)) {
if (parameters->optString(TEXT_VIEWER_FILE_ARGUMENT, file_argument)) {
TT_LOG_I(TAG, "Opening %s", file_argument.c_str());
lvgl::label_set_text_file(label, file_argument.c_str());
} else {
@@ -32,7 +33,7 @@ static void onShow(App& app, lv_obj_t* parent) {
}
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "TextViewer",
.name = "Text Viewer",
.type = TypeHidden,
@@ -1,6 +1,6 @@
#include "WifiApSettings.h"
#include "TactilityCore.h"
#include "app/App.h"
#include "app/AppContext.h"
#include "lvgl.h"
#include "lvgl/Style.h"
#include "lvgl/Toolbar.h"
@@ -11,11 +11,11 @@ namespace tt::app::wifiapsettings {
#define TAG "wifi_ap_settings"
extern const Manifest manifest;
extern const AppManifest manifest;
void start(const std::string& ssid) {
Bundle bundle;
bundle.putString("ssid", ssid);
auto bundle = std::shared_ptr<Bundle>(new Bundle());
bundle->putString("ssid", ssid);
service::loader::startApp(manifest.id, false, bundle);
}
@@ -39,9 +39,10 @@ static void onToggleAutoConnect(lv_event_t* event) {
}
}
static void onShow(App& app, lv_obj_t* parent) {
const Bundle& bundle = app.getParameters();
std::string ssid = bundle.getString("ssid");
static void onShow(AppContext& app, lv_obj_t* parent) {
auto paremeters = app.getParameters();
tt_check(paremeters != nullptr, "No parameters");
std::string ssid = paremeters->getString("ssid");
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, ssid);
@@ -69,7 +70,7 @@ static void onShow(App& app, lv_obj_t* parent) {
lv_obj_align(auto_connect_label, LV_ALIGN_TOP_LEFT, 0, 6);
lv_obj_t* auto_connect_switch = lv_switch_create(wrapper);
lv_obj_add_event_cb(auto_connect_switch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, (void*)&bundle);
lv_obj_add_event_cb(auto_connect_switch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, (void*)&paremeters);
lv_obj_align(auto_connect_switch, LV_ALIGN_TOP_RIGHT, 0, 0);
service::wifi::settings::WifiApSettings settings {};
@@ -85,7 +86,7 @@ static void onShow(App& app, lv_obj_t* parent) {
}
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "WifiApSettings",
.name = "Wi-Fi AP Settings",
.icon = LV_SYMBOL_WIFI,
+12 -10
View File
@@ -3,12 +3,12 @@
#include "Parameters.h"
#include "WifiConnect.h"
#include "Log.h"
#include "lvgl.h"
#include "service/gui/Gui.h"
#include "service/wifi/WifiSettings.h"
#include "lvgl/Style.h"
#include "lvgl/Toolbar.h"
#include <TactilityCore.h>
namespace tt::app::wificonnect {
@@ -107,7 +107,7 @@ void View::createBottomButtons(WifiConnect* wifi, lv_obj_t* parent) {
}
// TODO: Standardize dialogs
void View::init(App& app, WifiConnect* wifiConnect, lv_obj_t* parent) {
void View::init(AppContext& app, WifiConnect* wifiConnect, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app);
@@ -187,15 +187,17 @@ void View::init(App& app, WifiConnect* wifiConnect, lv_obj_t* parent) {
service::gui::keyboardAddTextArea(password_textarea);
// Init from app parameters
const Bundle& bundle = app.getParameters();
std::string ssid;
if (bundle.optString(WIFI_CONNECT_PARAM_SSID, ssid)) {
lv_textarea_set_text(ssid_textarea, ssid.c_str());
}
auto bundle = app.getParameters();
if (bundle != nullptr) {
std::string ssid;
if (bundle->optString(WIFI_CONNECT_PARAM_SSID, ssid)) {
lv_textarea_set_text(ssid_textarea, ssid.c_str());
}
std::string password;
if (bundle.optString(WIFI_CONNECT_PARAM_PASSWORD, password)) {
lv_textarea_set_text(password_textarea, password.c_str());
std::string password;
if (bundle->optString(WIFI_CONNECT_PARAM_PASSWORD, password)) {
lv_textarea_set_text(password_textarea, password.c_str());
}
}
}
+2 -2
View File
@@ -3,7 +3,7 @@
#include "Bindings.h"
#include "State.h"
#include "app/App.h"
#include "app/AppContext.h"
#include "lvgl.h"
namespace tt::app::wificonnect {
@@ -25,7 +25,7 @@ public:
lv_obj_t* connection_error = nullptr;
lv_group_t* group = nullptr;
void init(App& app, WifiConnect* wifiConnect, lv_obj_t* parent);
void init(AppContext& app, WifiConnect* wifiConnect, lv_obj_t* parent);
void update(Bindings* bindings, State* state);
void createBottomButtons(WifiConnect* wifi, lv_obj_t* parent);
@@ -1,6 +1,6 @@
#include "WifiConnect.h"
#include "app/App.h"
#include "app/AppContext.h"
#include "TactilityCore.h"
#include "service/loader/Loader.h"
#include "service/wifi/Wifi.h"
@@ -10,6 +10,18 @@ namespace tt::app::wificonnect {
#define TAG "wifi_connect"
extern const AppManifest manifest;
/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */
std::shared_ptr<WifiConnect> _Nullable optWifiConnect() {
app::AppContext* app = service::loader::getCurrentApp();
if (app->getManifest().id == manifest.id) {
return std::static_pointer_cast<WifiConnect>(app->getData());
} else {
return nullptr;
}
}
static void eventCallback(const void* message, void* context) {
auto* event = static_cast<const service::wifi::WifiEvent*>(message);
auto* wifi = static_cast<WifiConnect*>(context);
@@ -76,7 +88,7 @@ void WifiConnect::requestViewUpdate() {
unlock();
}
void WifiConnect::onShow(App& app, lv_obj_t* parent) {
void WifiConnect::onShow(AppContext& app, lv_obj_t* parent) {
lock();
view_enabled = true;
view.init(app, this, parent);
@@ -84,41 +96,35 @@ void WifiConnect::onShow(App& app, lv_obj_t* parent) {
unlock();
}
void WifiConnect::onHide(App& app) {
void WifiConnect::onHide(AppContext& app) {
// No need to lock view, as this is called from within Gui's LVGL context
lock();
view_enabled = false;
unlock();
}
static void onShow(App& app, lv_obj_t* parent) {
auto* wifi = static_cast<WifiConnect*>(app.getData());
static void onShow(AppContext& app, lv_obj_t* parent) {
auto wifi = std::static_pointer_cast<WifiConnect>(app.getData());
wifi->onShow(app, parent);
}
static void onHide(App& app) {
auto* wifi = static_cast<WifiConnect*>(app.getData());
static void onHide(AppContext& app) {
auto wifi = std::static_pointer_cast<WifiConnect>(app.getData());
wifi->onHide(app);
}
static void onStart(App& app) {
auto* wifi_connect = new WifiConnect();
app.setData(wifi_connect);
static void onStart(AppContext& app) {
auto wifi = std::shared_ptr<WifiConnect>(new WifiConnect());
app.setData(wifi);
}
static void onStop(App& app) {
auto* wifi_connect = static_cast<WifiConnect*>(app.getData());
tt_assert(wifi_connect != nullptr);
delete wifi_connect;
}
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "WifiConnect",
.name = "Wi-Fi Connect",
.icon = LV_SYMBOL_WIFI,
.type = TypeSettings,
.onStart = &onStart,
.onStop = &onStop,
.onShow = &onShow,
.onHide = &onHide
};
@@ -28,8 +28,8 @@ public:
void lock();
void unlock();
void onShow(App& app, lv_obj_t* parent);
void onHide(App& app);
void onShow(AppContext& app, lv_obj_t* parent);
void onHide(AppContext& app);
State& getState() { return state; }
Bindings& getBindings() { return bindings; }
+1 -1
View File
@@ -214,7 +214,7 @@ void View::updateEnableOnBootToggle() {
// region Main
void View::init(const App& app, Bindings* bindings, lv_obj_t* parent) {
void View::init(const AppContext& app, Bindings* bindings, lv_obj_t* parent) {
root = parent;
// Toolbar
+2 -2
View File
@@ -1,6 +1,6 @@
#pragma once
#include "app/App.h"
#include "app/AppContext.h"
#include "Bindings.h"
#include "State.h"
#include "lvgl.h"
@@ -16,7 +16,7 @@ private:
lv_obj_t* networks_list = nullptr;
public:
View() {}
void init(const App& app, Bindings* bindings, lv_obj_t* parent);
void init(const AppContext& app, Bindings* bindings, lv_obj_t* parent);
void update(Bindings* bindings, State* state);
private:
+13 -21
View File
@@ -2,7 +2,7 @@
#include "View.h"
#include "State.h"
#include "app/App.h"
#include "app/AppContext.h"
#include "app/wificonnect/Parameters.h"
#include "app/wifiapsettings/WifiApSettings.h"
#include "TactilityCore.h"
@@ -21,9 +21,9 @@ static void onConnect(const char* ssid) {
service::wifi::connect(&settings, false);
} else {
TT_LOG_I(TAG, "Starting connection dialog");
Bundle bundle;
bundle.putString(WIFI_CONNECT_PARAM_SSID, ssid);
bundle.putString(WIFI_CONNECT_PARAM_PASSWORD, "");
auto bundle = std::shared_ptr<Bundle>(new Bundle());
bundle->putString(WIFI_CONNECT_PARAM_SSID, ssid);
bundle->putString(WIFI_CONNECT_PARAM_PASSWORD, "");
service::loader::startApp("WifiConnect", false, bundle);
}
}
@@ -95,7 +95,7 @@ static void wifiManageEventCallback(const void* message, void* context) {
wifi->requestViewUpdate();
}
void WifiManage::onShow(App& app, lv_obj_t* parent) {
void WifiManage::onShow(AppContext& app, lv_obj_t* parent) {
PubSub* wifi_pubsub = service::wifi::getPubsub();
wifiSubscription = tt_pubsub_subscribe(wifi_pubsub, &wifiManageEventCallback, this);
@@ -121,7 +121,7 @@ void WifiManage::onShow(App& app, lv_obj_t* parent) {
}
}
void WifiManage::onHide(TT_UNUSED App& app) {
void WifiManage::onHide(TT_UNUSED AppContext& app) {
lock();
PubSub* wifi_pubsub = service::wifi::getPubsub();
tt_pubsub_unsubscribe(wifi_pubsub, wifiSubscription);
@@ -132,37 +132,29 @@ void WifiManage::onHide(TT_UNUSED App& app) {
// region Manifest methods
static void onStart(App& app) {
auto* wifi = new WifiManage();
static void onStart(AppContext& app) {
auto wifi = std::shared_ptr<WifiManage>(new WifiManage());
app.setData(wifi);
}
static void onStop(App& app) {
auto* wifi = (WifiManage*)app.getData();
tt_assert(wifi != nullptr);
delete wifi;
}
static void onShow(App& app, lv_obj_t* parent) {
auto* wifi = (WifiManage*)app.getData();
static void onShow(AppContext& app, lv_obj_t* parent) {
auto wifi = std::static_pointer_cast<WifiManage>(app.getData());
wifi->onShow(app, parent);
}
static void onHide(App& app) {
auto* wifi = (WifiManage*)app.getData();
static void onHide(AppContext& app) {
auto wifi = std::static_pointer_cast<WifiManage>(app.getData());
wifi->onHide(app);
}
// endregion
extern const Manifest manifest = {
extern const AppManifest manifest = {
.id = "WifiManage",
.name = "Wi-Fi",
.icon = LV_SYMBOL_WIFI,
.type = TypeSettings,
.onStart = onStart,
.onStop = onStop,
.onShow = onShow,
.onHide = onHide
};
+2 -2
View File
@@ -23,8 +23,8 @@ public:
void lock();
void unlock();
void onShow(App& app, lv_obj_t* parent);
void onHide(App& app);
void onShow(AppContext& app, lv_obj_t* parent);
void onHide(AppContext& app);
State& getState() { return state; }