mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-02-18 10:53:17 +00:00
Implemented AlertDialog app (#117)
This commit is contained in:
parent
103588948f
commit
77280def1d
@ -1,6 +1,5 @@
|
||||
# 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
|
||||
- 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::`
|
||||
- Gpio: Use Timer instead of Thread
|
||||
- I2cScannerThread: Use Timer instead of Thread
|
||||
@ -36,7 +35,6 @@
|
||||
|
||||
# App Ideas
|
||||
- System logger
|
||||
- Add FreeRTOS task manager functionality to System Info app
|
||||
- BlueTooth keyboard app
|
||||
- Chip 8 emulator
|
||||
- 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 "app/ManifestRegistry.h"
|
||||
@ -36,6 +35,7 @@ static const std::vector<const service::ServiceManifest*> system_services = {
|
||||
// region Default apps
|
||||
|
||||
namespace app {
|
||||
namespace alertdialog { extern const AppManifest manifest; }
|
||||
namespace boot { extern const AppManifest manifest; }
|
||||
namespace desktop { extern const AppManifest manifest; }
|
||||
namespace files { extern const AppManifest manifest; }
|
||||
@ -63,6 +63,7 @@ extern const app::AppManifest screenshot_app;
|
||||
#endif
|
||||
|
||||
static const std::vector<const app::AppManifest*> system_apps = {
|
||||
&app::alertdialog::manifest,
|
||||
&app::boot::manifest,
|
||||
&app::desktop::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);
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include "WifiApSettings.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "app/selectiondialog/SelectionDialog.h"
|
||||
#include "app/alertdialog/AlertDialog.h"
|
||||
#include "lvgl.h"
|
||||
#include "lvgl/Style.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
@ -30,12 +30,12 @@ void start(const std::string& ssid) {
|
||||
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 = {
|
||||
"Yes",
|
||||
"No"
|
||||
};
|
||||
selectiondialog::start("Are you sure?", choices);
|
||||
alertdialog::start("Confirmation", "Forget the Wi-Fi access point?", choices);
|
||||
}
|
||||
|
||||
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) {
|
||||
auto index = selectiondialog::getResultIndex(bundle);
|
||||
void onResult(TT_UNUSED AppContext& app, TT_UNUSED Result result, const Bundle& bundle) {
|
||||
auto index = alertdialog::getResultIndex(bundle);
|
||||
if (index == 0) {// Yes
|
||||
auto* app = optWifiApSettingsApp();
|
||||
if (app == nullptr) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user