more fixes

This commit is contained in:
2025-10-21 00:54:43 +02:00
parent 12e8a0e098
commit 678158c302
8 changed files with 139 additions and 15 deletions

View File

@@ -409,6 +409,18 @@ final class TimeSyncManager: NSObject, ObservableObject, UNUserNotificationCente
private func startScanning() {
guard shouldKeepScanning, central.state == .poweredOn else { return }
if isScanning { return }
let connected = central.retrieveConnectedPeripherals(withServices: [timeServiceUUID, fileServiceUUID])
if let restored = connected.first {
statusMessage = "Restoring connection…"
connectionState = .connecting
targetPeripheral = restored
restored.delegate = self
central.connect(restored, options: nil)
shouldKeepScanning = false
return
}
central.scanForPeripherals(withServices: [timeServiceUUID, fileServiceUUID], options: [
CBCentralManagerScanOptionAllowDuplicatesKey: false
])

View File

@@ -141,6 +141,14 @@ public:
capLengths(notification);
std::lock_guard<std::mutex> lock(mutex);
if (notification.externalId != 0) {
for (auto it = entries.begin(); it != entries.end();) {
if (it->externalId == notification.externalId)
it = entries.erase(it);
else
++it;
}
}
notification.id = nextId++;
notification.unread = true;
@@ -187,6 +195,23 @@ public:
++revisionCounter;
}
void removeByExternalId(std::uint64_t externalId) override {
if (externalId == 0)
return;
std::lock_guard<std::mutex> lock(mutex);
bool removed = false;
for (auto it = entries.begin(); it != entries.end();) {
if (it->externalId == externalId) {
it = entries.erase(it);
removed = true;
} else {
++it;
}
}
if (removed)
++revisionCounter;
}
private:
static constexpr std::size_t kMaxEntries = 8;
static constexpr std::size_t kMaxTitleBytes = 96;

View File

