Merge branch 'feat-sdcard-info' of https://github.com/jpirnay/crosspoint-reader into mybuild
This commit is contained in:
+2
-18
@@ -12,9 +12,6 @@
|
|||||||
|
|
||||||
HalStorage HalStorage::instance;
|
HalStorage HalStorage::instance;
|
||||||
|
|
||||||
// Local SdFat instance for volume stats only
|
|
||||||
static SdFat halStorageSdFat;
|
|
||||||
|
|
||||||
HalStorage::HalStorage() {
|
HalStorage::HalStorage() {
|
||||||
storageMutex = xSemaphoreCreateMutex();
|
storageMutex = xSemaphoreCreateMutex();
|
||||||
assert(storageMutex != nullptr);
|
assert(storageMutex != nullptr);
|
||||||
@@ -60,25 +57,12 @@ bool HalStorage::ensureDirectoryExists(const char* path) { HAL_STORAGE_WRAPPED_C
|
|||||||
|
|
||||||
uint64_t HalStorage::sdTotalBytes() const {
|
uint64_t HalStorage::sdTotalBytes() const {
|
||||||
StorageLock lock;
|
StorageLock lock;
|
||||||
// Try to initialize if not already
|
return SDCard.sdTotalBytes();
|
||||||
if (!halStorageSdFat.begin()) return 0;
|
|
||||||
auto* vol = halStorageSdFat.vol();
|
|
||||||
if (!vol) return 0;
|
|
||||||
return (uint64_t)vol->clusterCount() * vol->bytesPerCluster();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t HalStorage::sdUsedBytes() const {
|
uint64_t HalStorage::sdUsedBytes() const {
|
||||||
StorageLock lock;
|
StorageLock lock;
|
||||||
if (!halStorageSdFat.begin()) return 0;
|
return SDCard.sdUsedBytes();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t HalStorage::sdFreeBytes() const {
|
uint64_t HalStorage::sdFreeBytes() const {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include "ButtonRemapActivity.h"
|
#include "ButtonRemapActivity.h"
|
||||||
#include "CalibreSettingsActivity.h"
|
#include "CalibreSettingsActivity.h"
|
||||||
#include "ClearCacheActivity.h"
|
#include "ClearCacheActivity.h"
|
||||||
#include "SystemInformationActivity.h"
|
|
||||||
#include "CrossPointSettings.h"
|
#include "CrossPointSettings.h"
|
||||||
#include "KOReaderSettingsActivity.h"
|
#include "KOReaderSettingsActivity.h"
|
||||||
#include "LanguageSelectActivity.h"
|
#include "LanguageSelectActivity.h"
|
||||||
@@ -14,6 +13,7 @@
|
|||||||
#include "OtaUpdateActivity.h"
|
#include "OtaUpdateActivity.h"
|
||||||
#include "SettingsList.h"
|
#include "SettingsList.h"
|
||||||
#include "StatusBarSettingsActivity.h"
|
#include "StatusBarSettingsActivity.h"
|
||||||
|
#include "SystemInformationActivity.h"
|
||||||
#include "activities/network/WifiSelectionActivity.h"
|
#include "activities/network/WifiSelectionActivity.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ static std::string formatBytes(uint64_t bytes) {
|
|||||||
|
|
||||||
void SystemInformationActivity::onEnter() {
|
void SystemInformationActivity::onEnter() {
|
||||||
Activity::onEnter();
|
Activity::onEnter();
|
||||||
|
status_.reset();
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,6 +33,13 @@ void SystemInformationActivity::onExit() { Activity::onExit(); }
|
|||||||
void SystemInformationActivity::loop() {
|
void SystemInformationActivity::loop() {
|
||||||
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
||||||
finish();
|
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),
|
GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_SYSTEM_INFO),
|
||||||
CROSSPOINT_VERSION);
|
CROSSPOINT_VERSION);
|
||||||
|
|
||||||
const auto status = SystemStatus::collect();
|
|
||||||
|
|
||||||
// Layout: label on the left, value right of the midpoint
|
// Layout: label on the left, value right of the midpoint
|
||||||
const int leftX = metrics.verticalSpacing * 3;
|
const int leftX = metrics.verticalSpacing * 3;
|
||||||
const int valueX = pageWidth / 2;
|
const int valueX = pageWidth / 2;
|
||||||
@@ -59,6 +65,16 @@ void SystemInformationActivity::render(RenderLock&&) {
|
|||||||
renderer.drawText(UI_10_FONT_ID, valueX, y, value.c_str());
|
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
|
// Device
|
||||||
drawRow(0, "Version", status.version);
|
drawRow(0, "Version", status.version);
|
||||||
drawRow(1, "Free heap", std::to_string(status.freeHeapBytes / 1024) + " KB");
|
drawRow(1, "Free heap", std::to_string(status.freeHeapBytes / 1024) + " KB");
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "SystemStatus.h"
|
||||||
#include "activities/Activity.h"
|
#include "activities/Activity.h"
|
||||||
|
|
||||||
class SystemInformationActivity final : public Activity {
|
class SystemInformationActivity final : public Activity {
|
||||||
@@ -11,4 +14,7 @@ class SystemInformationActivity final : public Activity {
|
|||||||
void onExit() override;
|
void onExit() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
void render(RenderLock&&) override;
|
void render(RenderLock&&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<SystemStatus> status_;
|
||||||
};
|
};
|
||||||
|
|||||||
+155
-138
@@ -1,148 +1,164 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta charset="UTF-8" />
|
||||||
<title>CrossPoint Reader</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<style>
|
<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 {
|
:root {
|
||||||
--font-color: #333;
|
--font-color: #f5f5f5;
|
||||||
--bg: #f5f5f5;
|
--bg: #333;
|
||||||
--title-color: #2c3e50;
|
--title-color: #ecf0f1;
|
||||||
--card-bg: #FFF;
|
--card-bg: #444;
|
||||||
--label-color: #7f8c8d;
|
--label-color: #bdc3c7;
|
||||||
--border-color: #eee;
|
--border-color: #555;
|
||||||
--accent-color: rgb(110, 154, 130);
|
color-scheme: dark;
|
||||||
--accent-hover-color: #5a8c73;
|
|
||||||
}
|
}
|
||||||
@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">
|
body {
|
||||||
<a href="/" class="active">Home</a>
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||||
<a href="/files">File Manager</a>
|
Oxygen, Ubuntu, sans-serif;
|
||||||
<a href="/settings">Settings</a>
|
max-width: 800px;
|
||||||
</div>
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--font-color);
|
||||||
|
}
|
||||||
|
|
||||||
<div class="card">
|
h1 {
|
||||||
<h2>Device Status</h2>
|
color: var(--title-color);
|
||||||
<div class="info-row">
|
border-bottom: 2px solid var(--accent-color);
|
||||||
<span class="label">Version</span>
|
padding-bottom: 10px;
|
||||||
<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>
|
|
||||||
|
|
||||||
<div class="card">
|
h2 {
|
||||||
<p style="text-align: center; color: #95a5a6; margin: 0">
|
color: var(--title-color);
|
||||||
CrossPoint E-Reader • Open Source
|
margin-top: 0;
|
||||||
</p>
|
}
|
||||||
|
|
||||||
|
.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>
|
||||||
|
<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>
|
<script>
|
||||||
function formatBytes(bytes) {
|
function formatBytes(bytes) {
|
||||||
if (bytes == null) return 'N/A';
|
if (bytes == null) return 'N/A';
|
||||||
const units = ['B', 'KB', 'MB', 'GB'];
|
const units = ['B', 'KB', 'MB', 'GB'];
|
||||||
let val = bytes, i = 0;
|
let val = bytes, i = 0;
|
||||||
while (val >= 1024 && i < units.length - 1) { val /= 1024; i++; }
|
while (val >= 1024 && i < units.length - 1) { val /= 1024; i++; }
|
||||||
return parseFloat(val.toFixed(2)).toLocaleString() + ' ' + units[i];
|
return parseFloat(val.toFixed(2)).toLocaleString() + ' ' + units[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchStatus() {
|
async function fetchStatus() {
|
||||||
try {
|
try {
|
||||||
@@ -167,5 +183,6 @@
|
|||||||
// Fetch status on page load
|
// Fetch status on page load
|
||||||
window.onload = fetchStatus;
|
window.onload = fetchStatus;
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user