mirror of
https://github.com/pelgraine/Meck.git
synced 2026-07-04 08:41:19 +02:00
ed039aa711
The "SMS Inbox" badge on the Phone & SMS landing screen previously showed the number of conversations regardless of read state. It now shows the number of unread received messages and disappears when there are none. Read state is tracked persistently, so it survives reboots and modem power-cycles (the flag lives in each record on the SD card). SMSStore: - Add a `read` byte to SMSRecord, reusing one of the reserved bytes so the record stays 256 bytes and every existing field offset is unchanged (existing .sms files remain readable). - saveMessage marks sent messages read and received messages unread. - loadConversations scans each file and reports unreadCount (received messages with read=0); preview / last-message behaviour is unchanged. - markConversationRead / markFileRead flip received-unread records to read in place (open "r+", rewrite only the affected records). - begin() runs a one-time migration on first boot that marks all pre-existing history read, gated by a marker file (/sms/rdmig.dat), so old messages do not all appear as unread after the update. SMSScreen: - Landing-screen badge sums unreadCount across conversations, hidden at zero. - Opening a conversation marks it read and clears its in-RAM count. - A message arriving while its conversation is open is marked read. - Inbox reloads on the landing screen as well as the inbox view, so the badge updates live when a message arrives.
98 lines
2.9 KiB
C++
98 lines
2.9 KiB
C++
#pragma once
|
|
|
|
// =============================================================================
|
|
// SMSStore - SD card backed SMS message storage
|
|
//
|
|
// Stores sent and received messages in /sms/ on the SD card.
|
|
// Each conversation is a separate file named by phone number (sanitised).
|
|
// Messages are appended as fixed-size records for simple random access.
|
|
//
|
|
// Guard: HAS_4G_MODEM
|
|
// =============================================================================
|
|
|
|
#ifdef HAS_4G_MODEM
|
|
|
|
#ifndef SMS_STORE_H
|
|
#define SMS_STORE_H
|
|
|
|
#include <Arduino.h>
|
|
#include <SD.h>
|
|
|
|
#define SMS_PHONE_LEN 20
|
|
#define SMS_BODY_LEN 161
|
|
#define SMS_MAX_CONVERSATIONS 20
|
|
#define SMS_DIR "/sms"
|
|
#define SMS_READ_MIGRATED "/sms/rdmig.dat" // one-time read-state migration marker
|
|
|
|
// Fixed-size on-disk record (256 bytes, easy alignment)
|
|
struct SMSRecord {
|
|
uint32_t timestamp; // epoch seconds
|
|
uint8_t isSent; // 1=sent, 0=received
|
|
uint8_t read; // 1=read, 0=unread (received messages only)
|
|
uint8_t reserved[1];
|
|
uint8_t bodyLen; // actual length of body
|
|
char phone[SMS_PHONE_LEN]; // 20
|
|
char body[SMS_BODY_LEN]; // 161
|
|
uint8_t padding[256 - 4 - 3 - 1 - SMS_PHONE_LEN - SMS_BODY_LEN];
|
|
};
|
|
|
|
// In-memory message for UI
|
|
struct SMSMessage {
|
|
uint32_t timestamp;
|
|
bool isSent;
|
|
bool valid;
|
|
char phone[SMS_PHONE_LEN];
|
|
char body[SMS_BODY_LEN];
|
|
};
|
|
|
|
// Conversation summary for inbox view
|
|
struct SMSConversation {
|
|
char phone[SMS_PHONE_LEN];
|
|
char preview[40]; // last message preview
|
|
uint32_t lastTimestamp;
|
|
int messageCount;
|
|
int unreadCount;
|
|
bool valid;
|
|
};
|
|
|
|
class SMSStore {
|
|
public:
|
|
void begin();
|
|
bool isReady() const { return _ready; }
|
|
|
|
// Save a message (sent or received)
|
|
bool saveMessage(const char* phone, const char* body, bool isSent, uint32_t timestamp);
|
|
|
|
// Load conversation list (sorted by most recent)
|
|
int loadConversations(SMSConversation* out, int maxCount);
|
|
|
|
// Load messages for a specific phone number (chronological, oldest first)
|
|
int loadMessages(const char* phone, SMSMessage* out, int maxCount);
|
|
|
|
// Delete all messages for a phone number
|
|
bool deleteConversation(const char* phone);
|
|
|
|
// Get total message count for a phone number
|
|
int getMessageCount(const char* phone);
|
|
|
|
// Mark all received messages in a conversation as read (persisted to SD)
|
|
void markConversationRead(const char* phone);
|
|
|
|
private:
|
|
bool _ready = false;
|
|
|
|
// Convert phone number to safe filename
|
|
void phoneToFilename(const char* phone, char* out, size_t outLen);
|
|
|
|
// Set read=1 on every received-unread record in a conversation file
|
|
void markFileRead(const char* filepath);
|
|
|
|
// One-time: mark all pre-existing history read (it pre-dates read tracking)
|
|
void migrateExistingAsRead();
|
|
};
|
|
|
|
// Global singleton
|
|
extern SMSStore smsStore;
|
|
|
|
#endif // SMS_STORE_H
|
|
#endif // HAS_4G_MODEM
|