App and Service improvements (#106)
This commit is contained in:
committed by
GitHub
parent
e86a11b7e2
commit
50ee77d572
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user