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
custom
cooljqln 1 month ago
commit fe38a57120
  1. 1
      lua/images.lua
  2. BIN
      lua/img/unmount.png
  3. 21
      lua/main_menu.lua
  4. 77
      lua/settings.lua
  5. 4
      src/tangara/audio/audio_events.hpp
  6. 7
      src/tangara/audio/audio_fsm.cpp
  7. 4
      src/tangara/audio/audio_fsm.hpp
  8. 15
      src/tangara/system_fsm/running.cpp
  9. 3
      src/tangara/system_fsm/system_events.hpp
  10. 5
      src/tangara/system_fsm/system_fsm.hpp
  11. 5
      src/tangara/ui/ui_fsm.cpp
  12. 2
      src/tangara/ui/ui_fsm.hpp

@ -29,6 +29,7 @@ local img = {
unlistened = lvgl.ImgData("//lua/img/unlistened.png"), unlistened = lvgl.ImgData("//lua/img/unlistened.png"),
info = lvgl.ImgData("//lua/img/info.png"), info = lvgl.ImgData("//lua/img/info.png"),
menu = lvgl.ImgData("//lua/img/menu.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_directory = lvgl.ImgData("//lua/img/file_icons/directory.png"),
file_playlist = lvgl.ImgData("//lua/img/file_icons/playlist.png"), file_playlist = lvgl.ImgData("//lua/img/file_icons/playlist.png"),
file_music = lvgl.ImgData("//lua/img/file_icons/music.png"), file_music = lvgl.ImgData("//lua/img/file_icons/music.png"),

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

@ -15,6 +15,7 @@ local font = require("font")
local theme = require("theme") local theme = require("theme")
local img = require("images") local img = require("images")
local playback = require("playback") local playback = require("playback")
local usb = require("usb")
return widgets.MenuScreen:new { return widgets.MenuScreen:new {
create_ui = function(self) create_ui = function(self)
@ -238,6 +239,26 @@ return widgets.MenuScreen:new {
end) 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 {} local files_btn = bottom_bar:Button {}
files_btn:onClicked(function() files_btn:onClicked(function()
backstack.push(require("file_browser"):new { backstack.push(require("file_browser"):new {

@ -18,6 +18,7 @@ local font = require("font")
local main_menu = require("main_menu") local main_menu = require("main_menu")
local img = require("images") local img = require("images")
local nvs = require("nvs") local nvs = require("nvs")
local sd_card = require("sd_card")
local settings = {} local settings = {}
@ -512,6 +513,77 @@ settings.InputSettings = SettingsScreen:new {
end 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 { settings.MassStorageSettings = SettingsScreen:new {
title = "USB Storage", title = "USB Storage",
create_ui = function(self) create_ui = function(self)
@ -944,8 +1016,9 @@ settings.Root = widgets.MenuScreen:new {
submenu("Theme", settings.ThemeSettings) submenu("Theme", settings.ThemeSettings)
submenu("Input Method", settings.InputSettings) submenu("Input Method", settings.InputSettings)
section("USB") section("Storage")
submenu("Storage", settings.MassStorageSettings) submenu("SD Card", settings.SDSettings)
submenu("USB", settings.MassStorageSettings)
section("System") section("System")
submenu("Database", settings.DatabaseSettings) submenu("Database", settings.DatabaseSettings)

@ -150,6 +150,10 @@ struct TtsPlaybackChanged : tinyfsm::Event {
bool is_playing; bool is_playing;
}; };
struct UnmountReady : tinyfsm::Event {
bool idle;
};
namespace internal { namespace internal {
struct DecodingStarted : tinyfsm::Event { struct DecodingStarted : tinyfsm::Event {
std::shared_ptr<TrackInfo> track; std::shared_ptr<TrackInfo> track;

@ -534,10 +534,7 @@ auto Standby::entry() -> void {
updateOutputMode(); updateOutputMode();
} }
void Standby::react(const system_fsm::KeyLockChanged& ev) { void Standby::react(const system_fsm::UnmountRequest& ev) {
if (!ev.locking) {
return;
}
auto current = sStreamCues.current(); auto current = sStreamCues.current();
sServices->bg_worker().Dispatch<void>([=]() { sServices->bg_worker().Dispatch<void>([=]() {
auto db = sServices->database().lock(); auto db = sServices->database().lock();
@ -548,6 +545,7 @@ void Standby::react(const system_fsm::KeyLockChanged& ev) {
if (queue.totalSize() <= queue.currentPosition()) { if (queue.totalSize() <= queue.currentPosition()) {
// Nothing is playing, so don't bother saving the queue. // Nothing is playing, so don't bother saving the queue.
db->put(kQueueKey, ""); db->put(kQueueKey, "");
events::System().Dispatch(UnmountReady{.idle = ev.idle});
return; return;
} }
db->put(kQueueKey, queue.serialise()); db->put(kQueueKey, queue.serialise());
@ -562,6 +560,7 @@ void Standby::react(const system_fsm::KeyLockChanged& ev) {
}; };
db->put(kCurrentFileKey, current_track.toString()); db->put(kCurrentFileKey, current_track.toString());
} }
events::System().Dispatch(UnmountReady{.idle = ev.idle});
}); });
} }

@ -65,7 +65,7 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
void react(const OutputModeChanged&); void react(const OutputModeChanged&);
virtual void react(const system_fsm::BootComplete&) {} 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::SdStateChanged&) {}
virtual void react(const system_fsm::BluetoothEvent&); virtual void react(const system_fsm::BluetoothEvent&);
virtual void react(const system_fsm::HasPhonesChanged&); virtual void react(const system_fsm::HasPhonesChanged&);
@ -114,7 +114,7 @@ class Uninitialised : public AudioState {
class Standby : public AudioState { class Standby : public AudioState {
public: public:
void entry() override; void entry() override;
void react(const system_fsm::KeyLockChanged&) override; void react(const system_fsm::UnmountRequest&) override;
void react(const system_fsm::SdStateChanged&) override; void react(const system_fsm::SdStateChanged&) override;
using AudioState::react; using AudioState::react;

@ -59,9 +59,22 @@ void Running::react(const database::event::UpdateFinished&) {
checkIdle(); 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&) { void Running::react(const internal::UnmountTimeout&) {
if (IdleCondition()) { if (IdleCondition()) {
transit<Idle>(); events::Audio().Dispatch(UnmountRequest{.idle = true});
} }
} }

@ -40,6 +40,9 @@ struct FatalError : tinyfsm::Event {};
struct OnIdle : tinyfsm::Event {}; struct OnIdle : tinyfsm::Event {};
struct SdStateChanged : tinyfsm::Event {}; struct SdStateChanged : tinyfsm::Event {};
struct UnmountRequest : tinyfsm::Event {
bool idle;
};
struct StorageError : tinyfsm::Event { struct StorageError : tinyfsm::Event {
FRESULT error; FRESULT error;

@ -27,6 +27,7 @@
#include "drivers/touchwheel.hpp" #include "drivers/touchwheel.hpp"
#include "system_fsm/service_locator.hpp" #include "system_fsm/service_locator.hpp"
#include "system_fsm/system_events.hpp" #include "system_fsm/system_events.hpp"
#include "ui/ui_fsm.hpp"
#include "tinyfsm.hpp" #include "tinyfsm.hpp"
namespace system_fsm { namespace system_fsm {
@ -64,6 +65,8 @@ class SystemState : public tinyfsm::Fsm<SystemState> {
virtual void react(const internal::IdleTimeout&) {} virtual void react(const internal::IdleTimeout&) {}
virtual void react(const internal::UnmountTimeout&) {} virtual void react(const internal::UnmountTimeout&) {}
virtual void react(const internal::Mount&) {} virtual void react(const internal::Mount&) {}
virtual void react(const ui::UnmountRequest&) {}
virtual void react(const audio::UnmountReady&) {}
protected: protected:
auto IdleCondition() -> bool; auto IdleCondition() -> bool;
@ -103,6 +106,8 @@ class Running : public SystemState {
void react(const database::event::UpdateFinished&) override; void react(const database::event::UpdateFinished&) override;
void react(const SamdUsbMscChanged&) override; void react(const SamdUsbMscChanged&) override;
void react(const StorageError&) 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::UnmountTimeout&) override;
void react(const internal::Mount&) override; void react(const internal::Mount&) override;

@ -714,6 +714,11 @@ void Lua::entry() {
}); });
registry.AddPropertyModule("sd_card", { registry.AddPropertyModule("sd_card", {
{"mounted", &sSdMounted}, {"mounted", &sSdMounted},
{"unmount", [&](lua_State*) {
events::System().Dispatch(
UnmountRequest{});
return 0;
}},
}); });
registry.AddPropertyModule("usb", registry.AddPropertyModule("usb",
{ {

@ -37,6 +37,8 @@
namespace ui { namespace ui {
struct UnmountRequest : tinyfsm::Event {};
class UiState : public tinyfsm::Fsm<UiState> { class UiState : public tinyfsm::Fsm<UiState> {
public: public:
static auto InitBootSplash(drivers::IGpios&, drivers::NvsStorage&) -> bool; static auto InitBootSplash(drivers::IGpios&, drivers::NvsStorage&) -> bool;

Loading…
Cancel
Save