mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 15:17:48 +01:00
more fixes
This commit is contained in:
@@ -409,6 +409,18 @@ final class TimeSyncManager: NSObject, ObservableObject, UNUserNotificationCente
|
|||||||
private func startScanning() {
|
private func startScanning() {
|
||||||
guard shouldKeepScanning, central.state == .poweredOn else { return }
|
guard shouldKeepScanning, central.state == .poweredOn else { return }
|
||||||
if isScanning { 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: [
|
central.scanForPeripherals(withServices: [timeServiceUUID, fileServiceUUID], options: [
|
||||||
CBCentralManagerScanOptionAllowDuplicatesKey: false
|
CBCentralManagerScanOptionAllowDuplicatesKey: false
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -141,6 +141,14 @@ public:
|
|||||||
capLengths(notification);
|
capLengths(notification);
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
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.id = nextId++;
|
||||||
notification.unread = true;
|
notification.unread = true;
|
||||||
|
|
||||||
@@ -187,6 +195,23 @@ public:
|
|||||||
++revisionCounter;
|
++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:
|
private:
|
||||||
static constexpr std::size_t kMaxEntries = 8;
|
static constexpr std::size_t kMaxEntries = 8;
|
||||||
static constexpr std::size_t kMaxTitleBytes = 96;
|
static constexpr std::size_t kMaxTitleBytes = 96;
|
||||||
|
|||||||
@@ -175,6 +175,15 @@ PendingNotification& ensurePending(uint32_t uid) {
|
|||||||
return pending;
|
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) {
|
void finalizePending(uint32_t uid) {
|
||||||
if (!g_notificationCenter)
|
if (!g_notificationCenter)
|
||||||
return;
|
return;
|
||||||
@@ -184,6 +193,7 @@ void finalizePending(uint32_t uid) {
|
|||||||
|
|
||||||
cardboy::sdk::INotificationCenter::Notification note{};
|
cardboy::sdk::INotificationCenter::Notification note{};
|
||||||
note.timestamp = static_cast<std::uint64_t>(time(nullptr));
|
note.timestamp = static_cast<std::uint64_t>(time(nullptr));
|
||||||
|
note.externalId = uid;
|
||||||
if (!it->title.empty()) {
|
if (!it->title.empty()) {
|
||||||
note.title = it->title;
|
note.title = it->title;
|
||||||
} else if (!it->appIdentifier.empty()) {
|
} else if (!it->appIdentifier.empty()) {
|
||||||
@@ -975,7 +985,9 @@ void handleAncsNotificationSource(uint16_t connHandle, const uint8_t* data, uint
|
|||||||
uid);
|
uid);
|
||||||
|
|
||||||
if (eventId == 2) { // Removed
|
if (eventId == 2) { // Removed
|
||||||
finalizePending(uid);
|
discardPending(uid);
|
||||||
|
g_notificationCenter->removeByExternalId(uid);
|
||||||
|
ESP_LOGI(kLogTag, "Cleared notification uid=%" PRIu32, uid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "cardboy/sdk/app_framework.hpp"
|
#include "cardboy/sdk/app_framework.hpp"
|
||||||
#include "cardboy/sdk/app_system.hpp"
|
#include "cardboy/sdk/app_system.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
@@ -24,6 +25,20 @@ constexpr std::uint32_t kUnlockHoldMs = 1500;
|
|||||||
using Framebuffer = typename AppContext::Framebuffer;
|
using Framebuffer = typename AppContext::Framebuffer;
|
||||||
using Clock = typename AppContext::Clock;
|
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 {
|
struct TimeSnapshot {
|
||||||
bool hasWallTime = false;
|
bool hasWallTime = false;
|
||||||
int hour24 = 0;
|
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) {
|
static std::string truncateWithEllipsis(std::string_view text, int maxWidth, int scale, int letterSpacing) {
|
||||||
if (font16x8::measureText(text, scale, letterSpacing) <= maxWidth)
|
if (font16x8::measureText(text, scale, letterSpacing) <= maxWidth)
|
||||||
return std::string(text);
|
return std::string(text);
|
||||||
@@ -423,11 +462,15 @@ private:
|
|||||||
const int counterWidth = font16x8::measureText(counter, scaleSmall, 1);
|
const int counterWidth = font16x8::measureText(counter, scaleSmall, 1);
|
||||||
const int counterX = cardMarginSide + cardWidth - cardPadding - counterWidth;
|
const int counterX = cardMarginSide + cardWidth - cardPadding - counterWidth;
|
||||||
font16x8::drawText(framebuffer, counterX, cardMarginTop + cardPadding, counter, scaleSmall, true, 1);
|
font16x8::drawText(framebuffer, counterX, cardMarginTop + cardPadding, counter, scaleSmall, true, 1);
|
||||||
const int arrowX = counterX + (counterWidth - 8) / 2;
|
const int arrowWidth = font16x8::kGlyphWidth * scaleSmall;
|
||||||
int arrowY = cardMarginTop + cardPadding + textLineHeight + 1;
|
const int arrowSpacing = std::max(1, scaleSmall);
|
||||||
font16x8::drawText(framebuffer, arrowX, arrowY, "^", scaleSmall, true, 0);
|
const int arrowsTotalWide = arrowWidth * 2 + arrowSpacing;
|
||||||
arrowY += textLineHeight + 2;
|
const int arrowX = counterX + (counterWidth - arrowsTotalWide) / 2;
|
||||||
font16x8::drawText(framebuffer, arrowX, arrowY, "v", scaleSmall, true, 0);
|
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()) {
|
if (!bodyLines.empty()) {
|
||||||
@@ -450,12 +493,15 @@ private:
|
|||||||
font16x8::drawText(framebuffer, summaryX, summaryY, summary, scaleSmall, true, 1);
|
font16x8::drawText(framebuffer, summaryX, summaryY, summary, scaleSmall, true, 1);
|
||||||
|
|
||||||
if (notifications.size() > 1) {
|
if (notifications.size() > 1) {
|
||||||
int arrowX = (framebuffer.width() - 8) / 2;
|
const int arrowWidth = font16x8::kGlyphWidth * scaleSmall;
|
||||||
int arrowY = summaryY + textLineHeight + 1;
|
const int arrowSpacing = std::max(1, scaleSmall);
|
||||||
font16x8::drawText(framebuffer, arrowX, arrowY, "^", scaleSmall, true, 0);
|
const int arrowsTotalWide = arrowWidth * 2 + arrowSpacing;
|
||||||
arrowY += textLineHeight + 2;
|
const int arrowX = (framebuffer.width() - arrowsTotalWide) / 2;
|
||||||
font16x8::drawText(framebuffer, arrowX, arrowY, "v", scaleSmall, true, 0);
|
const int arrowY = summaryY + textLineHeight + 1;
|
||||||
cardHeight = std::max(cardHeight, arrowY + textLineHeight - cardMarginTop);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ public:
|
|||||||
struct Notification {
|
struct Notification {
|
||||||
std::uint64_t id = 0;
|
std::uint64_t id = 0;
|
||||||
std::uint64_t timestamp = 0;
|
std::uint64_t timestamp = 0;
|
||||||
|
std::uint64_t externalId = 0;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string body;
|
std::string body;
|
||||||
bool unread = true;
|
bool unread = true;
|
||||||
@@ -87,6 +88,7 @@ public:
|
|||||||
[[nodiscard]] virtual std::vector<Notification> recent(std::size_t limit) const = 0;
|
[[nodiscard]] virtual std::vector<Notification> recent(std::size_t limit) const = 0;
|
||||||
virtual void markAllRead() = 0;
|
virtual void markAllRead() = 0;
|
||||||
virtual void clear() = 0;
|
virtual void clear() = 0;
|
||||||
|
virtual void removeByExternalId(std::uint64_t externalId) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Services {
|
struct Services {
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ public:
|
|||||||
[[nodiscard]] std::vector<Notification> recent(std::size_t limit) const override;
|
[[nodiscard]] std::vector<Notification> recent(std::size_t limit) const override;
|
||||||
void markAllRead() override;
|
void markAllRead() override;
|
||||||
void clear() override;
|
void clear() override;
|
||||||
|
void removeByExternalId(std::uint64_t externalId) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr std::size_t kMaxEntries = 8;
|
static constexpr std::size_t kMaxEntries = 8;
|
||||||
|
|||||||
@@ -157,12 +157,21 @@ void DesktopNotificationCenter::pushNotification(Notification notification) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
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.id = nextId++;
|
||||||
notification.unread = true;
|
notification.unread = true;
|
||||||
|
|
||||||
if (entries.size() >= kMaxEntries)
|
|
||||||
entries.erase(entries.begin());
|
|
||||||
entries.push_back(std::move(notification));
|
entries.push_back(std::move(notification));
|
||||||
|
while (entries.size() > kMaxEntries)
|
||||||
|
entries.erase(entries.begin());
|
||||||
++revisionCounter;
|
++revisionCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +212,23 @@ void DesktopNotificationCenter::clear() {
|
|||||||
++revisionCounter;
|
++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) {}
|
DesktopFramebuffer::DesktopFramebuffer(DesktopRuntime& runtime) : runtime(runtime) {}
|
||||||
|
|
||||||
int DesktopFramebuffer::width_impl() const { return cardboy::sdk::kDisplayWidth; }
|
int DesktopFramebuffer::width_impl() const { return cardboy::sdk::kDisplayWidth; }
|
||||||
|
|||||||
@@ -695,7 +695,7 @@ CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y
|
|||||||
CONFIG_BT_NIMBLE_ROLE_OBSERVER=y
|
CONFIG_BT_NIMBLE_ROLE_OBSERVER=y
|
||||||
CONFIG_BT_NIMBLE_GATT_CLIENT=y
|
CONFIG_BT_NIMBLE_GATT_CLIENT=y
|
||||||
CONFIG_BT_NIMBLE_GATT_SERVER=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_SMP_ID_RESET is not set
|
||||||
CONFIG_BT_NIMBLE_SECURITY_ENABLE=y
|
CONFIG_BT_NIMBLE_SECURITY_ENABLE=y
|
||||||
CONFIG_BT_NIMBLE_SM_LEGACY=y
|
CONFIG_BT_NIMBLE_SM_LEGACY=y
|
||||||
|
|||||||
Reference in New Issue
Block a user