mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-02-18 10:53:17 +00:00
parent
80245e08fa
commit
103588948f
@ -1,4 +1,5 @@
|
||||
#include "hal/Configuration.h"
|
||||
#include "hal/SimulatorPower.h"
|
||||
#include "LvglTask.h"
|
||||
#include "src/lv_init.h"
|
||||
#include "SdlDisplay.h"
|
||||
@ -6,8 +7,6 @@
|
||||
|
||||
#define TAG "hardware"
|
||||
|
||||
extern const tt::hal::Power power;
|
||||
|
||||
static bool initBoot() {
|
||||
lv_init();
|
||||
lvgl_task_start();
|
||||
@ -30,7 +29,7 @@ extern const tt::hal::Configuration hardware = {
|
||||
.createDisplay = createDisplay,
|
||||
.createKeyboard = createKeyboard,
|
||||
.sdcard = nullptr,
|
||||
.power = &power,
|
||||
.power = simulatorPower,
|
||||
.i2c = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "Internal",
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
#include "hal/Power.h"
|
||||
|
||||
static bool is_charging_enabled = false;
|
||||
|
||||
static bool isCharging() {
|
||||
return is_charging_enabled;
|
||||
}
|
||||
|
||||
static bool isChargingEnabled() {
|
||||
return is_charging_enabled;
|
||||
}
|
||||
|
||||
static void setChargingEnabled(bool enabled) {
|
||||
is_charging_enabled = enabled;
|
||||
}
|
||||
|
||||
static uint8_t getChargeLevel() {
|
||||
return 204;
|
||||
}
|
||||
|
||||
static int32_t getCurrent() {
|
||||
return is_charging_enabled ? 100 : -50;
|
||||
}
|
||||
|
||||
extern const tt::hal::Power power = {
|
||||
.isCharging = isCharging,
|
||||
.isChargingEnabled = isChargingEnabled,
|
||||
.setChargingEnabled = setChargingEnabled,
|
||||
.getChargeLevel = getChargeLevel,
|
||||
.getCurrent = getCurrent
|
||||
};
|
||||
44
Boards/Simulator/Source/hal/SimulatorPower.cpp
Normal file
44
Boards/Simulator/Source/hal/SimulatorPower.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include "SimulatorPower.h"
|
||||
|
||||
#define TAG "simulator_power"
|
||||
|
||||
bool SimulatorPower::supportsMetric(MetricType type) const {
|
||||
switch (type) {
|
||||
case IS_CHARGING:
|
||||
case CURRENT:
|
||||
case BATTERY_VOLTAGE:
|
||||
case CHARGE_LEVEL:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Safety guard for when new enum values are introduced
|
||||
}
|
||||
|
||||
bool SimulatorPower::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
switch (type) {
|
||||
case IS_CHARGING:
|
||||
data.valueAsBool = true;
|
||||
return true;
|
||||
case CURRENT:
|
||||
data.valueAsInt32 = 42;
|
||||
return true;
|
||||
case BATTERY_VOLTAGE:
|
||||
data.valueAsUint32 = 4032;
|
||||
return true;
|
||||
case CHARGE_LEVEL:
|
||||
data.valueAsUint8 = 80;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Safety guard for when new enum values are introduced
|
||||
}
|
||||
|
||||
static std::shared_ptr<Power> power;
|
||||
|
||||
std::shared_ptr<Power> simulatorPower() {
|
||||
if (power == nullptr) {
|
||||
power = std::make_shared<SimulatorPower>();
|
||||
}
|
||||
return power;
|
||||
}
|
||||
|
||||
25
Boards/Simulator/Source/hal/SimulatorPower.h
Normal file
25
Boards/Simulator/Source/hal/SimulatorPower.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "hal/Power.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace tt::hal;
|
||||
|
||||
class SimulatorPower : public Power {
|
||||
|
||||
bool allowedToCharge = false;
|
||||
|
||||
public:
|
||||
|
||||
SimulatorPower() {}
|
||||
~SimulatorPower() {}
|
||||
|
||||
bool supportsMetric(MetricType type) const override;
|
||||
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
||||
|
||||
bool supportsChargeControl() const { return true; }
|
||||
bool isAllowedToCharge() const { return allowedToCharge; }
|
||||
void setAllowedToCharge(bool canCharge) { allowedToCharge = canCharge; }
|
||||
};
|
||||
|
||||
std::shared_ptr<Power> simulatorPower();
|
||||
@ -1,10 +1,11 @@
|
||||
# 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
|
||||
- Bug: I2C Scanner is on M5Stack devices is broken
|
||||
- WiFi AP Connect app: add "Forget" option.
|
||||
- T-Deck Plus: Implement battery status
|
||||
- Make firmwares available via release process
|
||||
- Make firmwares available via web serial website
|
||||
- Bug: When closing a top level app, there's often an error "can't stop root app"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "WifiApSettings.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "app/selectiondialog/SelectionDialog.h"
|
||||
#include "lvgl.h"
|
||||
#include "lvgl/Style.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
@ -29,6 +30,14 @@ void start(const std::string& ssid) {
|
||||
service::loader::startApp(manifest.id, false, bundle);
|
||||
}
|
||||
|
||||
static void onPressForget(lv_event_t* event) {
|
||||
std::vector<std::string> choices = {
|
||||
"Yes",
|
||||
"No"
|
||||
};
|
||||
selectiondialog::start("Are you sure?", choices);
|
||||
}
|
||||
|
||||
static void onToggleAutoConnect(lv_event_t* event) {
|
||||
lv_event_code_t code = lv_event_get_code(event);
|
||||
|
||||
@ -67,30 +76,35 @@ static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
|
||||
// Wrappers
|
||||
|
||||
lv_obj_t* secondary_flex = lv_obj_create(parent);
|
||||
lv_obj_set_width(secondary_flex, LV_PCT(100));
|
||||
lv_obj_set_flex_grow(secondary_flex, 1);
|
||||
lv_obj_set_flex_flow(secondary_flex, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_style_border_width(secondary_flex, 0, 0);
|
||||
lvgl::obj_set_style_no_padding(secondary_flex);
|
||||
lvgl::obj_set_style_bg_invisible(secondary_flex);
|
||||
|
||||
// align() methods don't work on flex, so we need this extra wrapper
|
||||
lv_obj_t* wrapper = lv_obj_create(secondary_flex);
|
||||
lv_obj_set_size(wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_t* wrapper = lv_obj_create(parent);
|
||||
lv_obj_set_width(wrapper, LV_PCT(100));
|
||||
lv_obj_set_flex_grow(wrapper, 1);
|
||||
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
|
||||
lvgl::obj_set_style_bg_invisible(wrapper);
|
||||
lv_obj_set_style_border_width(wrapper, 0, 0);
|
||||
|
||||
// Auto-connect toggle
|
||||
|
||||
lv_obj_t* auto_connect_label = lv_label_create(wrapper);
|
||||
lv_obj_t* auto_connect_wrapper = lv_obj_create(wrapper);
|
||||
lv_obj_set_size(auto_connect_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lvgl::obj_set_style_no_padding(auto_connect_wrapper);
|
||||
lv_obj_set_style_border_width(auto_connect_wrapper, 0, 0);
|
||||
|
||||
lv_obj_t* auto_connect_label = lv_label_create(auto_connect_wrapper);
|
||||
lv_label_set_text(auto_connect_label, "Auto-connect");
|
||||
lv_obj_align(auto_connect_label, LV_ALIGN_TOP_LEFT, 0, 6);
|
||||
|
||||
lv_obj_t* auto_connect_switch = lv_switch_create(wrapper);
|
||||
lv_obj_t* auto_connect_switch = lv_switch_create(auto_connect_wrapper);
|
||||
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);
|
||||
|
||||
lv_obj_t* forget_button = lv_button_create(wrapper);
|
||||
lv_obj_set_width(forget_button, LV_PCT(100));
|
||||
lv_obj_align_to(forget_button, auto_connect_wrapper, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
|
||||
lv_obj_add_event_cb(forget_button, onPressForget, LV_EVENT_CLICKED, nullptr);
|
||||
lv_obj_t* forget_button_label = lv_label_create(forget_button);
|
||||
lv_obj_align(forget_button_label, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_label_set_text(forget_button_label, "Forget");
|
||||
|
||||
service::wifi::settings::WifiApSettings settings {};
|
||||
if (service::wifi::settings::load(ssid.c_str(), &settings)) {
|
||||
if (settings.auto_connect) {
|
||||
@ -104,12 +118,43 @@ static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
}
|
||||
}
|
||||
|
||||
void onResult(AppContext& app, Result result, const Bundle& bundle) {
|
||||
auto index = selectiondialog::getResultIndex(bundle);
|
||||
if (index == 0) {// Yes
|
||||
auto* app = optWifiApSettingsApp();
|
||||
if (app == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto parameters = app->getParameters();
|
||||
tt_check(parameters != nullptr, "Parameters missing");
|
||||
|
||||
std::string ssid = parameters->getString("ssid");
|
||||
if (service::wifi::settings::remove(ssid.c_str())) {
|
||||
TT_LOG_I(TAG, "Removed SSID");
|
||||
|
||||
if (
|
||||
service::wifi::getRadioState() == service::wifi::WIFI_RADIO_CONNECTION_ACTIVE &&
|
||||
service::wifi::getConnectionTarget() == ssid
|
||||
) {
|
||||
service::wifi::disconnect();
|
||||
}
|
||||
|
||||
// Stop self
|
||||
service::loader::stopApp();
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Failed to remove SSID");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern const AppManifest manifest = {
|
||||
.id = "WifiApSettings",
|
||||
.name = "Wi-Fi AP Settings",
|
||||
.icon = LV_SYMBOL_WIFI,
|
||||
.type = TypeHidden,
|
||||
.onShow = onShow
|
||||
.onShow = onShow,
|
||||
.onResult = onResult
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -267,39 +267,15 @@ static void do_stop_app() {
|
||||
TT_LOG_I(TAG, "Free heap: %zu", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
|
||||
#endif
|
||||
|
||||
app::AppOnResult on_result = nullptr;
|
||||
app::AppInstance* app_to_resume = nullptr;
|
||||
// If there's a previous app, resume it
|
||||
if (!loader_singleton->app_stack.empty()) {
|
||||
app::AppInstance* app_to_resume = loader_singleton->app_stack.top();
|
||||
app_to_resume = loader_singleton->app_stack.top();
|
||||
tt_assert(app_to_resume);
|
||||
app_transition_to_state(*app_to_resume, app::StateShowing);
|
||||
|
||||
auto on_result = app_to_resume->getManifest().onResult;
|
||||
if (on_result != nullptr) {
|
||||
if (result_holder != nullptr) {
|
||||
auto result_bundle = result_holder->resultData.get();
|
||||
if (result_bundle != nullptr) {
|
||||
on_result(
|
||||
*app_to_resume,
|
||||
result_holder->result,
|
||||
*result_bundle
|
||||
);
|
||||
} else {
|
||||
const Bundle empty_bundle;
|
||||
on_result(
|
||||
*app_to_resume,
|
||||
result_holder->result,
|
||||
empty_bundle
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const Bundle empty_bundle;
|
||||
on_result(
|
||||
*app_to_resume,
|
||||
app::ResultCancelled,
|
||||
empty_bundle
|
||||
);
|
||||
}
|
||||
}
|
||||
on_result = app_to_resume->getManifest().onResult;
|
||||
}
|
||||
|
||||
loader_unlock();
|
||||
@ -314,6 +290,33 @@ static void do_stop_app() {
|
||||
}
|
||||
};
|
||||
tt_pubsub_publish(loader_singleton->pubsub_external, &event_external);
|
||||
|
||||
if (on_result != nullptr && app_to_resume != nullptr) {
|
||||
if (result_holder != nullptr) {
|
||||
auto result_bundle = result_holder->resultData.get();
|
||||
if (result_bundle != nullptr) {
|
||||
on_result(
|
||||
*app_to_resume,
|
||||
result_holder->result,
|
||||
*result_bundle
|
||||
);
|
||||
} else {
|
||||
const Bundle empty_bundle;
|
||||
on_result(
|
||||
*app_to_resume,
|
||||
result_holder->result,
|
||||
empty_bundle
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const Bundle empty_bundle;
|
||||
on_result(
|
||||
*app_to_resume,
|
||||
app::ResultCancelled,
|
||||
empty_bundle
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -16,11 +16,11 @@ bool load(const char* ssid, WifiApSettings* settings) {
|
||||
}
|
||||
|
||||
bool save(const WifiApSettings* settings) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(const char* ssid) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user