Add Bluetooth status icon to reader status bar
Display a Bluetooth icon in the status bar when BLE is connected. Also optimize memory management by lending the framebuffer instead of immediately tearing down BLE when heap is low, allowing BLE to remain active during rendering.
This commit is contained in:
@@ -969,17 +969,15 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shed the BLE stack before rendering into a starved heap. Everything below —
|
||||
// page deserialization, glyph caching, catch-up build steps — allocates through
|
||||
// throwing paths that abort() on OOM under -fno-exceptions. Field data: with no
|
||||
// shed, a session ground to <2.2 KB free (per-glyph SD fallbacks, no AA, 4.7 s
|
||||
// pages) and then aborted on a tiny vector growth. Freeing BLE returns ~52 KB and
|
||||
// restores a large contiguous block; the lifecycle restarts it behind its heap
|
||||
// gate once the pressure passes.
|
||||
FrameBufferBuildLoan buildLoan(renderer);
|
||||
|
||||
// If BLE leaves the reader below the render floor, lend the framebuffer before
|
||||
// deserializing/loading the page instead of tearing BLE down immediately. The
|
||||
// restore path still frees BLE if the framebuffer cannot be reallocated.
|
||||
if (BleHid.isRunning() && ESP.getFreeHeap() < RENDER_MIN_FREE_HEAP) {
|
||||
LOG_ERR("ERS", "Render heap %u below floor %u; freeing BLE RAM", (unsigned)ESP.getFreeHeap(),
|
||||
LOG_INF("ERS", "Render heap %u below floor %u; lending framebuffer", (unsigned)ESP.getFreeHeap(),
|
||||
(unsigned)RENDER_MIN_FREE_HEAP);
|
||||
bleinput::stop();
|
||||
buildLoan.release();
|
||||
}
|
||||
|
||||
const auto showPendingSyncSaveError = [this]() {
|
||||
@@ -988,8 +986,6 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
||||
GUI.drawPopup(renderer, tr(STR_SAVE_PROGRESS_FAILED));
|
||||
};
|
||||
|
||||
FrameBufferBuildLoan buildLoan(renderer);
|
||||
|
||||
// A section build failure (e.g. an invalid/corrupt EPUB that fails XML parsing) leaves the
|
||||
// "Indexing" popup on screen with no way forward. Surface an explicit error instead of hanging.
|
||||
// clearScreen first so the error popup doesn't overlay the stale "Indexing" popup.
|
||||
@@ -1639,7 +1635,7 @@ void EpubReaderActivity::renderStatusBar() const {
|
||||
}
|
||||
|
||||
GUI.drawStatusBar(renderer, bookProgress, currentPage, pageCount, title, 0, textYOffset, true, currentPageBookmarked,
|
||||
section->isBuilding());
|
||||
section->isBuilding(), BleHid.isConnected());
|
||||
}
|
||||
|
||||
void EpubReaderActivity::navigateToHref(const std::string& hrefStr, const bool savePosition) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
// size: 16x16, generated from Lucide bluetooth.svg with FreeInk's icon generator.
|
||||
static const uint8_t BluetoothStatusIcon[] = {0xFF, 0xFF, 0xFE, 0x7F, 0xFE, 0x3F, 0xFE, 0x1F, 0xF6, 0x4F, 0xFA,
|
||||
0x5F, 0xFC, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0xFA, 0x5F,
|
||||
0xF6, 0x4F, 0xFE, 0x1F, 0xFE, 0x3F, 0xFE, 0x7F, 0xFF, 0xFF};
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "I18n.h"
|
||||
#include "RecentBooksStore.h"
|
||||
#include "components/icons/bluetooth.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "components/icons/bookmark.h"
|
||||
#include "fontIds.h"
|
||||
@@ -23,8 +24,10 @@ constexpr int homeMarginTop = 30;
|
||||
constexpr int subtitleY = 738;
|
||||
constexpr int bookmarkStatusIconWidth = 16;
|
||||
constexpr int bookmarkStatusIconHeight = 14;
|
||||
constexpr int bookmarkStatusIconGap = 4;
|
||||
constexpr int bookmarkStatusIconTopCrop = 2;
|
||||
constexpr int bluetoothStatusIconWidth = 16;
|
||||
constexpr int bluetoothStatusIconHeight = 16;
|
||||
constexpr int statusIconGap = 4;
|
||||
|
||||
bool statusBarTextLaneVisible() {
|
||||
return SETTINGS.statusBarChapterPageCount || SETTINGS.statusBarBookProgressPercentage ||
|
||||
@@ -43,6 +46,17 @@ void drawBookmarkStatusIcon(const GfxRenderer& renderer, const int x, const int
|
||||
}
|
||||
}
|
||||
|
||||
void drawBluetoothStatusIcon(const GfxRenderer& renderer, const int x, const int y) {
|
||||
constexpr int bytesPerRow = bluetoothStatusIconWidth / 8;
|
||||
for (int row = 0; row < bluetoothStatusIconHeight; ++row) {
|
||||
for (int col = 0; col < bluetoothStatusIconWidth; ++col) {
|
||||
const uint8_t byte = BluetoothStatusIcon[row * bytesPerRow + col / 8];
|
||||
const uint8_t mask = 1U << (7 - (col % 8));
|
||||
renderer.drawPixel(x + col, y + row, (byte & mask) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void BaseTheme::drawBatteryOutline(const GfxRenderer& renderer, int x, int y, int battWidth, int rectHeight) {
|
||||
@@ -749,7 +763,8 @@ void BaseTheme::fillPopupProgress(const GfxRenderer& renderer, const Rect& layou
|
||||
|
||||
void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, const int currentPage,
|
||||
const int pageCount, std::string title, const int paddingBottom, const int textYOffset,
|
||||
const bool fillMargin, const bool isPageBookmarked, const bool pageCountEstimated) const {
|
||||
const bool fillMargin, const bool isPageBookmarked, const bool pageCountEstimated,
|
||||
const bool bluetoothConnected) const {
|
||||
auto metrics = UITheme::getInstance().getMetrics();
|
||||
int orientedMarginTop, orientedMarginRight, orientedMarginBottom, orientedMarginLeft;
|
||||
renderer.getOrientedViewableTRBL(&orientedMarginTop, &orientedMarginRight, &orientedMarginBottom,
|
||||
@@ -845,9 +860,17 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Bookmark
|
||||
// Draw status icons
|
||||
if (showStatusBarTextLane && bluetoothConnected) {
|
||||
const int bluetoothGap = leftClusterWidth > 0 ? statusIconGap : 0;
|
||||
const int bluetoothX = leftClusterX + leftClusterWidth + bluetoothGap;
|
||||
const int bluetoothY = textY + 3;
|
||||
drawBluetoothStatusIcon(renderer, bluetoothX, bluetoothY);
|
||||
leftClusterWidth += bluetoothStatusIconWidth + bluetoothGap;
|
||||
}
|
||||
|
||||
if (showStatusBarTextLane && isPageBookmarked) {
|
||||
const int bookmarkGap = leftClusterWidth > 0 ? bookmarkStatusIconGap : 0;
|
||||
const int bookmarkGap = leftClusterWidth > 0 ? statusIconGap : 0;
|
||||
const int bookmarkX = leftClusterX + leftClusterWidth + bookmarkGap;
|
||||
const int bookmarkY = textY + 5;
|
||||
drawBookmarkStatusIcon(renderer, bookmarkX, bookmarkY);
|
||||
|
||||
@@ -238,7 +238,7 @@ class BaseTheme {
|
||||
void drawStatusBar(GfxRenderer& renderer, const float bookProgress, const int currentPage, const int pageCount,
|
||||
std::string title, const int paddingBottom = 0, const int textYOffset = 0,
|
||||
const bool fillMargin = true, const bool isPageBookmarked = false,
|
||||
const bool pageCountEstimated = false) const;
|
||||
const bool pageCountEstimated = false, const bool bluetoothConnected = false) const;
|
||||
void drawHelpText(const GfxRenderer& renderer, Rect rect, const char* label) const;
|
||||
virtual void drawTextField(const GfxRenderer& renderer, Rect rect, const int textWidth, bool cursorMode = false,
|
||||
int contentStartX = 0, int contentWidth = 0) const;
|
||||
|
||||
@@ -571,7 +571,22 @@ void loop() {
|
||||
|
||||
gpio.update();
|
||||
updateBluetoothLifecycle(); // bring BLE up/down for the current activity context
|
||||
static bool lastBleConnected = false;
|
||||
BleHid.poll(); // drive BLE auto-reconnect + key auto-repeat (no-op when BT off)
|
||||
const bool bleConnected = BleHid.isConnected();
|
||||
if (bleConnected && !lastBleConnected) {
|
||||
LOG_INF("BLELC", "connected name=%s heap=%u maxAlloc=%u", BleHid.connectedName(), ESP.getFreeHeap(),
|
||||
ESP.getMaxAllocHeap());
|
||||
if (activityManager.isReaderActivity() && !activityManager.currentKeepsBluetoothAlive()) {
|
||||
activityManager.requestUpdate();
|
||||
}
|
||||
} else if (!bleConnected && lastBleConnected) {
|
||||
LOG_INF("BLELC", "disconnected heap=%u maxAlloc=%u", ESP.getFreeHeap(), ESP.getMaxAllocHeap());
|
||||
if (activityManager.isReaderActivity() && !activityManager.currentKeepsBluetoothAlive()) {
|
||||
activityManager.requestUpdate();
|
||||
}
|
||||
}
|
||||
lastBleConnected = bleConnected;
|
||||
mappedInputManager.pollBle(); // drain BLE keys -> logical-button overlay for this frame
|
||||
halTiltSensor.update(SETTINGS.tiltPageTurn, SETTINGS.orientation, activityManager.isReaderActivity());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user