Критический фикс отправки после верификации устройства и релиз 1.5.2
This commit is contained in:
@@ -23,8 +23,8 @@ val gitShortSha = safeGitOutput("rev-parse", "--short", "HEAD") ?: "unknown"
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Rosetta versioning — bump here on each release
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
val rosettaVersionName = "1.5.1"
|
||||
val rosettaVersionCode = 53 // Increment on each release
|
||||
val rosettaVersionName = "1.5.2"
|
||||
val rosettaVersionCode = 54 // Increment on each release
|
||||
val customWebRtcAar = file("libs/libwebrtc-custom.aar")
|
||||
|
||||
android {
|
||||
|
||||
@@ -17,22 +17,27 @@ object ReleaseNotes {
|
||||
val RELEASE_NOTICE = """
|
||||
Update v$VERSION_PLACEHOLDER
|
||||
|
||||
- Полностью переработан UX записи голосовых: удержание для записи, отправка по отпусканию, Slide to cancel
|
||||
- Пересобрана панель записи ГС в Telegram-style с новым layout, волнами и анимациями
|
||||
- Добавлена и доработана анимация удаления ГС (корзина), устранены рывки и визуальные артефакты
|
||||
- Исправлены зависания/ANR при записи и отмене голосовых (race-condition, stuck-состояния, watchdog-сценарии)
|
||||
- Исправлены скачки и наложения input-панели во время записи (включая Type message/overlay конфликты)
|
||||
- Добавлены улучшения плеера голосовых: мини-плеер, интеграция в чат, корректная работа скоростей
|
||||
- В чат-листе улучшено отображение и поведение активного воспроизведения голосовых
|
||||
- Добавлена и отшлифована система выделения текста: handles, magnifier, toolbar (Copy/Select All), haptic
|
||||
- Исправлены координаты и стабильность выделения текста в сложных сценариях
|
||||
- Исправлена обработка reply в группах с Desktop (fallback на hex-ключ для reply blob)
|
||||
- Оптимизированы тяжелые UI-сценарии: prewarm для circular reveal, ускорена анимация онбординга
|
||||
- Улучшены миниатюры медиа через BlurHash и стабильность загрузки вложений
|
||||
- Доработан экран звонков и related UI (включая пустой экран с Lottie-анимацией)
|
||||
- Доработаны элементы профиля и сайдбара (включая обновления аккаунт-блока и действий)
|
||||
- Добавлена смена иконки приложения (калькулятор, погода, заметки) через настройки
|
||||
- Выполнен большой пакет фиксов по чатам/звонкам/коннекту и визуальному паритету с Telegram
|
||||
- Перемотка голосовых полностью переработана в Telegram-style: drag по waveform и точный seek по отпусканию
|
||||
- Устранены конфликты жестов у ГС: tap/drag/scrub больше не конфликтуют со swipe-to-reply и swipe-back
|
||||
- Голосовой плеер доработан: стабильный scrub в паузе, корректный keepPaused, более надежный прогресс
|
||||
- Добавлена очередь ГС внутри диалога с автопереходом к следующему голосовому по хронологии
|
||||
- Улучшена совместимость payload голосовых (hex/base64 decode fallback) и восстановление аудиофайла из кэша
|
||||
- Исправлено позиционирование и clipping кнопки записи в input-панели
|
||||
- Добавлен haptic при старте записи и обновлены иконки записи voice/video
|
||||
- В сайдбаре ограничен список аккаунтов (до 3) для более чистого Telegram-like layout
|
||||
- Исправлен transition emoji -> keyboard: убран «пустой» зазор при закрытии emoji-панели
|
||||
- В selection header чата добавлена кнопка Pin/Unpin для выбранного сообщения
|
||||
- В Forward-пикере всегда показывается Saved Messages (даже если self-диалог ещё не создан)
|
||||
- Переработаны media-permissions в attach/media picker: корректный permanently denied flow с переходом в Settings
|
||||
- Улучшена инициализация аккаунта после login/unlock/create-account, устранён race «Sync postponed until account is initialized»
|
||||
- Доработана синхронизация профиля аккаунта (name/username/verified), включая замену placeholder-имён
|
||||
- Исправлен критичный баг отправки после верификации нового устройства на втором девайсе
|
||||
- Исправлен reconnect overflow: устранена отрицательная задержка (-2147483648000ms) и дубли disconnect/reconnect
|
||||
- Улучшена обработка device verification (ACCEPT/DECLINE) и reconnect-логика протокола
|
||||
- Звонки: добавлен proximity manager (экран гаснет возле уха), добавлен WAKE_LOCK, учтён speaker on/off
|
||||
- Звонки: рингтон теперь учитывает системный ringer mode (silent/vibrate), снижены ложные звуковые срабатывания
|
||||
- Убраны дубли CALL-attachments у callee: источник call-события теперь единый (каноничный от caller)
|
||||
- Групповые сообщения: fallback для plaintext-пакетов без group key и расширенная диагностика decrypt-ошибок
|
||||
""".trimIndent()
|
||||
|
||||
fun getNotice(version: String): String =
|
||||
|
||||
@@ -311,14 +311,35 @@ class Protocol(
|
||||
when (resolve.solution) {
|
||||
DeviceResolveSolution.ACCEPT -> {
|
||||
log("✅ DEVICE VERIFICATION ACCEPTED (deviceId=${shortKey(resolve.deviceId, 12)})")
|
||||
if (_state.value == ProtocolState.DEVICE_VERIFICATION_REQUIRED) {
|
||||
val stateAtAccept = _state.value
|
||||
if (stateAtAccept == ProtocolState.AUTHENTICATED) {
|
||||
log("✅ ACCEPT ignored: already authenticated")
|
||||
return@waitPacket
|
||||
}
|
||||
|
||||
if (stateAtAccept == ProtocolState.DEVICE_VERIFICATION_REQUIRED) {
|
||||
setState(ProtocolState.CONNECTED, "Device verification accepted")
|
||||
val publicKey = lastPublicKey
|
||||
val privateHash = lastPrivateHash
|
||||
if (!publicKey.isNullOrBlank() && !privateHash.isNullOrBlank()) {
|
||||
}
|
||||
|
||||
val publicKey = lastPublicKey
|
||||
val privateHash = lastPrivateHash
|
||||
if (publicKey.isNullOrBlank() || privateHash.isNullOrBlank()) {
|
||||
log("⚠️ ACCEPT received but credentials are missing, waiting for reconnect")
|
||||
return@waitPacket
|
||||
}
|
||||
|
||||
when (_state.value) {
|
||||
ProtocolState.DISCONNECTED -> {
|
||||
log("🔄 ACCEPT while disconnected -> reconnecting")
|
||||
connect()
|
||||
}
|
||||
|
||||
ProtocolState.CONNECTING -> {
|
||||
log("⏳ ACCEPT while connecting -> waiting for onOpen auto-handshake")
|
||||
}
|
||||
|
||||
else -> {
|
||||
startHandshake(publicKey, privateHash, lastDevice)
|
||||
} else {
|
||||
log("⚠️ ACCEPT received but credentials are missing, waiting for reconnect")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -644,7 +665,14 @@ class Protocol(
|
||||
val currentState = _state.value
|
||||
val socket = webSocket
|
||||
val socketReady = socket != null
|
||||
val authReady = handshakeComplete && currentState == ProtocolState.AUTHENTICATED
|
||||
val authReady = currentState == ProtocolState.AUTHENTICATED
|
||||
if (authReady && !handshakeComplete) {
|
||||
// Defensive self-heal:
|
||||
// AUTHENTICATED state must imply completed handshake.
|
||||
// If these flags diverge, message sending can be stuck in queue forever.
|
||||
log("⚠️ AUTHENTICATED with handshakeComplete=false -> self-heal handshakeComplete=true")
|
||||
handshakeComplete = true
|
||||
}
|
||||
val preAuthAllowedPacket =
|
||||
packet is PacketSignalPeer || packet is PacketWebRTC || packet is PacketIceServers
|
||||
val preAuthReady =
|
||||
@@ -772,6 +800,13 @@ class Protocol(
|
||||
private fun handleDisconnect() {
|
||||
val previousState = _state.value
|
||||
log("🔌 DISCONNECT HANDLER: previousState=$previousState, manuallyClosed=$isManuallyClosed, reconnectAttempts=$reconnectAttempts, isConnecting=$isConnecting")
|
||||
|
||||
// Duplicate callbacks are possible (e.g. heartbeat failure + onFailure/onClosed).
|
||||
// If we are already disconnected and a reconnect is pending, avoid scheduling another one.
|
||||
if (previousState == ProtocolState.DISCONNECTED && reconnectJob?.isActive == true) {
|
||||
log("⚠️ DISCONNECT DUPLICATE: reconnect already scheduled, skipping")
|
||||
return
|
||||
}
|
||||
|
||||
// КРИТИЧНО: если уже идет подключение, не делаем ничего
|
||||
if (isConnecting) {
|
||||
@@ -799,12 +834,16 @@ class Protocol(
|
||||
// КРИТИЧНО: отменяем предыдущий reconnect job если есть
|
||||
reconnectJob?.cancel()
|
||||
|
||||
// Экспоненциальная задержка: 1s, 2s, 4s, 8s, максимум 30s
|
||||
val delayMs = minOf(1000L * (1 shl minOf(reconnectAttempts - 1, 4)), 30000L)
|
||||
log("🔄 SCHEDULING RECONNECT: attempt #$reconnectAttempts, delay=${delayMs}ms")
|
||||
// Экспоненциальная задержка: 1s, 2s, 4s, 8s, 16s, максимум 30s.
|
||||
// IMPORTANT: reconnectAttempts may be 0 right after AUTHENTICATED reset.
|
||||
// Using (1 shl -1) causes overflow (seen in logs as -2147483648000ms).
|
||||
val nextAttemptNumber = (reconnectAttempts + 1).coerceAtLeast(1)
|
||||
val exponent = (nextAttemptNumber - 1).coerceIn(0, 4)
|
||||
val delayMs = minOf(1000L * (1L shl exponent), 30000L)
|
||||
log("🔄 SCHEDULING RECONNECT: attempt #$nextAttemptNumber, delay=${delayMs}ms")
|
||||
|
||||
if (reconnectAttempts > 20) {
|
||||
log("⚠️ WARNING: Too many reconnect attempts ($reconnectAttempts), may be stuck in loop")
|
||||
if (nextAttemptNumber > 20) {
|
||||
log("⚠️ WARNING: Too many reconnect attempts ($nextAttemptNumber), may be stuck in loop")
|
||||
}
|
||||
|
||||
reconnectJob = scope.launch {
|
||||
|
||||
Reference in New Issue
Block a user