diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java index eac081d8b6..8969eed452 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java @@ -482,7 +482,7 @@ public class ConversationAdapter /** * Momentarily highlights a mention at the requested position. */ - void pulseAtPosition(int position) { + public void pulseAtPosition(int position) { if (position >= 0 && position < getItemCount()) { int correctedPosition = isHeaderPosition(position) ? position + 1 : position; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesBottomSheet.kt index a1f62fe6ec..51e2253ad2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesBottomSheet.kt @@ -10,6 +10,8 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager @@ -93,7 +95,16 @@ class MessageQuotesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment() { messageAdapter.submitList(messages) { if (firstRender) { - (list.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(messages.size - 1, 100) + val targetMessageId = MessageId.deserialize(arguments?.getString(KEY_MESSAGE_ID, null) ?: throw IllegalArgumentException()) + val targetMessagePosition = messages.indexOfFirst { it.messageRecord.id == targetMessageId.id && it.messageRecord.isMms == targetMessageId.mms } + + (list.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(targetMessagePosition, 100) + + if (targetMessagePosition != messages.size - 1) { + (dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED + messageAdapter.pulseAtPosition(targetMessagePosition) + } + firstRender = false } else if (!list.canScrollVertically(1)) { list.layoutManager?.scrollToPosition(0) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesRepository.kt index 6205b4b15f..94a09f88f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesRepository.kt @@ -35,28 +35,30 @@ class MessageQuotesRepository { } val databaseObserver: DatabaseObserver = ApplicationDependencies.getDatabaseObserver() - val observer = DatabaseObserver.Observer { emitter.onNext(getMessageInQuoteChainSync(application, messageId)) } + val observer = DatabaseObserver.Observer { emitter.onNext(getMessagesInQuoteChainSync(application, messageId)) } databaseObserver.registerConversationObserver(threadId, observer) emitter.setCancellable { databaseObserver.unregisterObserver(observer) } - emitter.onNext(getMessageInQuoteChainSync(application, messageId)) + emitter.onNext(getMessagesInQuoteChainSync(application, messageId)) } } @WorkerThread - private fun getMessageInQuoteChainSync(application: Application, messageId: MessageId): List { - var originalRecord: MessageRecord? = if (messageId.mms) { - SignalDatabase.mms.getMessageRecordOrNull(messageId.id) + private fun getMessagesInQuoteChainSync(application: Application, messageId: MessageId): List { + val rootMessageId: MessageId = SignalDatabase.mmsSms.getRootOfQuoteChain(messageId) + + var originalRecord: MessageRecord? = if (rootMessageId.mms) { + SignalDatabase.mms.getMessageRecordOrNull(rootMessageId.id) } else { - SignalDatabase.sms.getMessageRecordOrNull(messageId.id) + SignalDatabase.sms.getMessageRecordOrNull(rootMessageId.id) } if (originalRecord == null) { return emptyList() } - val replyRecords: List = SignalDatabase.mmsSms.getAllMessagesThatQuote(messageId) + val replyRecords: List = SignalDatabase.mmsSms.getAllMessagesThatQuote(rootMessageId) val replies: List = ConversationDataSource.ReactionHelper() .apply { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsTable.java index 0551909f20..bbc0f65909 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsTable.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsTable.java @@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.database.MessageTable.SyncMessageId; import org.thoughtcrime.securesms.database.model.MessageExportStatus; import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.notifications.v2.DefaultMessageNotifier; @@ -290,6 +291,39 @@ public class MmsSmsTable extends DatabaseTable { } } + public MessageId getRootOfQuoteChain(@NonNull MessageId id) { + if (!id.isMms()) { + return id; + } + + MmsMessageRecord targetMessage; + try { + targetMessage = (MmsMessageRecord) SignalDatabase.mms().getMessageRecord(id.getId()); + } catch (NoSuchMessageException e) { + throw new IllegalArgumentException("Invalid message ID!"); + } + + if (targetMessage.getQuote() == null) { + return id; + } + + String query; + if (targetMessage.getQuote().getAuthor().equals(Recipient.self().getId())) { + query = MmsTable.DATE_SENT + " = " + targetMessage.getQuote().getId() + " AND (" + MmsSmsColumns.TYPE + " & " + MmsSmsColumns.Types.BASE_TYPE_MASK + ") = " + MmsSmsColumns.Types.BASE_SENT_TYPE; + } else { + query = MmsTable.DATE_SENT + " = " + targetMessage.getQuote().getId() + " AND " + MmsTable.RECIPIENT_ID + " = '" + targetMessage.getQuote().getAuthor().serialize() + "'"; + } + + try (Reader reader = new Reader(queryTables(PROJECTION, query, null, "1", false))) { + MessageRecord record; + if ((record = reader.getNext()) != null) { + return getRootOfQuoteChain(new MessageId(record.getId(), record.isMms())); + } + } + + return id; + } + public List getAllMessagesThatQuote(@NonNull MessageId id) { MessageRecord targetMessage; try {