diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 434fa4a3cf..10c0382b2c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -130,6 +130,7 @@ import org.thoughtcrime.securesms.conversation.v2.mention.MentionViewModel import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallback import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallbackDelegate import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper +import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageViewDelegate import org.thoughtcrime.securesms.conversation.v2.search.SearchBottomBar @@ -1311,8 +1312,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe private fun showConversationReaction(message: MessageRecord, messageView: View) { val messageContentView = when(messageView){ is VisibleMessageView -> messageView.messageContentView - else -> messageView - } + is ControlMessageView -> messageView.controlContentView + else -> null + } ?: return val messageContentBitmap = try { messageContentView.drawToBitmap() diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.kt index 1e000dd4ee..a76dead344 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.kt @@ -223,7 +223,6 @@ class ConversationReactionOverlay : FrameLayout { endScale = spaceAvailableForItem / conversationItemSnapshot.height endX += Util.halfOffsetFromScale(conversationItemSnapshot.width, endScale) * if (isMessageOnLeft) -1 else 1 endY = spaceForReactionBar - Util.halfOffsetFromScale(conversationItemSnapshot.height, endScale) - val contextMenuTop = endY + conversationItemSnapshot.height * endScale reactionBarBackgroundY = reactionBarTopPadding //getReactionBarOffsetForTouch(selectedConversationModel.getBubbleY(), contextMenuTop + Util.halfOffsetFromScale(conversationItemSnapshot.getHeight(), endScale), menuPadding, reactionBarOffset, reactionBarHeight, reactionBarTopPadding, endY); endApparentTop = endY + Util.halfOffsetFromScale(conversationItemSnapshot.height, endScale) } else { @@ -272,11 +271,17 @@ class ConversationReactionOverlay : FrameLayout { revealAnimatorSet.start() if (isWideLayout) { val scrubberRight = scrubberX + scrubberWidth - val offsetX = if (isMessageOnLeft) scrubberRight + menuPadding else scrubberX - contextMenu.getMaxWidth() - menuPadding + val offsetX = when { + isMessageOnLeft -> scrubberRight + menuPadding + else -> scrubberX - contextMenu.getMaxWidth() - menuPadding + } contextMenu.show(offsetX.toInt(), Math.min(backgroundView.y, (overlayHeight - contextMenu.getMaxHeight()).toFloat()).toInt()) } else { val contentX = if (isMessageOnLeft) scrubberHorizontalMargin.toFloat() else selectedConversationModel.bubbleX - val offsetX = if (isMessageOnLeft) contentX else -contextMenu.getMaxWidth() + contentX + bubbleWidth + val offsetX = when { + isMessageOnLeft -> contentX + else -> -contextMenu.getMaxWidth() + contentX + bubbleWidth + } val menuTop = endApparentTop + conversationItemSnapshot.height * endScale contextMenu.show(offsetX.toInt(), (menuTop + menuPadding).toInt()) } @@ -527,8 +532,12 @@ class ConversationReactionOverlay : FrameLayout { val recipient = get(context).threadDatabase().getRecipientForThreadId(message.threadId) ?: return emptyList() val userPublicKey = getLocalNumber(context)!! + + // control messages and "marked as deleted" messages can only delete + val isDeleteOnly = message.isDeleted || message.isControlMessage + // Select message - if(!message.isDeleted) { + if(!isDeleteOnly) { items += ActionItem( R.attr.menu_select_icon, R.string.select, @@ -538,15 +547,15 @@ class ConversationReactionOverlay : FrameLayout { } // Reply val canWrite = openGroup == null || openGroup.canWrite - if (canWrite && !message.isPending && !message.isFailed && !message.isOpenGroupInvitation && !message.isDeleted) { + if (canWrite && !message.isPending && !message.isFailed && !message.isOpenGroupInvitation && !isDeleteOnly) { items += ActionItem(R.attr.menu_reply_icon, R.string.reply, { handleActionItemClicked(Action.REPLY) }, R.string.AccessibilityId_reply) } // Copy message text - if (!containsControlMessage && hasText && !message.isDeleted) { + if (!containsControlMessage && hasText && !isDeleteOnly) { items += ActionItem(R.attr.menu_copy_icon, R.string.copy, { handleActionItemClicked(Action.COPY_MESSAGE) }) } // Copy Account ID - if (!recipient.isCommunityRecipient && message.isIncoming && !message.isDeleted) { + if (!recipient.isCommunityRecipient && message.isIncoming && !isDeleteOnly) { items += ActionItem(R.attr.menu_copy_icon, R.string.accountIDCopy, { handleActionItemClicked(Action.COPY_ACCOUNT_ID) }) } // Delete message @@ -555,15 +564,15 @@ class ConversationReactionOverlay : FrameLayout { R.string.AccessibilityId_deleteMessage, message.subtitle, ThemeUtil.getThemedColor(context, R.attr.danger)) } // Ban user - if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey) && !message.isDeleted) { + if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey) && !isDeleteOnly) { items += ActionItem(R.attr.menu_block_icon, R.string.banUser, { handleActionItemClicked(Action.BAN_USER) }) } // Ban and delete all - if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey) && !message.isDeleted) { + if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey) && !isDeleteOnly) { items += ActionItem(R.attr.menu_trash_icon, R.string.banDeleteAll, { handleActionItemClicked(Action.BAN_AND_DELETE_ALL) }) } // Message detail - if(!message.isDeleted) { + if(!isDeleteOnly) { items += ActionItem( R.attr.menu_info_icon, R.string.messageInfo, @@ -578,7 +587,7 @@ class ConversationReactionOverlay : FrameLayout { items += ActionItem(R.attr.menu_reply_icon, R.string.resync, { handleActionItemClicked(Action.RESYNC) }) } // Save media.. - if (message.isMms && !message.isDeleted) { + if (message.isMms && !isDeleteOnly) { // ..but only provide the save option if the there is a media attachment which has finished downloading. val mmsMessage = message as MediaMmsMessageRecord if (mmsMessage.containsMediaSlide() && !mmsMessage.isMediaPending) { @@ -591,8 +600,8 @@ class ConversationReactionOverlay : FrameLayout { } // deleted messages have no emoji reactions - backgroundView.isVisible = !message.isDeleted - foregroundView.isVisible = !message.isDeleted + backgroundView.isVisible = !isDeleteOnly + foregroundView.isVisible = !isDeleteOnly return items } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index 84323b14cb..bca6cd2791 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt @@ -235,8 +235,6 @@ class ConversationViewModel( // Refer to our figma document for info on message deletion [https://www.figma.com/design/kau6LggVcMMWmZRMibEo8F/Standardise-Message-Deletion?node-id=0-1&t=dEPcU0SZ9G2s4gh2-0] - //todo DELETION handle control messages deletion ( and make clickable ) - //todo DELETION handle multi select scenarios viewModelScope.launch(Dispatchers.IO) { @@ -304,7 +302,7 @@ class ConversationViewModel( * otherwise they will appear as a special type of message * that says something like "This message was deleted" */ - fun deletedLocally(messages: Set) { + fun deleteLocally(messages: Set) { // make sure to stop audio messages, if any messages.filterIsInstance() .mapNotNull { it.slideDeck.audioSlide } @@ -734,7 +732,7 @@ class ConversationViewModel( it.copy(deleteDeviceOnly = null) } - deletedLocally(command.messages) + deleteLocally(command.messages) } is Commands.MarkAsDeletedForEveryone -> { markAsDeletedForEveryone(command.data) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt index c633acc839..89591777be 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt @@ -6,6 +6,7 @@ import android.content.Intent import android.util.AttributeSet import android.util.Log import android.view.LayoutInflater +import android.view.View import android.widget.LinearLayout import androidx.core.content.res.ResourcesCompat import androidx.core.view.isGone @@ -54,6 +55,8 @@ class ControlMessageView : LinearLayout { @Inject lateinit var disappearingMessages: DisappearingMessages + val controlContentView: View get() = binding.controlContentView + init { layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT) } @@ -84,7 +87,7 @@ class ControlMessageView : LinearLayout { && message.expiryMode != (MessagingModuleConfiguration.shared.storage.getExpirationConfiguration(message.threadId)?.expiryMode ?: ExpiryMode.NONE) && threadRecipient?.isGroupRecipient != true - followSetting.setOnClickListener { disappearingMessages.showFollowSettingDialog(context, message) } + binding.controlContentView.setOnClickListener { disappearingMessages.showFollowSettingDialog(context, message) } } } message.isMediaSavedNotification -> { @@ -128,7 +131,7 @@ class ControlMessageView : LinearLayout { } // remove clicks by default - setOnClickListener(null) + binding.controlContentView.setOnClickListener(null) hideInfo() // handle click behaviour depending on criteria @@ -138,7 +141,7 @@ class ControlMessageView : LinearLayout { // show a dedicated privacy dialog !TextSecurePreferences.isCallNotificationsEnabled(context) -> { showInfo() - setOnClickListener { + binding.controlContentView.setOnClickListener { context.showSessionDialog { val titleTxt = context.getSubbedString( R.string.callsMissedCallFrom, @@ -165,7 +168,7 @@ class ControlMessageView : LinearLayout { // show a dedicated permission dialog !Permissions.hasAll(context, Manifest.permission.RECORD_AUDIO) -> { showInfo() - setOnClickListener { + binding.controlContentView.setOnClickListener { context.showSessionDialog { val titleTxt = context.getSubbedString( R.string.callsMissedCallFrom, @@ -201,11 +204,8 @@ class ControlMessageView : LinearLayout { binding.callView.isVisible = message.isCallLog // handle long clicked if it was passed on - //todo DELETION currently control messages lose their ability to be clickable due to the long click, like the "mised phone call" CM - Log.d("", "*** Has long click? $longPress") longPress?.let { - binding.root.setOnLongClickListener { - Log.d("", "*** Long clicking") + binding.controlContentView.setOnLongClickListener { longPress.invoke() true } diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index aeaa7338f2..162a79c027 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -552,7 +552,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } else { showMuteDialog(this) { until -> lifecycleScope.launch(Dispatchers.IO) { - Log.d("", "**** until: $until") recipientDatabase.setMuted(thread.recipient, until) withContext(Dispatchers.Main) { binding.recyclerView.adapter!!.notifyDataSetChanged() diff --git a/app/src/main/res/layout/view_control_message.xml b/app/src/main/res/layout/view_control_message.xml index 5d79f1908a..0ca39ca387 100644 --- a/app/src/main/res/layout/view_control_message.xml +++ b/app/src/main/res/layout/view_control_message.xml @@ -27,7 +27,7 @@ android:visibility="gone" app:tint="?android:textColorTertiary" tools:src="@drawable/ic_timer" - tools:visibility="visible"/> + tools:visibility="visible" /> + tools:visibility="visible" /> - - - + android:orientation="vertical"> + + + android:layout_height="wrap_content" + android:orientation="horizontal"> - + - + + + + + \ No newline at end of file