Implement forgetting WiFi APs (#116)

.. and fix the simulator build
This commit is contained in:
Ken Van Hoeylandt 2024-12-09 23:37:21 +01:00 committed by GitHub
parent 80245e08fa
commit 103588948f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 166 additions and 80 deletions

View File

@ -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",

View File

@ -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
};

View 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;
}

View 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();

View File

@ -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"

View File

@ -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

View File

@ -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
);
}
}
}

View File

@ -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