mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-02-18 19:03:16 +00:00
Implemented AlertDialog app (#117)
This commit is contained in:
parent
103588948f
commit
77280def1d
@ -1,6 +1,5 @@
|
|||||||
# TODOs
|
# TODOs
|
||||||
- AppContext's onResult should pass the app id (or launch request id!) that was started, so we can differentiate between multiple types of apps being launched
|
- AppContext's onResult should pass the app id (or launch request id!) that was started, so we can differentiate between multiple types of apps being launched
|
||||||
- Create AlertDialog app like on Android with title, message and optional buttons. Update WifiApSettings with it.
|
|
||||||
- Loader: Use Timer instead of Thread, and move API to `tt::app::`
|
- Loader: Use Timer instead of Thread, and move API to `tt::app::`
|
||||||
- Gpio: Use Timer instead of Thread
|
- Gpio: Use Timer instead of Thread
|
||||||
- I2cScannerThread: Use Timer instead of Thread
|
- I2cScannerThread: Use Timer instead of Thread
|
||||||
@ -36,7 +35,6 @@
|
|||||||
|
|
||||||
# App Ideas
|
# App Ideas
|
||||||
- System logger
|
- System logger
|
||||||
- Add FreeRTOS task manager functionality to System Info app
|
|
||||||
- BlueTooth keyboard app
|
- BlueTooth keyboard app
|
||||||
- Chip 8 emulator
|
- Chip 8 emulator
|
||||||
- BadUSB (in December 2024, TinyUSB has a bug where uninstalling and re-installing the driver fails)
|
- BadUSB (in December 2024, TinyUSB has a bug where uninstalling and re-installing the driver fails)
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#include <Dispatcher.h>
|
|
||||||
#include "Tactility.h"
|
#include "Tactility.h"
|
||||||
|
|
||||||
#include "app/ManifestRegistry.h"
|
#include "app/ManifestRegistry.h"
|
||||||
@ -36,6 +35,7 @@ static const std::vector<const service::ServiceManifest*> system_services = {
|
|||||||
// region Default apps
|
// region Default apps
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
namespace alertdialog { extern const AppManifest manifest; }
|
||||||
namespace boot { extern const AppManifest manifest; }
|
namespace boot { extern const AppManifest manifest; }
|
||||||
namespace desktop { extern const AppManifest manifest; }
|
namespace desktop { extern const AppManifest manifest; }
|
||||||
namespace files { extern const AppManifest manifest; }
|
namespace files { extern const AppManifest manifest; }
|
||||||
@ -63,6 +63,7 @@ extern const app::AppManifest screenshot_app;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const std::vector<const app::AppManifest*> system_apps = {
|
static const std::vector<const app::AppManifest*> system_apps = {
|
||||||
|
&app::alertdialog::manifest,
|
||||||
&app::boot::manifest,
|
&app::boot::manifest,
|
||||||
&app::desktop::manifest,
|
&app::desktop::manifest,
|
||||||
&app::display::manifest,
|
&app::display::manifest,
|
||||||
|
|||||||
125
Tactility/Source/app/alertdialog/AlertDialog.cpp
Normal file
125
Tactility/Source/app/alertdialog/AlertDialog.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
#include "AlertDialog.h"
|
||||||
|
#include "lvgl.h"
|
||||||
|
#include "lvgl/Toolbar.h"
|
||||||
|
#include "service/loader/Loader.h"
|
||||||
|
#include <StringUtils.h>
|
||||||
|
#include <TactilityCore.h>
|
||||||
|
|
||||||
|
namespace tt::app::alertdialog {
|
||||||
|
|
||||||
|
#define PARAMETER_BUNDLE_KEY_TITLE "title"
|
||||||
|
#define PARAMETER_BUNDLE_KEY_MESSAGE "message"
|
||||||
|
#define PARAMETER_BUNDLE_KEY_BUTTON_LABELS "buttonLabels"
|
||||||
|
#define RESULT_BUNDLE_KEY_INDEX "index"
|
||||||
|
|
||||||
|
#define PARAMETER_ITEM_CONCATENATION_TOKEN ";;"
|
||||||
|
#define DEFAULT_TITLE "Select..."
|
||||||
|
|
||||||
|
#define TAG "selection_dialog"
|
||||||
|
|
||||||
|
extern const AppManifest manifest;
|
||||||
|
|
||||||
|
void start(std::string title, std::string message, const std::vector<std::string>& buttonLabels) {
|
||||||
|
std::string items_joined = string::join(buttonLabels, PARAMETER_ITEM_CONCATENATION_TOKEN);
|
||||||
|
auto bundle = std::make_shared<Bundle>();
|
||||||
|
bundle->putString(PARAMETER_BUNDLE_KEY_TITLE, title);
|
||||||
|
bundle->putString(PARAMETER_BUNDLE_KEY_MESSAGE, message);
|
||||||
|
bundle->putString(PARAMETER_BUNDLE_KEY_BUTTON_LABELS, items_joined);
|
||||||
|
service::loader::startApp(manifest.id, false, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t getResultIndex(const Bundle& bundle) {
|
||||||
|
int32_t index = -1;
|
||||||
|
bundle.optInt32(RESULT_BUNDLE_KEY_INDEX, index);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setResultIndex(std::shared_ptr<Bundle> bundle, int32_t index) {
|
||||||
|
bundle->putInt32(RESULT_BUNDLE_KEY_INDEX, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getTitleParameter(std::shared_ptr<const Bundle> bundle) {
|
||||||
|
std::string result;
|
||||||
|
if (bundle->optString(PARAMETER_BUNDLE_KEY_TITLE, result)) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return DEFAULT_TITLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onButtonClicked(lv_event_t* e) {
|
||||||
|
lv_event_code_t code = lv_event_get_code(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::AppContext* app = service::loader::getCurrentApp();
|
||||||
|
auto bundle = std::make_shared<Bundle>();
|
||||||
|
setResultIndex(bundle, (int32_t)index);
|
||||||
|
app->setResult(app::ResultOk, bundle);
|
||||||
|
service::loader::stopApp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createButton(lv_obj_t* parent, const std::string& text, size_t index) {
|
||||||
|
lv_obj_t* button = lv_button_create(parent);
|
||||||
|
lv_obj_t* button_label = lv_label_create(button);
|
||||||
|
lv_obj_align(button_label, LV_ALIGN_CENTER, 0, 0);
|
||||||
|
lv_label_set_text(button_label, text.c_str());
|
||||||
|
lv_obj_add_event_cb(button, &onButtonClicked, LV_EVENT_CLICKED, (void*)index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||||
|
auto parameters = app.getParameters();
|
||||||
|
tt_check(parameters != nullptr, "Parameters missing");
|
||||||
|
|
||||||
|
std::string title = getTitleParameter(app.getParameters());
|
||||||
|
lv_obj_t* toolbar = lvgl::toolbar_create(parent, title);
|
||||||
|
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
|
||||||
|
|
||||||
|
lv_obj_t* message_label = lv_label_create(parent);
|
||||||
|
lv_obj_align(message_label, LV_ALIGN_CENTER, 0, 0);
|
||||||
|
|
||||||
|
std::string message;
|
||||||
|
if (parameters->optString(PARAMETER_BUNDLE_KEY_MESSAGE, message)) {
|
||||||
|
lv_label_set_text(message_label, message.c_str());
|
||||||
|
lv_label_set_long_mode(message_label, LV_LABEL_LONG_WRAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_t* button_wrapper = lv_obj_create(parent);
|
||||||
|
lv_obj_set_flex_flow(button_wrapper, LV_FLEX_FLOW_ROW);
|
||||||
|
lv_obj_set_size(button_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
|
lv_obj_set_style_pad_all(button_wrapper, 0, 0);
|
||||||
|
lv_obj_set_flex_align(button_wrapper, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||||
|
lv_obj_set_style_border_width(button_wrapper, 0, 0);
|
||||||
|
lv_obj_align(button_wrapper, LV_ALIGN_BOTTOM_MID, 0, -4);
|
||||||
|
|
||||||
|
std::string items_concatenated;
|
||||||
|
if (parameters->optString(PARAMETER_BUNDLE_KEY_BUTTON_LABELS, items_concatenated)) {
|
||||||
|
std::vector<std::string> labels = string::split(items_concatenated, PARAMETER_ITEM_CONCATENATION_TOKEN);
|
||||||
|
if (labels.empty() || labels.front().empty()) {
|
||||||
|
TT_LOG_E(TAG, "No items provided");
|
||||||
|
app.setResult(ResultError);
|
||||||
|
service::loader::stopApp();
|
||||||
|
} else if (labels.size() == 1) {
|
||||||
|
auto result_bundle = std::make_shared<Bundle>();
|
||||||
|
setResultIndex(result_bundle, 0);
|
||||||
|
app.setResult(ResultOk, result_bundle);
|
||||||
|
service::loader::stopApp();
|
||||||
|
TT_LOG_W(TAG, "Auto-selecting single item");
|
||||||
|
} else {
|
||||||
|
size_t index = 0;
|
||||||
|
for (const auto& label: labels) {
|
||||||
|
createButton(button_wrapper, label, index++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern const AppManifest manifest = {
|
||||||
|
.id = "AlertDialog",
|
||||||
|
.name = "Alert Dialog",
|
||||||
|
.type = TypeHidden,
|
||||||
|
.onShow = onShow
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
23
Tactility/Source/app/alertdialog/AlertDialog.h
Normal file
23
Tactility/Source/app/alertdialog/AlertDialog.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "Bundle.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the app by its ID and provide:
|
||||||
|
* - a title
|
||||||
|
* - a text
|
||||||
|
* - 0, 1 or more buttons
|
||||||
|
*/
|
||||||
|
namespace tt::app::alertdialog {
|
||||||
|
|
||||||
|
void start(std::string title, std::string message, const std::vector<std::string>& buttonLabels);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index of the button that the user selected.
|
||||||
|
*
|
||||||
|
* @return a value greater than 0 when a selection was done, or -1 when the app was closed clicking one of the selection buttons.
|
||||||
|
*/
|
||||||
|
int32_t getResultIndex(const Bundle& bundle);
|
||||||
|
}
|
||||||
@ -109,15 +109,3 @@ extern const AppManifest manifest = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
extern void tt_app_selectiondialog_start2(const char* title, int argc, const char* argv[]) {
|
|
||||||
std::vector<std::string> list;
|
|
||||||
for (int i = 0; i < argc; i++) {
|
|
||||||
const char* item = argv[i];
|
|
||||||
list.push_back(item);
|
|
||||||
}
|
|
||||||
tt::app::selectiondialog::start(title, list);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@ -16,7 +16,10 @@ namespace tt::app::selectiondialog {
|
|||||||
|
|
||||||
void start(std::string title, const std::vector<std::string>& items);
|
void start(std::string title, const std::vector<std::string>& items);
|
||||||
|
|
||||||
/** App result data */
|
/**
|
||||||
|
* Get the index of the item that the user selected.
|
||||||
|
*
|
||||||
|
* @return a value greater than 0 when a selection was done, or -1 when the app was closed without selecting an item.
|
||||||
|
*/
|
||||||
int32_t getResultIndex(const Bundle& bundle);
|
int32_t getResultIndex(const Bundle& bundle);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#include "WifiApSettings.h"
|
#include "WifiApSettings.h"
|
||||||
#include "TactilityCore.h"
|
#include "TactilityCore.h"
|
||||||
#include "app/AppContext.h"
|
#include "app/AppContext.h"
|
||||||
#include "app/selectiondialog/SelectionDialog.h"
|
#include "app/alertdialog/AlertDialog.h"
|
||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
#include "lvgl/Style.h"
|
#include "lvgl/Style.h"
|
||||||
#include "lvgl/Toolbar.h"
|
#include "lvgl/Toolbar.h"
|
||||||
@ -30,12 +30,12 @@ void start(const std::string& ssid) {
|
|||||||
service::loader::startApp(manifest.id, false, bundle);
|
service::loader::startApp(manifest.id, false, bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onPressForget(lv_event_t* event) {
|
static void onPressForget(TT_UNUSED lv_event_t* event) {
|
||||||
std::vector<std::string> choices = {
|
std::vector<std::string> choices = {
|
||||||
"Yes",
|
"Yes",
|
||||||
"No"
|
"No"
|
||||||
};
|
};
|
||||||
selectiondialog::start("Are you sure?", choices);
|
alertdialog::start("Confirmation", "Forget the Wi-Fi access point?", choices);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onToggleAutoConnect(lv_event_t* event) {
|
static void onToggleAutoConnect(lv_event_t* event) {
|
||||||
@ -118,8 +118,8 @@ static void onShow(AppContext& app, lv_obj_t* parent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onResult(AppContext& app, Result result, const Bundle& bundle) {
|
void onResult(TT_UNUSED AppContext& app, TT_UNUSED Result result, const Bundle& bundle) {
|
||||||
auto index = selectiondialog::getResultIndex(bundle);
|
auto index = alertdialog::getResultIndex(bundle);
|
||||||
if (index == 0) {// Yes
|
if (index == 0) {// Yes
|
||||||
auto* app = optWifiApSettingsApp();
|
auto* app = optWifiApSettingsApp();
|
||||||
if (app == nullptr) {
|
if (app == nullptr) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user