Something went wrong. Try again.
A fork of https://github.com/crosspoint-reader/crosspoint-reader
Something went wrong. Try again.
6.6 kB · 192 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193#include "UsbDriveActivity.h"
#include <Arduino.h>#include <HalStorage.h>#include <I18n.h>
#include "MappedInputManager.h"#include "SilentRestart.h"#include "components/UITheme.h"
namespace fui = freeink::ui;
void UsbDriveActivity::onEnter() { Activity::onEnter(); resetUi(); app.setScreen(&UsbDriveActivity::driveScreen, this);
// Show the safety instructions before giving the raw SD card to the USB host. requestUpdateAndWait(); if (!Storage.beginUsbDrive()) { LOG_ERR("USB", "Unable to start USB Drive"); preparing = false; startFailed = true; state = State::IoError; startFailureStartedAt = millis(); requestUpdate(); return; }
preparing = false; state = State::WaitingForHost; hostWaitStartedAt = millis(); requestUpdate();}
void UsbDriveActivity::onExit() { if (!restartRequested) Storage.endUsbDrive(); Activity::onExit();}
void UsbDriveActivity::loop() { if (!startFailed) { const State nextState = Storage.usbDriveState(); if (nextState != state) { state = nextState; requestUpdate(); } }
// A battery-powered USB device may stay mounted after cable removal. End // sustained suspend sessions too; host sleep intentionally has the same policy. if (state == State::Connected && Storage.usbDriveHostSuspended()) { if (!hostSuspendPending) { hostSuspendPending = true; hostSuspendStartedAt = millis(); } else if (millis() - hostSuspendStartedAt >= HOST_SUSPEND_TIMEOUT_MS) { LOG_INF("USB", "USB Drive host suspend timed out; ending session"); // endUsbDrive() soft-disconnects before teardown and reboot. restartToHome(); return; } } else { hostSuspendPending = false; }
if (state == State::WaitingForHost && millis() - hostWaitStartedAt >= HOST_WAIT_TIMEOUT_MS) { LOG_INF("USB", "USB Drive host wait timed out"); restartToHome(); return; }
if (startFailed && millis() - startFailureStartedAt >= START_FAILURE_TIMEOUT_MS) { LOG_INF("USB", "USB Drive startup failure timed out"); restartToHome(); return; }
if (!startFailed && state == State::IoError) { if (!forcedDisconnectRequested) { forcedDisconnectRequested = true; forcedDisconnectRequestedAt = millis(); LOG_ERR("USB", "USB Drive I/O error; disconnecting host"); if (!Storage.disconnectUsbDriveHost()) { LOG_ERR("USB", "Unable to request USB Drive host disconnect"); } } else if (millis() - forcedDisconnectRequestedAt >= FORCED_DISCONNECT_TIMEOUT_MS) { LOG_ERR("USB", "USB Drive host disconnect timed out; forcing restart"); restartToHome(); } return; }
const bool canExitWithInput = state == State::WaitingForHost || startFailed; if (canExitWithInput && (mappedInput.wasPressed(MappedInputManager::Button::Back) || mappedInput.wasPressed(MappedInputManager::Button::Power) || mappedInput.wasHomeGesture())) { restartToHome(); return; }
if (state == State::Ejected || state == State::Disconnected || state == State::Unsupported) { restartToHome(); }}
void UsbDriveActivity::render(RenderLock&&) { renderer.clearScreen();
const auto& metrics = UITheme::getInstance().getMetrics(); const int pageWidth = renderer.getScreenWidth(); GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_USB_DRIVE));
renderUi();
if (state == State::WaitingForHost || startFailed) { const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); } renderer.displayBuffer();}
void UsbDriveActivity::driveScreen(UiScreen& screen, void* user) { static_cast<UsbDriveActivity*>(user)->buildDriveScreen(screen);}
void UsbDriveActivity::buildDriveScreen(UiScreen& screen) const { const char* message = nullptr; const char* detail = nullptr; const char* secondaryDetail = nullptr; if (preparing) { message = tr(STR_USB_DRIVE_PREPARING); detail = tr(STR_USB_DRIVE_EJECT_HINT); } else { switch (state) { case State::WaitingForHost: message = tr(STR_USB_DRIVE_WAITING); break; case State::Connected: message = tr(STR_USB_DRIVE_CONNECTED); detail = tr(STR_USB_DRIVE_CONNECT_DELAY); secondaryDetail = tr(STR_USB_DRIVE_EJECT_HINT); break; case State::IoError: message = startFailed ? tr(STR_USB_DRIVE_START_ERROR) : tr(STR_USB_DRIVE_ERROR); break; case State::Ejected: case State::Disconnected: case State::Unsupported: return; } }
const auto& metrics = UITheme::getInstance().getMetrics(); screen.setContentMarginFromScreen(fui::Insets{ static_cast<int16_t>(metrics.topPadding + metrics.headerHeight), static_cast<int16_t>(metrics.contentSidePadding), static_cast<int16_t>(metrics.buttonHintsHeight), static_cast<int16_t>(metrics.contentSidePadding)});
auto messageStyle = screen.theme().smallText; messageStyle.align = fui::TextAlign::Center; messageStyle.bold = true; messageStyle.maxLines = 2; auto detailStyle = screen.theme().smallText; detailStyle.align = fui::TextAlign::Center; detailStyle.maxLines = 3;
const fui::Rect body = screen.body(); const int16_t messageHeight = fui::measureWrappedText(screen.target(), message, messageStyle, body.width).height; const int16_t detailHeight = detail ? fui::measureWrappedText(screen.target(), detail, detailStyle, body.width).height : 0; const int16_t secondaryDetailHeight = secondaryDetail ? fui::measureWrappedText(screen.target(), secondaryDetail, detailStyle, body.width).height : 0; const int16_t gap = detail ? screen.theme().spaceMd : 0; const int16_t secondaryGap = secondaryDetail ? screen.theme().spaceMd : 0; const int16_t totalHeight = static_cast<int16_t>(messageHeight + gap + detailHeight + secondaryGap + secondaryDetailHeight); if (body.height > totalHeight) screen.spacer(static_cast<int16_t>((body.height - totalHeight) / 2));
screen.target().text(screen.takeTop(messageHeight, gap), message, messageStyle); if (detail) { screen.target().text(screen.takeTop(detailHeight, secondaryGap), detail, detailStyle); } if (secondaryDetail) { screen.target().text(screen.takeTop(secondaryDetailHeight), secondaryDetail, detailStyle); }}
void UsbDriveActivity::restartToHome() { if (restartRequested) return; restartRequested = true; Storage.endUsbDrive(); delay(20); restartToHomeAfterStorageHandoff();}