feat(chat-input): привести lock flow записи ГС к Telegram (геометрия и анимации)
This commit is contained in:
@@ -27,10 +27,8 @@
|
||||
|
||||
<application
|
||||
android:name=".RosettaApplication"
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.rosetta.messenger.data
|
||||
|
||||
import android.content.Context
|
||||
import com.rosetta.messenger.BuildConfig
|
||||
import com.rosetta.messenger.crypto.CryptoManager
|
||||
import com.rosetta.messenger.crypto.MessageCrypto
|
||||
import com.rosetta.messenger.database.*
|
||||
@@ -266,7 +267,7 @@ class MessageRepository @Inject constructor(
|
||||
try {
|
||||
CryptoManager.encryptWithPassword(messageText, privateKey)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("ReleaseNotes", "❌ encryptWithPassword failed", e)
|
||||
if (BuildConfig.DEBUG) android.util.Log.e("ReleaseNotes", "❌ encryptWithPassword failed", e)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -351,12 +352,12 @@ class MessageRepository @Inject constructor(
|
||||
suspend fun checkAndSendVersionUpdateMessage() {
|
||||
val account = currentAccount
|
||||
if (account == null) {
|
||||
android.util.Log.w("ReleaseNotes", "❌ currentAccount is null, skipping update message")
|
||||
if (BuildConfig.DEBUG) android.util.Log.w("ReleaseNotes", "❌ currentAccount is null, skipping update message")
|
||||
return
|
||||
}
|
||||
val privateKey = currentPrivateKey
|
||||
if (privateKey == null) {
|
||||
android.util.Log.w("ReleaseNotes", "❌ currentPrivateKey is null, skipping update message")
|
||||
if (BuildConfig.DEBUG) android.util.Log.w("ReleaseNotes", "❌ currentPrivateKey is null, skipping update message")
|
||||
return
|
||||
}
|
||||
val prefs = context.getSharedPreferences("rosetta_system_${account}", Context.MODE_PRIVATE)
|
||||
@@ -364,7 +365,7 @@ class MessageRepository @Inject constructor(
|
||||
val currentVersion = com.rosetta.messenger.BuildConfig.VERSION_NAME
|
||||
val currentKey = "${currentVersion}_${ReleaseNotes.noticeHash}"
|
||||
|
||||
android.util.Log.d("ReleaseNotes", "checkUpdate: version=$currentVersion, lastKey=$lastNoticeKey, currentKey=$currentKey, match=${lastNoticeKey == currentKey}")
|
||||
if (BuildConfig.DEBUG) android.util.Log.d("ReleaseNotes", "checkUpdate: version=$currentVersion, lastKey=$lastNoticeKey, currentKey=$currentKey, match=${lastNoticeKey == currentKey}")
|
||||
|
||||
if (lastNoticeKey != currentKey) {
|
||||
// Delete the previous message for this version (if any)
|
||||
@@ -375,15 +376,15 @@ class MessageRepository @Inject constructor(
|
||||
}
|
||||
|
||||
val messageId = addUpdateSystemMessage(ReleaseNotes.getNotice(currentVersion))
|
||||
android.util.Log.d("ReleaseNotes", "addUpdateSystemMessage result: messageId=$messageId")
|
||||
if (BuildConfig.DEBUG) android.util.Log.d("ReleaseNotes", "addUpdateSystemMessage result: messageId=$messageId")
|
||||
if (messageId != null) {
|
||||
prefs.edit()
|
||||
.putString("lastNoticeKey", currentKey)
|
||||
.putString("lastNoticeMessageId_$currentVersion", messageId)
|
||||
.apply()
|
||||
android.util.Log.d("ReleaseNotes", "✅ Update message saved successfully")
|
||||
if (BuildConfig.DEBUG) android.util.Log.d("ReleaseNotes", "✅ Update message saved successfully")
|
||||
} else {
|
||||
android.util.Log.e("ReleaseNotes", "❌ Failed to create update message")
|
||||
if (BuildConfig.DEBUG) android.util.Log.e("ReleaseNotes", "❌ Failed to create update message")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -881,7 +882,7 @@ class MessageRepository @Inject constructor(
|
||||
MessageCrypto.decryptIncoming(packet.content, packet.chachaKey, privateKey)
|
||||
} catch (e: Exception) {
|
||||
// Fallback: если дешифровка не удалась (напр. CALL с encrypted empty content)
|
||||
android.util.Log.w("MessageRepository", "Decryption fallback for ${messageId.take(8)}: ${e.message}")
|
||||
if (BuildConfig.DEBUG) android.util.Log.w("MessageRepository", "Decryption fallback for ${messageId.take(8)}: ${e.message}")
|
||||
""
|
||||
}
|
||||
}
|
||||
@@ -1326,7 +1327,7 @@ class MessageRepository @Inject constructor(
|
||||
syncedOpponentsWithWrongStatus.forEach { opponentKey ->
|
||||
runCatching { dialogDao.updateDialogFromMessages(account, opponentKey) }
|
||||
}
|
||||
android.util.Log.i(
|
||||
if (BuildConfig.DEBUG) android.util.Log.i(
|
||||
"MessageRepository",
|
||||
"✅ Normalized $normalizedSyncedCount synced own messages to DELIVERED"
|
||||
)
|
||||
@@ -1335,14 +1336,14 @@ class MessageRepository @Inject constructor(
|
||||
// Mark expired messages as ERROR (older than 80 seconds)
|
||||
val expiredCount = messageDao.markExpiredWaitingAsError(account, now - MESSAGE_MAX_TIME_TO_DELIVERED_MS)
|
||||
if (expiredCount > 0) {
|
||||
android.util.Log.w("MessageRepository", "⚠️ Marked $expiredCount expired WAITING messages as ERROR")
|
||||
if (BuildConfig.DEBUG) android.util.Log.w("MessageRepository", "⚠️ Marked $expiredCount expired WAITING messages as ERROR")
|
||||
}
|
||||
|
||||
// Get remaining WAITING messages (younger than 80s)
|
||||
val waitingMessages = messageDao.getWaitingMessages(account, now - MESSAGE_MAX_TIME_TO_DELIVERED_MS)
|
||||
if (waitingMessages.isEmpty()) return
|
||||
|
||||
android.util.Log.i("MessageRepository", "🔄 Retrying ${waitingMessages.size} WAITING messages")
|
||||
if (BuildConfig.DEBUG) android.util.Log.i("MessageRepository", "🔄 Retrying ${waitingMessages.size} WAITING messages")
|
||||
|
||||
for (entity in waitingMessages) {
|
||||
// Skip saved messages (should not happen, but guard)
|
||||
@@ -1366,7 +1367,7 @@ class MessageRepository @Inject constructor(
|
||||
privateKey
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.w("MessageRepository", "⚠️ Cannot regenerate aesChachaKey for ${entity.messageId.take(8)}, sending without it")
|
||||
if (BuildConfig.DEBUG) android.util.Log.w("MessageRepository", "⚠️ Cannot regenerate aesChachaKey for ${entity.messageId.take(8)}, sending without it")
|
||||
""
|
||||
}
|
||||
}
|
||||
@@ -1393,9 +1394,9 @@ class MessageRepository @Inject constructor(
|
||||
|
||||
// iOS parity: use retry mechanism for reconnect-resent messages too
|
||||
protocolClient.sendMessageWithRetry(packet)
|
||||
android.util.Log.d("MessageRepository", "🔄 Resent WAITING message: ${entity.messageId.take(8)}")
|
||||
if (BuildConfig.DEBUG) android.util.Log.d("MessageRepository", "🔄 Resent WAITING message: ${entity.messageId.take(8)}")
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("MessageRepository", "❌ Failed to retry message ${entity.messageId.take(8)}: ${e.message}")
|
||||
if (BuildConfig.DEBUG) android.util.Log.e("MessageRepository", "❌ Failed to retry message ${entity.messageId.take(8)}: ${e.message}")
|
||||
// Mark as ERROR if retry fails
|
||||
messageDao.updateDeliveryStatus(account, entity.messageId, DeliveryStatus.ERROR.value)
|
||||
val dialogKey = getDialogKey(entity.toPublicKey)
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.graphics.BitmapFactory
|
||||
import android.os.Build
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.rosetta.messenger.BuildConfig
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import com.google.firebase.messaging.FirebaseMessagingService
|
||||
@@ -136,7 +137,7 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
/** Вызывается когда получено push-уведомление */
|
||||
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||
super.onMessageReceived(remoteMessage)
|
||||
Log.d(TAG, "\u2709\ufe0f onMessageReceived: messageId=${remoteMessage.messageId} from=${remoteMessage.from} data=${remoteMessage.data} notif=${remoteMessage.notification?.body}")
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "\u2709\ufe0f onMessageReceived: messageId=${remoteMessage.messageId} from=${remoteMessage.from} data=${remoteMessage.data} notif=${remoteMessage.notification?.body}")
|
||||
|
||||
val data = remoteMessage.data
|
||||
val notificationTitle = remoteMessage.notification?.title?.trim().orEmpty()
|
||||
@@ -153,7 +154,7 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
val hasNotificationContent = notificationTitle.isNotBlank() || notificationBody.isNotBlank()
|
||||
|
||||
if (!hasDataContent && !hasNotificationContent) {
|
||||
Log.d(TAG, "Silent/empty push ignored (iOS wake-up push)")
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Silent/empty push ignored (iOS wake-up push)")
|
||||
// Still trigger reconnect if WebSocket is disconnected
|
||||
protocolGateway.reconnectNowIfNeeded("silent_push")
|
||||
return
|
||||
@@ -226,14 +227,14 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
isReadEvent -> {
|
||||
val keysToClear = collectReadDialogKeys(data, dialogKey, senderPublicKey)
|
||||
if (keysToClear.isEmpty()) {
|
||||
Log.d(TAG, "READ push received but no dialog key in payload: $data")
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "READ push received but no dialog key in payload: $data")
|
||||
} else {
|
||||
keysToClear.forEach { key ->
|
||||
cancelNotificationForChat(applicationContext, key)
|
||||
}
|
||||
val titleHints = collectReadTitleHints(data, keysToClear)
|
||||
cancelMatchingActiveNotifications(keysToClear, titleHints)
|
||||
Log.d(
|
||||
if (BuildConfig.DEBUG) Log.d(
|
||||
TAG,
|
||||
"READ push cleared notifications for keys=$keysToClear titles=$titleHints"
|
||||
)
|
||||
@@ -317,11 +318,11 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
val now = System.currentTimeMillis()
|
||||
val lastTs = lastNotifTimestamps[dedupKey]
|
||||
if (lastTs != null && now - lastTs < DEDUP_WINDOW_MS) {
|
||||
Log.d(TAG, "\ud83d\udeab Dedup BLOCKED notification for key=$dedupKey, delta=${now - lastTs}ms")
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "\ud83d\udeab Dedup BLOCKED notification for key=$dedupKey, delta=${now - lastTs}ms")
|
||||
return // duplicate push — skip
|
||||
}
|
||||
lastNotifTimestamps[dedupKey] = now
|
||||
Log.d(TAG, "\u2705 Showing notification for key=$dedupKey")
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "\u2705 Showing notification for key=$dedupKey")
|
||||
val senderKey = senderPublicKey?.trim().orEmpty()
|
||||
if (senderKey.isNotEmpty() && isDialogMuted(senderKey)) {
|
||||
return
|
||||
@@ -508,7 +509,7 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
}
|
||||
|
||||
private fun pushCallLog(msg: String) {
|
||||
Log.d(TAG, msg)
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, msg)
|
||||
try {
|
||||
val dir = java.io.File(applicationContext.filesDir, "crash_reports")
|
||||
if (!dir.exists()) dir.mkdirs()
|
||||
@@ -534,7 +535,7 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
pushCallLog("wakeProtocolFromPush: authRestore=$restored account=${account.take(8)}…")
|
||||
protocolGateway.reconnectNowIfNeeded("push_$reason")
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "wakeProtocolFromPush failed: ${error.message}")
|
||||
if (BuildConfig.DEBUG) Log.w(TAG, "wakeProtocolFromPush failed: ${error.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -717,7 +718,7 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
|
||||
if (matchesDeterministicId || matchesDialogKey || matchesHint) {
|
||||
manager.cancel(sbn.tag, sbn.id)
|
||||
Log.d(
|
||||
if (BuildConfig.DEBUG) Log.d(
|
||||
TAG,
|
||||
"READ push fallback cancel id=${sbn.id} tag=${sbn.tag} " +
|
||||
"channel=${notification.channelId} title='$title' " +
|
||||
@@ -726,7 +727,7 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
}
|
||||
}
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "cancelMatchingActiveNotifications failed: ${error.message}")
|
||||
if (BuildConfig.DEBUG) Log.w(TAG, "cancelMatchingActiveNotifications failed: ${error.message}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2471,7 +2471,9 @@ fun CallAttachment(
|
||||
text = callUi.subtitle,
|
||||
fontSize = 12.sp,
|
||||
color =
|
||||
if (callUi.isError) {
|
||||
if (callUi.isError && isOutgoing) {
|
||||
Color.White.copy(alpha = 0.72f)
|
||||
} else if (callUi.isError) {
|
||||
Color(0xFFE55A5A)
|
||||
} else if (isOutgoing) {
|
||||
Color.White.copy(alpha = 0.72f)
|
||||
|
||||
@@ -563,7 +563,6 @@ fun MessageBubble(
|
||||
Modifier.fillMaxWidth().pointerInput(isSystemSafeChat, textSelectionHelper?.isActive, isVoiceWaveGestureActive) {
|
||||
if (isSystemSafeChat) return@pointerInput
|
||||
if (textSelectionHelper?.isActive == true) return@pointerInput
|
||||
if (hasVoiceAttachmentForGesture) return@pointerInput
|
||||
if (isVoiceWaveGestureActive) return@pointerInput
|
||||
// 🔥 Простой горизонтальный свайп для reply
|
||||
// Используем detectHorizontalDragGestures который лучше работает со
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user