@@ -175,6 +175,15 @@ PendingNotification& ensurePending(uint32_t uid) {
return pending;
}
void discardPending(uint32_t uid) {
for (auto it = g_pendingNotifications.begin(); it != g_pendingNotifications.end(); ++it) {
if (it->uid == uid) {
g_pendingNotifications.erase(it);
break;
}
}
}
void finalizePending(uint32_t uid) {
if (!g_notificationCenter)
return;
@@ -184,6 +193,7 @@ void finalizePending(uint32_t uid) {
cardboy::sdk::INotificationCenter::Notification note{};
note.timestamp = static_cast<std::uint64_t>(time(nullptr));
note.externalId = uid;
if (!it->title.empty()) {
note.title = it->title;
} else if (!it->appIdentifier.empty()) {
@@ -975,7 +985,9 @@ void handleAncsNotificationSource(uint16_t connHandle, const uint8_t* data, uint
uid);
if (eventId == 2) { // Removed
finalizePending(uid);
discardPending(uid);
g_notificationCenter->removeByExternalId(uid);
ESP_LOGI(kLogTag, "Cleared notification uid=%" PRIu32, uid);
return;
}

View File

@@ -5,6 +5,7 @@
#include "cardboy/sdk/app_framework.hpp"
#include "cardboy/sdk/app_system.hpp"
#include <array>
#include <algorithm>
#include <cstdio>
#include <ctime>
@@ -24,6 +25,20 @@ constexpr std::uint32_t kUnlockHoldMs = 1500;
using Framebuffer = typename AppContext::Framebuffer;
using Clock = typename AppContext::Clock;
constexpr std::array<std::uint8_t, font16x8::kGlyphHeight> kArrowUpGlyph{0b00011000, 0b00111100, 0b01111110,
0b11111111, 0b00011000, 0b00011000,
0b00011000, 0b00011000, 0b00011000,
0b00011000, 0b00011000, 0b00011000,
0b00011000, 0b00011000, 0b00000000,
0b00000000};
constexpr std::array<std::uint8_t, font16x8::kGlyphHeight> kArrowDownGlyph{0b00000000, 0b00000000, 0b00011000,
0b00011000, 0b00011000, 0b00011000,
0b00011000, 0b00011000, 0b00011000,
0b00011000, 0b00011000, 0b11111111,
0b01111110, 0b00111100, 0b00011000,
0b00000000};
struct TimeSnapshot {
bool hasWallTime = false;
int hour24 = 0;
@@ -264,6 +279,30 @@ private:
}
}
static void drawArrowGlyph(Framebuffer& fb, int x, int y,
const std::array<std::uint8_t, font16x8::kGlyphHeight>& glyph, int scale = 1) {
if (scale <= 0)
return;
for (int row = 0; row < font16x8::kGlyphHeight; ++row) {
const std::uint8_t rowBits = glyph[row];
for (int col = 0; col < font16x8::kGlyphWidth; ++col) {
const auto mask = static_cast<std::uint8_t>(1u << (font16x8::kGlyphWidth - 1 - col));
if ((rowBits & mask) == 0)
continue;
for (int sx = 0; sx < scale; ++sx) {
for (int sy = 0; sy < scale; ++sy) {
fb.drawPixel(x + col * scale + sx, y + row * scale + sy, true);
}
}
}
}
}
static void drawArrow(Framebuffer& fb, int x, int y, bool up, int scale = 1) {
const auto& glyph = up ? kArrowUpGlyph : kArrowDownGlyph;
drawArrowGlyph(fb, x, y, glyph, scale);
}
static std::string truncateWithEllipsis(std::string_view text, int maxWidth, int scale, int letterSpacing) {
if (font16x8::measureText(text, scale, letterSpacing) <= maxWidth)
return std::string(text);
@@ -423,11 +462,15 @@ private:
const int counterWidth = font16x8::measureText(counter, scaleSmall, 1);
const int counterX = cardMarginSide + cardWidth - cardPadding - counterWidth;
font16x8::drawText(framebuffer, counterX, cardMarginTop + cardPadding, counter, scaleSmall, true, 1);
const int arrowX = counterX + (counterWidth - 8) / 2;
int arrowY = cardMarginTop + cardPadding + textLineHeight + 1;
font16x8::drawText(framebuffer, arrowX, arrowY, "^", scaleSmall, true, 0);
arrowY += textLineHeight + 2;
font16x8::drawText(framebuffer, arrowX, arrowY, "v", scaleSmall, true, 0);
const int arrowWidth = font16x8::kGlyphWidth * scaleSmall;
const int arrowSpacing = std::max(1, scaleSmall);
const int arrowsTotalWide = arrowWidth * 2 + arrowSpacing;
const int arrowX = counterX + (counterWidth - arrowsTotalWide) / 2;
const int arrowY = cardMarginTop + cardPadding + textLineHeight + 1;
drawArrow(framebuffer, arrowX, arrowY, true, scaleSmall);
drawArrow(framebuffer, arrowX + arrowWidth + arrowSpacing, arrowY, false, scaleSmall);
const int arrowHeight = font16x8::kGlyphHeight * scaleSmall;
cardHeight = std::max(cardHeight, arrowY + arrowHeight - cardMarginTop);
}
if (!bodyLines.empty()) {
@@ -450,12 +493,15 @@ private:
font16x8::drawText(framebuffer, summaryX, summaryY, summary, scaleSmall, true, 1);
if (notifications.size() > 1) {
int arrowX = (framebuffer.width() - 8) / 2;
int arrowY = summaryY + textLineHeight + 1;
font16x8::drawText(framebuffer, arrowX, arrowY, "^", scaleSmall, true, 0);
arrowY += textLineHeight + 2;
font16x8::drawText(framebuffer, arrowX, arrowY, "v", scaleSmall, true, 0);
cardHeight = std::max(cardHeight, arrowY + textLineHeight - cardMarginTop);
const int arrowWidth = font16x8::kGlyphWidth * scaleSmall;
const int arrowSpacing = std::max(1, scaleSmall);
const int arrowsTotalWide = arrowWidth * 2 + arrowSpacing;
const int arrowX = (framebuffer.width() - arrowsTotalWide) / 2;
const int arrowY = summaryY + textLineHeight + 1;
drawArrow(framebuffer, arrowX, arrowY, true, scaleSmall);
drawArrow(framebuffer, arrowX + arrowWidth + arrowSpacing, arrowY, false, scaleSmall);
const int arrowHeight = font16x8::kGlyphHeight * scaleSmall;
cardHeight = std::max(cardHeight, arrowY + arrowHeight - cardMarginTop);
}
}
}

View File

@@ -75,6 +75,7 @@ public:
struct Notification {
std::uint64_t id = 0;
std::uint64_t timestamp = 0;
std::uint64_t externalId = 0;
std::string title;
std::string body;
bool unread = true;
@@ -87,6 +88,7 @@ public:
[[nodiscard]] virtual std::vector<Notification> recent(std::size_t limit) const = 0;
virtual void markAllRead() = 0;
virtual void clear() = 0;
virtual void removeByExternalId(std::uint64_t externalId) = 0;
};
struct Services {

View File

@@ -93,6 +93,7 @@ public:
[[nodiscard]] std::vector<Notification> recent(std::size_t limit) const override;
void markAllRead() override;
void clear() override;
void removeByExternalId(std::uint64_t externalId) override;
private:
static constexpr std::size_t kMaxEntries = 8;

View File

@@ -157,12 +157,21 @@ void DesktopNotificationCenter::pushNotification(Notification notification) {
}
std::lock_guard<std::mutex> lock(mutex);
if (notification.externalId != 0) {
for (auto it = entries.begin(); it != entries.end();) {
if (it->externalId == notification.externalId)
it = entries.erase(it);
else
++it;
}
}
notification.id = nextId++;
notification.unread = true;
if (entries.size() >= kMaxEntries)
entries.erase(entries.begin());
entries.push_back(std::move(notification));
while (entries.size() > kMaxEntries)
entries.erase(entries.begin());
++revisionCounter;
}
@@ -203,6 +212,23 @@ void DesktopNotificationCenter::clear() {
++revisionCounter;
}
void DesktopNotificationCenter::removeByExternalId(std::uint64_t externalId) {
if (externalId == 0)
return;
std::lock_guard<std::mutex> lock(mutex);
bool removed = false;
for (auto it = entries.begin(); it != entries.end();) {
if (it->externalId == externalId) {
it = entries.erase(it);
removed = true;
} else {
++it;
}
}
if (removed)
++revisionCounter;
}
DesktopFramebuffer::DesktopFramebuffer(DesktopRuntime& runtime) : runtime(runtime) {}
int DesktopFramebuffer::width_impl() const { return cardboy::sdk::kDisplayWidth; }

View File

@@ -695,7 +695,7 @@ CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y
CONFIG_BT_NIMBLE_ROLE_OBSERVER=y
CONFIG_BT_NIMBLE_GATT_CLIENT=y
CONFIG_BT_NIMBLE_GATT_SERVER=y
# CONFIG_BT_NIMBLE_NVS_PERSIST is not set
CONFIG_BT_NIMBLE_NVS_PERSIST=y
# CONFIG_BT_NIMBLE_SMP_ID_RESET is not set
CONFIG_BT_NIMBLE_SECURITY_ENABLE=y
CONFIG_BT_NIMBLE_SM_LEGACY=y