Merge branch 'feat-sdcard-info' of https://github.com/jpirnay/crosspoint-reader into mybuild

This commit is contained in:
jpirnay
2026-03-19 11:20:47 +01:00
5 changed files with 182 additions and 159 deletions
+2 -18
View File
@@ -12,9 +12,6 @@
HalStorage HalStorage::instance;
// Local SdFat instance for volume stats only
static SdFat halStorageSdFat;
HalStorage::HalStorage() {
storageMutex = xSemaphoreCreateMutex();
assert(storageMutex != nullptr);
@@ -60,25 +57,12 @@ bool HalStorage::ensureDirectoryExists(const char* path) { HAL_STORAGE_WRAPPED_C
uint64_t HalStorage::sdTotalBytes() const {
StorageLock lock;
// Try to initialize if not already
if (!halStorageSdFat.begin()) return 0;
auto* vol = halStorageSdFat.vol();
if (!vol) return 0;
return (uint64_t)vol->clusterCount() * vol->bytesPerCluster();
return SDCard.sdTotalBytes();
}
uint64_t HalStorage::sdUsedBytes() const {
StorageLock lock;
if (!halStorageSdFat.begin()) return 0;
auto* vol = halStorageSdFat.vol();
if (!vol) return 0;
const int32_t freeClusters = vol->freeClusterCount();
if (freeClusters < 0) return 0; // error reading FAT
const uint64_t clusterCount = vol->clusterCount();
uint64_t cappedFreeClusters = freeClusters < 0 ? 0 : (uint64_t)freeClusters;
if (cappedFreeClusters > clusterCount) cappedFreeClusters = clusterCount;
const uint64_t bytesPerCluster = vol->bytesPerCluster();
return (clusterCount - cappedFreeClusters) * bytesPerCluster;
return SDCard.sdUsedBytes();
}
uint64_t HalStorage::sdFreeBytes() const {
+1 -1
View File
@@ -6,7 +6,6 @@
#include "ButtonRemapActivity.h"
#include "CalibreSettingsActivity.h"
#include "ClearCacheActivity.h"
#include "SystemInformationActivity.h"
#include "CrossPointSettings.h"
#include "KOReaderSettingsActivity.h"
#include "LanguageSelectActivity.h"
@@ -14,6 +13,7 @@
#include "OtaUpdateActivity.h"
#include "SettingsList.h"
#include "StatusBarSettingsActivity.h"
#include "SystemInformationActivity.h"
#include "activities/network/WifiSelectionActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
@@ -24,6 +24,7 @@ static std::string formatBytes(uint64_t bytes) {
void SystemInformationActivity::onEnter() {
Activity::onEnter();
status_.reset();
requestUpdate();
}
@@ -32,6 +33,13 @@ void SystemInformationActivity::onExit() { Activity::onExit(); }
void SystemInformationActivity::loop() {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
finish();
return;
}
// Collect status (includes the potentially slow SD FAT walk) outside of render()
// so the screen is shown with a "Reading..." placeholder first.
if (!status_.has_value()) {
status_ = SystemStatus::collect();
requestUpdate();
}
}
@@ -45,8 +53,6 @@ void SystemInformationActivity::render(RenderLock&&) {
GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_SYSTEM_INFO),
CROSSPOINT_VERSION);
const auto status = SystemStatus::collect();
// Layout: label on the left, value right of the midpoint
const int leftX = metrics.verticalSpacing * 3;
const int valueX = pageWidth / 2;
@@ -59,6 +65,16 @@ void SystemInformationActivity::render(RenderLock&&) {
renderer.drawText(UI_10_FONT_ID, valueX, y, value.c_str());
};
if (!status_.has_value()) {
// Stats not yet collected — show a placeholder so the screen updates immediately
drawRow(0, "Version", CROSSPOINT_VERSION);
drawRow(3, "SD card", "Reading...");
renderer.displayBuffer();
return;
}
const auto& status = *status_;
// Device
drawRow(0, "Version", status.version);
drawRow(1, "Free heap", std::to_string(status.freeHeapBytes / 1024) + " KB");
@@ -1,5 +1,8 @@
#pragma once
#include <optional>
#include "SystemStatus.h"
#include "activities/Activity.h"
class SystemInformationActivity final : public Activity {
@@ -11,4 +14,7 @@ class SystemInformationActivity final : public Activity {
void onExit() override;
void loop() override;
void render(RenderLock&&) override;
private:
std::optional<SystemStatus> status_;
};
+155 -138
View File
@@ -1,148 +1,164 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CrossPoint Reader</title>
<style>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CrossPoint Reader</title>
<style>
:root {
--font-color: #333;
--bg: #f5f5f5;
--title-color: #2c3e50;
--card-bg: #FFF;
--label-color: #7f8c8d;
--border-color: #eee;
--accent-color: rgb(110, 154, 130);
--accent-hover-color: #5a8c73;
}
@media (prefers-color-scheme: dark) {
:root {
--font-color: #333;
--bg: #f5f5f5;
--title-color: #2c3e50;
--card-bg: #FFF;
--label-color: #7f8c8d;
--border-color: #eee;
--accent-color: rgb(110, 154, 130);
--accent-hover-color: #5a8c73;
--font-color: #f5f5f5;
--bg: #333;
--title-color: #ecf0f1;
--card-bg: #444;
--label-color: #bdc3c7;
--border-color: #555;
color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
:root {
--font-color: #f5f5f5;
--bg: #333;
--title-color: #ecf0f1;
--card-bg: #444;
--label-color: #bdc3c7;
--border-color: #555;
color-scheme: dark;
}
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: var(--bg);
color: var(--font-color);
}
h1 {
color: var(--title-color);
border-bottom: 2px solid var(--accent-color);
padding-bottom: 10px;
}
h2 {
color: var(--title-color);
margin-top: 0;
}
.card {
background: var(--card-bg);
border-radius: 8px;
padding: 20px;
margin: 15px 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid var(--border-color);
}
.info-row:last-child {
border-bottom: none;
}
.label {
font-weight: 600;
color: var(--label-color);
}
.value {
color: var(--title-color);
}
.status {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
background-color: #27ae60;
color: white;
font-size: 0.9em;
}
.nav-links {
margin: 20px 0;
display: flex;
gap: 10px;
}
.nav-links a {
padding: 10px 20px;
color: var(--font-color);
text-decoration: none;
border-radius: 4px;
}
.nav-links a.active {
background-color: var(--accent-color);
color: white;
}
.nav-links a:not(.active):hover {
background-color: var(--accent-hover-color);
color: white;
}
</style>
</head>
<body>
<h1>📚 CrossPoint Reader</h1>
}
<div class="nav-links">
<a href="/" class="active">Home</a>
<a href="/files">File Manager</a>
<a href="/settings">Settings</a>
</div>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: var(--bg);
color: var(--font-color);
}
<div class="card">
<h2>Device Status</h2>
<div class="info-row">
<span class="label">Version</span>
<span class="value" id="version"></span>
</div>
<div class="info-row">
<span class="label">WiFi Status</span>
<span class="status">Connected</span>
</div>
<div class="info-row">
<span class="label">IP Address</span>
<span class="value" id="ip-address"></span>
</div>
<div class="info-row">
<span class="label">Free Memory</span>
<span class="value" id="free-heap"></span>
</div>
<div class="info-row">
<span class="label">SD Card</span>
<span class="value" id="sd-space"></span>
</div>
</div>
h1 {
color: var(--title-color);
border-bottom: 2px solid var(--accent-color);
padding-bottom: 10px;
}
<div class="card">
<p style="text-align: center; color: #95a5a6; margin: 0">
CrossPoint E-Reader • Open Source
</p>
h2 {
color: var(--title-color);
margin-top: 0;
}
.card {
background: var(--card-bg);
border-radius: 8px;
padding: 20px;
margin: 15px 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid var(--border-color);
}
.info-row:last-child {
border-bottom: none;
}
.label {
font-weight: 600;
color: var(--label-color);
}
.value {
color: var(--title-color);
}
.status {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
background-color: #27ae60;
color: white;
font-size: 0.9em;
}
.nav-links {
margin: 20px 0;
display: flex;
gap: 10px;
}
.nav-links a {
padding: 10px 20px;
color: var(--font-color);
text-decoration: none;
border-radius: 4px;
}
.nav-links a.active {
background-color: var(--accent-color);
color: white;
}
.nav-links a:not(.active):hover {
background-color: var(--accent-hover-color);
color: white;
}
</style>
</head>
<body>
<h1>📚 CrossPoint Reader</h1>
<div class="nav-links">
<a href="/" class="active">Home</a>
<a href="/files">File Manager</a>
<a href="/settings">Settings</a>
</div>
<div class="card">
<h2>Device Status</h2>
<div class="info-row">
<span class="label">Version</span>
<span class="value" id="version">Loading...</span>
</div>
<div class="info-row">
<span class="label">WiFi Status</span>
<span class="status">Connected</span>
</div>
<div class="info-row">
<span class="label">IP Address</span>
<span class="value" id="ip-address">Loading...</span>
</div>
<div class="info-row">
<span class="label">Free Memory</span>
<span class="value" id="free-heap">Loading...</span>
</div>
<div class="info-row">
<span class="label">SD Card</span>
<span class="value" id="sd-space">Loading...</span>
</div>
</div>
<div class="card">
<p style="text-align: center; color: #95a5a6; margin: 0">
CrossPoint E-Reader • Open Source
</p>
</div>
<script>
function formatBytes(bytes) {
if (bytes == null) return 'N/A';
const units = ['B', 'KB', 'MB', 'GB'];
let val = bytes, i = 0;
while (val >= 1024 && i < units.length - 1) { val /= 1024; i++; }
return parseFloat(val.toFixed(2)).toLocaleString() + ' ' + units[i];
}
function formatBytes(bytes) {
if (bytes == null) return 'N/A';
const units = ['B', 'KB', 'MB', 'GB'];
let val = bytes, i = 0;
while (val >= 1024 && i < units.length - 1) { val /= 1024; i++; }
return parseFloat(val.toFixed(2)).toLocaleString() + ' ' + units[i];
}
async function fetchStatus() {
try {
@@ -167,5 +183,6 @@
// Fetch status on page load
window.onload = fetchStatus;
</script>
</body>
</html>
</body>
</html>