diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBottomSheetCallback.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBottomSheetCallback.kt new file mode 100644 index 0000000000..0ac3f09a6c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBottomSheetCallback.kt @@ -0,0 +1,12 @@ +package org.thoughtcrime.securesms.conversation + +import org.thoughtcrime.securesms.database.model.MessageRecord + +/** + * Callback interface for bottom sheets that show conversation data in a conversation and + * want to manipulate the conversation view. + */ +interface ConversationBottomSheetCallback { + fun getConversationAdapterListener(): ConversationAdapter.ItemClickListener + fun jumpToMessage(messageRecord: MessageRecord) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 8276789489..009bbb7afc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -207,7 +207,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import kotlin.Unit; @SuppressLint("StaticFieldLeak") -public class ConversationFragment extends LoggingFragment implements MultiselectForwardBottomSheet.Callback, MessageQuotesBottomSheet.Callback { +public class ConversationFragment extends LoggingFragment implements MultiselectForwardBottomSheet.Callback, ConversationBottomSheetCallback { private static final String TAG = Log.tag(ConversationFragment.class); private static final int SCROLL_ANIMATION_THRESHOLD = 50; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java index 1bb7d7c4ba..cf3aad9805 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java @@ -20,7 +20,6 @@ import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; -import android.app.AlarmManager; import android.app.PendingIntent; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -137,8 +136,6 @@ import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel; import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView; import org.thoughtcrime.securesms.components.location.SignalPlace; import org.thoughtcrime.securesms.components.mention.MentionAnnotation; -import org.thoughtcrime.securesms.components.menu.ActionItem; -import org.thoughtcrime.securesms.components.menu.SignalContextMenu; import org.thoughtcrime.securesms.components.reminder.BubbleOptOutReminder; import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder; import org.thoughtcrime.securesms.components.reminder.GroupsV1MigrationSuggestionsReminder; @@ -180,6 +177,7 @@ import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.ThreadTable; import org.thoughtcrime.securesms.database.identity.IdentityRecordList; +import org.thoughtcrime.securesms.database.model.GroupRecord; import org.thoughtcrime.securesms.database.model.IdentityRecord; import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; import org.thoughtcrime.securesms.database.model.Mention; @@ -337,8 +335,6 @@ import kotlin.Unit; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import org.thoughtcrime.securesms.database.model.GroupRecord; - /** * Fragment for displaying a message thread, as well as * composing/sending a new message into that thread. @@ -370,7 +366,7 @@ public class ConversationParentFragment extends Fragment Material3OnScrollHelperBinder, MessageDetailsFragment.Callback, ScheduleMessageTimePickerBottomSheet.ScheduleCallback, - ScheduledMessagesBottomSheet.Callback + ConversationBottomSheetCallback { private static final int SHORTCUT_ICON_SIZE = Build.VERSION.SDK_INT >= 26 ? ViewUtil.dpToPx(72) : ViewUtil.dpToPx(48 + 16 * 2); @@ -3677,10 +3673,16 @@ public class ConversationParentFragment extends Fragment sendMessage(null, scheduledTime); } + @Override public @NonNull ConversationAdapter.ItemClickListener getConversationAdapterListener() { return fragment.getConversationAdapterListener(); } - + + @Override + public void jumpToMessage(@NonNull MessageRecord messageRecord) { + fragment.jumpToMessage(messageRecord); + } + // Listeners private class RecordingSession implements SingleObserver { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ScheduledMessagesBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ScheduledMessagesBottomSheet.kt index a92dd5e0a8..cd964e901b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ScheduledMessagesBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ScheduledMessagesBottomSheet.kt @@ -38,7 +38,6 @@ import org.thoughtcrime.securesms.giph.mp4.GiphyMp4ProjectionPlayerHolder import org.thoughtcrime.securesms.giph.mp4.GiphyMp4ProjectionRecycler import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange -import org.thoughtcrime.securesms.linkpreview.LinkPreview import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.mms.TextSlide @@ -67,7 +66,7 @@ class ScheduledMessagesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment private var deleteDialog: AlertDialog? = null private lateinit var messageAdapter: ConversationAdapter - private lateinit var callback: Callback + private lateinit var callback: ConversationBottomSheetCallback private val viewModel: ScheduledMessagesViewModel by viewModels( factoryProducer = { @@ -244,18 +243,25 @@ class ScheduledMessagesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment viewModel.rescheduleMessage(messageId, scheduledTime) } - private fun getAdapterListener(): ConversationAdapter.ItemClickListener { - return callback.getConversationAdapterListener() - } + private inner class ConversationAdapterListener : ConversationAdapter.ItemClickListener by callback.getConversationAdapterListener() { + override fun onQuoteClicked(messageRecord: MmsMessageRecord) { + dismissAllowingStateLoss() + callback.getConversationAdapterListener().onQuoteClicked(messageRecord) + } + + override fun onScheduledIndicatorClicked(view: View, messageRecord: MessageRecord) { + showScheduledMessageContextMenu(view, messageRecord) + } + + override fun onGroupMemberClicked(recipientId: RecipientId, groupId: GroupId) { + dismissAllowingStateLoss() + callback.getConversationAdapterListener().onGroupMemberClicked(recipientId, groupId) + } - private inner class ConversationAdapterListener : ConversationAdapter.ItemClickListener by getAdapterListener() { override fun onItemClick(item: MultiselectPart) = Unit override fun onItemLongClick(itemView: View, item: MultiselectPart) = Unit - override fun onQuoteClicked(messageRecord: MmsMessageRecord) = Unit - override fun onLinkPreviewClicked(linkPreview: LinkPreview) = Unit override fun onQuotedIndicatorClicked(messageRecord: MessageRecord) = Unit override fun onReactionClicked(multiselectPart: MultiselectPart, messageId: Long, isMms: Boolean) = Unit - override fun onGroupMemberClicked(recipientId: RecipientId, groupId: GroupId) = Unit override fun onMessageWithRecaptchaNeededClicked(messageRecord: MessageRecord) = Unit override fun onGroupMigrationLearnMoreClicked(membershipChange: GroupMigrationMembershipChange) = Unit override fun onChatSessionRefreshLearnMoreClicked() = Unit @@ -270,13 +276,6 @@ class ScheduledMessagesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment override fun onViewGiftBadgeClicked(messageRecord: MessageRecord) = Unit override fun onActivatePaymentsClicked() = Unit override fun onSendPaymentClicked(recipientId: RecipientId) = Unit - override fun onScheduledIndicatorClicked(view: View, messageRecord: MessageRecord) { - showScheduledMessageContextMenu(view, messageRecord) - } - } - - interface Callback { - fun getConversationAdapterListener(): ConversationAdapter.ItemClickListener } companion object { 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 efac5aa80c..ed289b7a87 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 @@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager import org.thoughtcrime.securesms.conversation.ConversationAdapter +import org.thoughtcrime.securesms.conversation.ConversationBottomSheetCallback import org.thoughtcrime.securesms.conversation.colors.Colorizer import org.thoughtcrime.securesms.conversation.colors.RecyclerViewColorizer import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart @@ -137,8 +138,8 @@ class MessageQuotesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment() { return callback } - private fun getCallback(): Callback { - return findListener() ?: throw IllegalStateException("Parent must implement callback interface!") + private fun getCallback(): ConversationBottomSheetCallback { + return findListener() ?: throw IllegalStateException("Parent must implement callback interface!") } private fun getAdapterListener(): ConversationAdapter.ItemClickListener { @@ -251,11 +252,6 @@ class MessageQuotesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment() { } } - interface Callback { - fun getConversationAdapterListener(): ConversationAdapter.ItemClickListener - fun jumpToMessage(messageRecord: MessageRecord) - } - companion object { private const val KEY_MESSAGE_ID = "message_id" private const val KEY_CONVERSATION_RECIPIENT_ID = "conversation_recipient_id" diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java index 443cf9143d..899f702a57 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java @@ -252,7 +252,7 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie "CREATE INDEX IF NOT EXISTS mms_story_type_index ON " + TABLE_NAME + " (" + STORY_TYPE + ");", "CREATE INDEX IF NOT EXISTS mms_parent_story_id_index ON " + TABLE_NAME + " (" + PARENT_STORY_ID + ");", "CREATE INDEX IF NOT EXISTS " + INDEX_THREAD_STORY_SCHEDULED_DATE + " ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + "," + STORY_TYPE + "," + PARENT_STORY_ID + "," + SCHEDULED_DATE + ");", - "CREATE INDEX IF NOT EXISTS mms_quote_id_quote_author_index ON " + TABLE_NAME + "(" + QUOTE_ID + ", " + QUOTE_AUTHOR + ");", + "CREATE INDEX IF NOT EXISTS message_quote_id_quote_author_scheduled_date_index ON " + TABLE_NAME + " (" + QUOTE_ID + ", " + QUOTE_AUTHOR + ", " + SCHEDULED_DATE + ");", "CREATE INDEX IF NOT EXISTS mms_exported_index ON " + TABLE_NAME + " (" + EXPORTED + ");", "CREATE INDEX IF NOT EXISTS mms_id_type_payment_transactions_index ON " + TABLE_NAME + " (" + ID + "," + TYPE + ") WHERE " + TYPE + " & " + MessageTypes.SPECIAL_TYPE_PAYMENTS_NOTIFICATION + " != 0;" }; @@ -3844,8 +3844,8 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie RecipientId author = messageRecord.isOutgoing() ? Recipient.self().getId() : messageRecord.getRecipient().getId(); long timestamp = messageRecord.getDateSent(); - String where = MessageTable.QUOTE_ID + " = ? AND " + MessageTable.QUOTE_AUTHOR + " = ?"; - String[] whereArgs = SqlUtil.buildArgs(timestamp, author); + String where = MessageTable.QUOTE_ID + " = ? AND " + MessageTable.QUOTE_AUTHOR + " = ? AND " + SCHEDULED_DATE + " = ?"; + String[] whereArgs = SqlUtil.buildArgs(timestamp, author, -1); try (Cursor cursor = getReadableDatabase().query(MessageTable.TABLE_NAME, new String[]{ "1" }, where, whereArgs, null, null, null, "1")) { return cursor.moveToFirst(); @@ -3862,19 +3862,19 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie } Map byQuoteDescriptor = new HashMap<>(records.size()); - List args = new ArrayList<>(records.size()); + List args = new ArrayList<>(records.size()); for (MessageRecord record : records) { long timestamp = record.getDateSent(); RecipientId author = record.isOutgoing() ? Recipient.self().getId() : record.getRecipient().getId(); byQuoteDescriptor.put(new QuoteDescriptor(timestamp, author), record); - args.add(SqlUtil.buildArgs(timestamp, author)); + args.add(SqlUtil.buildArgs(timestamp, author, -1)); } String[] projection = new String[] { QUOTE_ID, QUOTE_AUTHOR }; - List queries = SqlUtil.buildCustomCollectionQuery(QUOTE_ID + " = ? AND " + QUOTE_AUTHOR + " = ?", args); + List queries = SqlUtil.buildCustomCollectionQuery(QUOTE_ID + " = ? AND " + QUOTE_AUTHOR + " = ? AND " + SCHEDULED_DATE + " = ?", args); Set quotedIds = new HashSet<>(); for (SqlUtil.Query query : queries) { @@ -3930,7 +3930,7 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie } RecipientId author = targetMessage.isOutgoing() ? Recipient.self().getId() : targetMessage.getRecipient().getId(); - String query = QUOTE_ID + " = " + targetMessage.getDateSent() + " AND " + QUOTE_AUTHOR + " = " + author.serialize(); + String query = QUOTE_ID + " = " + targetMessage.getDateSent() + " AND " + QUOTE_AUTHOR + " = " + author.serialize() + " AND " + SCHEDULED_DATE + " = -1"; String order = DATE_RECEIVED + " DESC"; List records = new ArrayList<>(); @@ -3959,7 +3959,7 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie public int getQuotedMessagePosition(long threadId, long quoteId, @NonNull RecipientId recipientId) { String[] projection = new String[]{ DATE_SENT, RECIPIENT_ID, REMOTE_DELETED}; String order = DATE_RECEIVED + " DESC"; - String selection = THREAD_ID + " = " + threadId + " AND " + STORY_TYPE + " = 0" + " AND " + PARENT_STORY_ID + " <= 0"; + String selection = THREAD_ID + " = " + threadId + " AND " + STORY_TYPE + " = 0" + " AND " + PARENT_STORY_ID + " <= 0 AND " + SCHEDULED_DATE + " = -1"; try (Cursor cursor = getReadableDatabase().query(TABLE_NAME, projection, selection, null, null, null, order)) { boolean isOwnNumber = Recipient.resolved(recipientId).isSelf(); @@ -3983,7 +3983,7 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie public int getMessagePositionInConversation(long threadId, long receivedTimestamp, @NonNull RecipientId recipientId) { String[] projection = new String[]{ DATE_RECEIVED, RECIPIENT_ID, REMOTE_DELETED}; String order = DATE_RECEIVED + " DESC"; - String selection = THREAD_ID + " = " + threadId + " AND " + STORY_TYPE + " = 0" + " AND " + PARENT_STORY_ID + " <= 0"; + String selection = THREAD_ID + " = " + threadId + " AND " + STORY_TYPE + " = 0" + " AND " + PARENT_STORY_ID + " <= 0 AND " + SCHEDULED_DATE + " = -1"; try (Cursor cursor = getReadableDatabase().query(TABLE_NAME, projection, selection, null, null, null, order)) { boolean isOwnNumber = Recipient.resolved(recipientId).isSelf(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index db5dc880ec..6bae6037ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V172_GroupMembershi import org.thoughtcrime.securesms.database.helpers.migration.V173_ScheduledMessagesMigration import org.thoughtcrime.securesms.database.helpers.migration.V174_ReactionForeignKeyMigration import org.thoughtcrime.securesms.database.helpers.migration.V175_FixFullTextSearchLink +import org.thoughtcrime.securesms.database.helpers.migration.V176_AddScheduledDateToQuoteIndex /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -39,7 +40,7 @@ object SignalDatabaseMigrations { val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass) - const val DATABASE_VERSION = 175 + const val DATABASE_VERSION = 176 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { @@ -150,6 +151,10 @@ object SignalDatabaseMigrations { if (oldVersion < 175) { V175_FixFullTextSearchLink.migrate(context, db, oldVersion, newVersion) } + + if (oldVersion < 176) { + V176_AddScheduledDateToQuoteIndex.migrate(context, db, oldVersion, newVersion) + } } @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V176_AddScheduledDateToQuoteIndex.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V176_AddScheduledDateToQuoteIndex.kt new file mode 100644 index 0000000000..26f10bc810 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V176_AddScheduledDateToQuoteIndex.kt @@ -0,0 +1,14 @@ +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Expand quote index to included scheduled date so they can be excluded. + */ +object V176_AddScheduledDateToQuoteIndex : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("DROP INDEX IF EXISTS mms_quote_id_quote_author_index") + db.execSQL("CREATE INDEX IF NOT EXISTS message_quote_id_quote_author_scheduled_date_index ON message (quote_id, quote_author, scheduled_date);") + } +}