summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcooljqln <cooljqln@noreply.codeberg.org>2025-03-12 23:51:40 +0000
committercooljqln <cooljqln@noreply.codeberg.org>2025-03-12 23:51:40 +0000
commitfe38a57120145f73abf170400fe4a0eb0fd2714a (patch)
tree80c2ca8003fe32e59eccf2e208a53e1e87c66790
parent42c2a4f2445ff56a2a0a78c4ef265e5be346d40d (diff)
parent9fdf94e9cee5a5180ffefc2b8314f7a9879ebbc6 (diff)
downloadtangara-fw-fe38a57120145f73abf170400fe4a0eb0fd2714a.tar.gz
Merge pull request 'Allow manually unmounting the SD card' (#271) from ayumi/tangara-fw:unmount into main
Reviewed-on: https://codeberg.org/cool-tech-zone/tangara-fw/pulls/271
-rw-r--r--lua/images.lua1
-rw-r--r--lua/img/unmount.pngbin0 -> 107 bytes
-rw-r--r--lua/main_menu.lua21
-rw-r--r--lua/settings.lua77
-rw-r--r--src/tangara/audio/audio_events.hpp4
-rw-r--r--src/tangara/audio/audio_fsm.cpp7
-rw-r--r--src/tangara/audio/audio_fsm.hpp4
-rw-r--r--src/tangara/system_fsm/running.cpp15
-rw-r--r--src/tangara/system_fsm/system_events.hpp3
-rw-r--r--src/tangara/system_fsm/system_fsm.hpp5
-rw-r--r--src/tangara/ui/ui_fsm.cpp5
-rw-r--r--src/tangara/ui/ui_fsm.hpp2
12 files changed, 135 insertions, 9 deletions
diff --git a/lua/images.lua b/lua/images.lua
index 21a1aa9a..a492b9a2 100644
--- a/lua/images.lua
+++ b/lua/images.lua
@@ -29,6 +29,7 @@ local img = {
unlistened = lvgl.ImgData("//lua/img/unlistened.png"),
info = lvgl.ImgData("//lua/img/info.png"),
menu = lvgl.ImgData("//lua/img/menu.png"),
+ unmount = lvgl.ImgData("//lua/img/unmount.png"),
file_directory = lvgl.ImgData("//lua/img/file_icons/directory.png"),
file_playlist = lvgl.ImgData("//lua/img/file_icons/playlist.png"),
file_music = lvgl.ImgData("//lua/img/file_icons/music.png"),
diff --git a/lua/img/unmount.png b/lua/img/unmount.png
new file mode 100644
index 00000000..9bd8a674
--- /dev/null
+++ b/lua/img/unmount.png
Binary files differ
diff --git a/lua/main_menu.lua b/lua/main_menu.lua
index 8754df85..3f7f63e7 100644
--- a/lua/main_menu.lua
+++ b/lua/main_menu.lua
@@ -15,6 +15,7 @@ local font = require("font")
local theme = require("theme")
local img = require("images")
local playback = require("playback")
+local usb = require("usb")
return widgets.MenuScreen:new {
create_ui = function(self)
@@ -238,6 +239,26 @@ return widgets.MenuScreen:new {
end)
}
+ local unmount_btn = bottom_bar:Button {}
+ unmount_btn:onClicked(function()
+ sd_card.unmount()
+ end)
+ unmount_btn:Image { src = img.unmount }
+ widgets.Description(unmount_btn, "Unmount the SD Card")
+ theme.set_subject(unmount_btn, "menu_icon")
+ local unmount_btn_bind = function()
+ if sd_card.mounted:get() and not database.updating:get() and not usb.msc_enabled:get() then
+ unmount_btn:clear_flag(lvgl.FLAG.HIDDEN)
+ else
+ unmount_btn:add_flag(lvgl.FLAG.HIDDEN)
+ end
+ end
+ self.bindings = self.bindings + {
+ sd_card.mounted:bind(unmount_btn_bind),
+ database.updating:bind(unmount_btn_bind),
+ usb.msc_enabled:bind(unmount_btn_bind)
+ }
+
local files_btn = bottom_bar:Button {}
files_btn:onClicked(function()
backstack.push(require("file_browser"):new {
diff --git a/lua/settings.lua b/lua/settings.lua
index 0238a876..5ed93521 100644
--- a/lua/settings.lua
+++ b/lua/settings.lua
@@ -18,6 +18,7 @@ local font = require("font")
local main_menu = require("main_menu")
local img = require("images")
local nvs = require("nvs")
+local sd_card = require("sd_card")
local settings = {}
@@ -512,6 +513,77 @@ settings.InputSettings = SettingsScreen:new {
end
}
+settings.SDSettings = SettingsScreen:new {
+ title = "SD Card",
+ create_ui = function(self)
+ SettingsScreen.create_ui(self)
+
+ local actions_container = self.content:Object {
+ w = lvgl.PCT(100),
+ h = lvgl.SIZE_CONTENT,
+ flex = {
+ flex_direction = "row",
+ justify_content = "center",
+ align_items = "space-evenly",
+ align_content = "center",
+ },
+ pad_top = 4,
+ pad_column = 4,
+ }
+ actions_container:add_style(styles.list_item)
+
+ local busy_text = self.content:Label {
+ w = lvgl.PCT(100),
+ text = "",
+ long_mode = lvgl.LABEL.LONG_WRAP,
+ }
+ local busy_text_bind = function()
+ if database.updating:get() then
+ busy_text:clear_flag(lvgl.FLAG.HIDDEN)
+ busy_text:set {
+ text = "Your database is currently updating. Please wait."
+ }
+ elseif usb.msc_enabled:get() then
+ busy_text:clear_flag(lvgl.FLAG.HIDDEN)
+ busy_text:set {
+ text = "USB Mass Storage is currently enabled. Please disable it before unmounting."
+ }
+ elseif not sd_card.mounted:get() then
+ busy_text:clear_flag(lvgl.FLAG.HIDDEN)
+ busy_text:set {
+ text = "No SD card is currently mounted."
+ }
+ else
+ busy_text:add_flag(lvgl.FLAG.HIDDEN)
+ end
+ end
+ self.bindings = self.bindings + {
+ sd_card.mounted:bind(busy_text_bind),
+ database.updating:bind(busy_text_bind),
+ usb.msc_enabled:bind(busy_text_bind)
+ }
+
+ local unmount_btn = actions_container:Button {}
+ unmount_btn:Label { text = "Unmount" }
+ unmount_btn:onClicked(function()
+ sd_card.unmount()
+ end)
+ local unmount_btn_bind = function()
+ if sd_card.mounted:get() and not database.updating:get() and not usb.msc_enabled:get() then
+ unmount_btn:clear_flag(lvgl.FLAG.HIDDEN)
+ else
+ unmount_btn:add_flag(lvgl.FLAG.HIDDEN)
+ end
+ end
+ self.bindings = self.bindings + {
+ sd_card.mounted:bind(unmount_btn_bind),
+ database.updating:bind(unmount_btn_bind),
+ usb.msc_enabled:bind(unmount_btn_bind)
+ }
+ unmount_btn:focus()
+ end
+}
+
settings.MassStorageSettings = SettingsScreen:new {
title = "USB Storage",
create_ui = function(self)
@@ -944,8 +1016,9 @@ settings.Root = widgets.MenuScreen:new {
submenu("Theme", settings.ThemeSettings)
submenu("Input Method", settings.InputSettings)
- section("USB")
- submenu("Storage", settings.MassStorageSettings)
+ section("Storage")
+ submenu("SD Card", settings.SDSettings)
+ submenu("USB", settings.MassStorageSettings)
section("System")
submenu("Database", settings.DatabaseSettings)
diff --git a/src/tangara/audio/audio_events.hpp b/src/tangara/audio/audio_events.hpp
index 89dc28ff..24a5cf83 100644
--- a/src/tangara/audio/audio_events.hpp
+++ b/src/tangara/audio/audio_events.hpp
@@ -150,6 +150,10 @@ struct TtsPlaybackChanged : tinyfsm::Event {
bool is_playing;
};
+struct UnmountReady : tinyfsm::Event {
+ bool idle;
+};
+
namespace internal {
struct DecodingStarted : tinyfsm::Event {
std::shared_ptr<TrackInfo> track;
diff --git a/src/tangara/audio/audio_fsm.cpp b/src/tangara/audio/audio_fsm.cpp
index 7854818d..27c29eee 100644
--- a/src/tangara/audio/audio_fsm.cpp
+++ b/src/tangara/audio/audio_fsm.cpp
@@ -534,10 +534,7 @@ auto Standby::entry() -> void {
updateOutputMode();
}
-void Standby::react(const system_fsm::KeyLockChanged& ev) {
- if (!ev.locking) {
- return;
- }
+void Standby::react(const system_fsm::UnmountRequest& ev) {
auto current = sStreamCues.current();
sServices->bg_worker().Dispatch<void>([=]() {
auto db = sServices->database().lock();
@@ -548,6 +545,7 @@ void Standby::react(const system_fsm::KeyLockChanged& ev) {
if (queue.totalSize() <= queue.currentPosition()) {
// Nothing is playing, so don't bother saving the queue.
db->put(kQueueKey, "");
+ events::System().Dispatch(UnmountReady{.idle = ev.idle});
return;
}
db->put(kQueueKey, queue.serialise());
@@ -562,6 +560,7 @@ void Standby::react(const system_fsm::KeyLockChanged& ev) {
};
db->put(kCurrentFileKey, current_track.toString());
}
+ events::System().Dispatch(UnmountReady{.idle = ev.idle});
});
}
diff --git a/src/tangara/audio/audio_fsm.hpp b/src/tangara/audio/audio_fsm.hpp
index fb42e387..c30012d9 100644
--- a/src/tangara/audio/audio_fsm.hpp
+++ b/src/tangara/audio/audio_fsm.hpp
@@ -65,7 +65,7 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
void react(const OutputModeChanged&);
virtual void react(const system_fsm::BootComplete&) {}
- virtual void react(const system_fsm::KeyLockChanged&){};
+ virtual void react(const system_fsm::UnmountRequest&) {}
virtual void react(const system_fsm::SdStateChanged&) {}
virtual void react(const system_fsm::BluetoothEvent&);
virtual void react(const system_fsm::HasPhonesChanged&);
@@ -114,7 +114,7 @@ class Uninitialised : public AudioState {
class Standby : public AudioState {
public:
void entry() override;
- void react(const system_fsm::KeyLockChanged&) override;
+ void react(const system_fsm::UnmountRequest&) override;
void react(const system_fsm::SdStateChanged&) override;
using AudioState::react;
diff --git a/src/tangara/system_fsm/running.cpp b/src/tangara/system_fsm/running.cpp
index 13f58ef9..87b7f1ac 100644
--- a/src/tangara/system_fsm/running.cpp
+++ b/src/tangara/system_fsm/running.cpp
@@ -59,9 +59,22 @@ void Running::react(const database::event::UpdateFinished&) {
checkIdle();
}
+void Running::react(const ui::UnmountRequest& ev) {
+ events::Audio().Dispatch(audio::TogglePlayPause{.set_to = false});
+ events::Audio().Dispatch(UnmountRequest{.idle = false});
+}
+
+void Running::react(const audio::UnmountReady& ev) {
+ if (ev.idle) {
+ transit<Idle>();
+ } else {
+ unmountStorage();
+ }
+}
+
void Running::react(const internal::UnmountTimeout&) {
if (IdleCondition()) {
- transit<Idle>();
+ events::Audio().Dispatch(UnmountRequest{.idle = true});
}
}
diff --git a/src/tangara/system_fsm/system_events.hpp b/src/tangara/system_fsm/system_events.hpp
index c2e3a2ab..b96846e0 100644
--- a/src/tangara/system_fsm/system_events.hpp
+++ b/src/tangara/system_fsm/system_events.hpp
@@ -40,6 +40,9 @@ struct FatalError : tinyfsm::Event {};
struct OnIdle : tinyfsm::Event {};
struct SdStateChanged : tinyfsm::Event {};
+struct UnmountRequest : tinyfsm::Event {
+ bool idle;
+};
struct StorageError : tinyfsm::Event {
FRESULT error;
diff --git a/src/tangara/system_fsm/system_fsm.hpp b/src/tangara/system_fsm/system_fsm.hpp
index 40009781..398a0b9b 100644
--- a/src/tangara/system_fsm/system_fsm.hpp
+++ b/src/tangara/system_fsm/system_fsm.hpp
@@ -27,6 +27,7 @@
#include "drivers/touchwheel.hpp"
#include "system_fsm/service_locator.hpp"
#include "system_fsm/system_events.hpp"
+#include "ui/ui_fsm.hpp"
#include "tinyfsm.hpp"
namespace system_fsm {
@@ -64,6 +65,8 @@ class SystemState : public tinyfsm::Fsm<SystemState> {
virtual void react(const internal::IdleTimeout&) {}
virtual void react(const internal::UnmountTimeout&) {}
virtual void react(const internal::Mount&) {}
+ virtual void react(const ui::UnmountRequest&) {}
+ virtual void react(const audio::UnmountReady&) {}
protected:
auto IdleCondition() -> bool;
@@ -103,6 +106,8 @@ class Running : public SystemState {
void react(const database::event::UpdateFinished&) override;
void react(const SamdUsbMscChanged&) override;
void react(const StorageError&) override;
+ void react(const ui::UnmountRequest&) override;
+ void react(const audio::UnmountReady&) override;
void react(const internal::UnmountTimeout&) override;
void react(const internal::Mount&) override;
diff --git a/src/tangara/ui/ui_fsm.cpp b/src/tangara/ui/ui_fsm.cpp
index a22c704b..ebc4b635 100644
--- a/src/tangara/ui/ui_fsm.cpp
+++ b/src/tangara/ui/ui_fsm.cpp
@@ -714,6 +714,11 @@ void Lua::entry() {
});
registry.AddPropertyModule("sd_card", {
{"mounted", &sSdMounted},
+ {"unmount", [&](lua_State*) {
+ events::System().Dispatch(
+ UnmountRequest{});
+ return 0;
+ }},
});
registry.AddPropertyModule("usb",
{
diff --git a/src/tangara/ui/ui_fsm.hpp b/src/tangara/ui/ui_fsm.hpp
index aae69f94..d4354bec 100644
--- a/src/tangara/ui/ui_fsm.hpp
+++ b/src/tangara/ui/ui_fsm.hpp
@@ -37,6 +37,8 @@
namespace ui {
+struct UnmountRequest : tinyfsm::Event {};
+
class UiState : public tinyfsm::Fsm<UiState> {
public:
static auto InitBootSplash(drivers::IGpios&, drivers::NvsStorage&) -> bool;