feat: Bump version to 1.0.11, add ConnectionLogsScreen, and enhance message synchronization logic
This commit is contained in:
@@ -98,6 +98,7 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
|
||||
/** Desktop parity: MESSAGE_MAX_TIME_TO_DELEVERED_S = 80 (seconds) */
|
||||
private const val MESSAGE_MAX_TIME_TO_DELIVERED_MS = 80_000L
|
||||
private const val MAX_SYNC_FUTURE_DRIFT_MS = 86_400_000L // 24h
|
||||
|
||||
const val SYSTEM_SAFE_PUBLIC_KEY = "0x000000000000000000000000000000000000000002"
|
||||
const val SYSTEM_SAFE_TITLE = "Safe"
|
||||
@@ -366,16 +367,43 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
|
||||
suspend fun getLastSyncTimestamp(): Long {
|
||||
val account = currentAccount ?: return 0L
|
||||
return syncTimeDao.getLastSync(account) ?: 0L
|
||||
val stored = syncTimeDao.getLastSync(account) ?: 0L
|
||||
val normalized = normalizeSyncTimestamp(stored)
|
||||
if (normalized != stored) {
|
||||
syncTimeDao.upsert(AccountSyncTimeEntity(account = account, lastSync = normalized))
|
||||
if (stored > 0) {
|
||||
android.util.Log.w(
|
||||
"MessageRepository",
|
||||
"⚠️ Normalized invalid last_sync for account=${account.take(10)}...: $stored -> $normalized"
|
||||
)
|
||||
ProtocolManager.addLog("⚠️ SYNC cursor normalized: $stored -> $normalized")
|
||||
}
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
suspend fun updateLastSyncTimestamp(timestamp: Long) {
|
||||
if (timestamp <= 0) return
|
||||
val account = currentAccount ?: return
|
||||
val existing = syncTimeDao.getLastSync(account) ?: 0L
|
||||
if (timestamp > existing) {
|
||||
syncTimeDao.upsert(AccountSyncTimeEntity(account = account, lastSync = timestamp))
|
||||
val normalized = normalizeSyncTimestamp(timestamp)
|
||||
if (normalized <= 0) return
|
||||
// Desktop parity: allow moving sync cursor backward if needed.
|
||||
syncTimeDao.upsert(AccountSyncTimeEntity(account = account, lastSync = normalized))
|
||||
}
|
||||
|
||||
private fun normalizeSyncTimestamp(rawTimestamp: Long): Long {
|
||||
if (rawTimestamp <= 0) return 0L
|
||||
val now = System.currentTimeMillis()
|
||||
val maxAllowed = now + MAX_SYNC_FUTURE_DRIFT_MS
|
||||
var normalized = rawTimestamp
|
||||
|
||||
// Heal common corruption where extra decimal places appear in timestamp.
|
||||
while (normalized > maxAllowed) {
|
||||
normalized /= 10L
|
||||
if (normalized <= 0L) return 0L
|
||||
}
|
||||
|
||||
return if (normalized > maxAllowed) 0L else normalized
|
||||
}
|
||||
|
||||
/** Получить поток сообщений для диалога */
|
||||
@@ -591,21 +619,21 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
return optimisticMessage
|
||||
}
|
||||
|
||||
/** Обработка входящего сообщения */
|
||||
suspend fun handleIncomingMessage(packet: PacketMessage) {
|
||||
/** Обработка входящего сообщения. Возвращает true если пакет обработан безопасно. */
|
||||
suspend fun handleIncomingMessage(packet: PacketMessage): Boolean {
|
||||
val startTime = System.currentTimeMillis()
|
||||
|
||||
val account =
|
||||
currentAccount
|
||||
?: run {
|
||||
MessageLogger.debug("📥 RECEIVE SKIP: account is null")
|
||||
return
|
||||
return false
|
||||
}
|
||||
val privateKey =
|
||||
currentPrivateKey
|
||||
?: run {
|
||||
MessageLogger.debug("📥 RECEIVE SKIP: privateKey is null")
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
// 📝 LOG: Начало обработки входящего сообщения
|
||||
@@ -625,7 +653,7 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
val isBlocked = database.blacklistDao().isUserBlocked(packet.fromPublicKey, account)
|
||||
if (isBlocked) {
|
||||
MessageLogger.logBlockedSender(packet.fromPublicKey)
|
||||
return
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,14 +678,14 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
MessageLogger.debug(
|
||||
"📥 SKIP (in-memory cache): Message $messageId already being processed"
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
// 🔥 ВТОРОЙ УРОВЕНЬ ЗАЩИТЫ: Проверка в БД (для сообщений сохранённых в предыдущих сессиях)
|
||||
val isDuplicate = messageDao.messageExists(account, messageId)
|
||||
MessageLogger.logDuplicateCheck(messageId, isDuplicate)
|
||||
if (isDuplicate) {
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
val dialogOpponentKey = if (isOwnMessage) packet.toPublicKey else packet.fromPublicKey
|
||||
@@ -794,6 +822,7 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
|
||||
// 📝 LOG: Успешная обработка
|
||||
MessageLogger.logReceiveSuccess(messageId, System.currentTimeMillis() - startTime)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
// 📝 LOG: Ошибка обработки
|
||||
MessageLogger.logDecryptionError(messageId, e)
|
||||
@@ -803,6 +832,7 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
// Разрешаем повторную обработку через re-sync, если пакет не удалось сохранить.
|
||||
processedMessageIds.remove(messageId)
|
||||
e.printStackTrace()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,15 @@ object ReleaseNotes {
|
||||
val RELEASE_NOTICE = """
|
||||
Update v$VERSION_PLACEHOLDER
|
||||
|
||||
Верификация аккаунта
|
||||
- Бейдж верификации отображается в боковом меню рядом с именем
|
||||
- Бейдж верификации отображается в экране профиля
|
||||
- Статус загружается из кэша пользователей при старте
|
||||
Синхронизация сообщений
|
||||
- Исправлен недолет сообщений после оффлайна при массовой отправке (спам-тест)
|
||||
- Исправлен сценарий, когда синхронизация останавливалась на первой пачке
|
||||
- Нормализуется sync-cursor (last_sync), включая поврежденные timestamp
|
||||
- Следующий sync-запрос отправляется с безопасным timestamp
|
||||
|
||||
Стабильность
|
||||
- Фильтрация неподдерживаемых пакетов (группы, conversations)
|
||||
- Добавлена фильтрация delivery-пакетов для неподдерживаемых диалогов
|
||||
- Улучшена обработка ошибок в очереди входящих пакетов
|
||||
Стабильность протокола
|
||||
- Улучшена защита чтения строк из бинарного потока
|
||||
- Ошибки внутри батча больше не клинят дальнейшую догрузку пакетов
|
||||
""".trimIndent()
|
||||
|
||||
fun getNotice(version: String): String =
|
||||
|
||||
Reference in New Issue
Block a user