Implement conversation item context menu interaction

This commit is contained in:
nielsandriesse 2021-06-07 14:04:55 +10:00
parent 4ecfd1f230
commit e1345a8774
5 changed files with 109 additions and 53 deletions

View File

@ -14,15 +14,28 @@ import kotlinx.android.synthetic.main.activity_conversation_v2.*
import kotlinx.android.synthetic.main.activity_conversation_v2_action_bar.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallback
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.mms.GlideApp
class ConversationActivityV2 : PassphraseRequiredActionBarActivity() {
private var threadID: Long = -1
private var actionMode: ActionMode? = null
private val adapter by lazy {
val cursor = DatabaseFactory.getMmsSmsDatabase(this).getConversation(threadID)
val adapter = ConversationAdapter(this, cursor) { handleLongPress(it) }
val adapter = ConversationAdapter(
this,
cursor,
onItemPress = { message, position ->
handlePress(message, position)
},
onItemLongPress = { message, position ->
handleLongPress(message, position)
}
)
adapter.setHasStableIds(true)
adapter
}
@ -84,7 +97,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity() {
}
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
return ConversationMenuHelper.onPrepareOptionsMenu(menu, menuInflater, thread, this) { onOptionsItemSelected(it) }
ConversationMenuHelper.onPrepareOptionsMenu(menu, menuInflater, thread, this) { onOptionsItemSelected(it) }
// FIXME: Make the menu respect the current app theme
super.onPrepareOptionsMenu(menu)
return true
}
// endregion
@ -98,12 +114,36 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity() {
Log.d("Loki", "Reply to message at position: $messagePosition.")
}
private fun handleLongPress(messagePosition: Int) {
private fun handlePress(message: MessageRecord, position: Int) {
val actionMode = this.actionMode
if (actionMode != null) {
adapter.toggleSelection(message, position)
val actionModeCallback = ConversationActionModeCallback(adapter, threadID, this)
actionModeCallback.updateActionModeMenu(actionMode.menu)
if (adapter.selectedItems.isEmpty()) {
actionMode.finish()
this.actionMode = null
}
}
}
private fun handleLongPress(message: MessageRecord, position: Int) {
val actionMode = this.actionMode
val actionModeCallback = ConversationActionModeCallback(adapter, threadID, this)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
startActionMode(actionModeCallback, ActionMode.TYPE_PRIMARY)
if (actionMode == null) { // Nothing should be selected if this is the case
adapter.toggleSelection(message, position)
this.actionMode = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
startActionMode(actionModeCallback, ActionMode.TYPE_PRIMARY)
} else {
startActionMode(actionModeCallback)
}
} else {
startActionMode(actionModeCallback)
adapter.toggleSelection(message, position)
actionModeCallback.updateActionModeMenu(actionMode.menu)
if (adapter.selectedItems.isEmpty()) {
actionMode.finish()
this.actionMode = null
}
}
}
// endregion

View File

@ -2,19 +2,22 @@ package org.thoughtcrime.securesms.conversation.v2
import android.content.Context
import android.database.Cursor
import android.graphics.drawable.ColorDrawable
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import network.loki.messenger.R
import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
import java.lang.IllegalStateException
class ConversationAdapter(context: Context, cursor: Cursor, private val onItemLongPress: (Int) -> Unit)
: CursorRecyclerViewAdapter<ViewHolder>(context, cursor) {
class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPress: (MessageRecord, Int) -> Unit,
private val onItemLongPress: (MessageRecord, Int) -> Unit) : CursorRecyclerViewAdapter<ViewHolder>(context, cursor) {
private val messageDB = DatabaseFactory.getMmsSmsDatabase(context)
var selectedItems = setOf<MessageRecord>()
var selectedItems = mutableSetOf<MessageRecord>()
sealed class ViewType(val rawValue: Int) {
object Visible : ViewType(0)
@ -59,9 +62,15 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemLo
when (viewHolder) {
is VisibleMessageViewHolder -> {
val view = viewHolder.view
view.background = if (selectedItems.contains(message)) {
ColorDrawable(context.resources.getColorWithID(R.color.red, context.theme))
} else {
null
}
view.bind(message)
view.setOnClickListener { onItemPress(message, viewHolder.adapterPosition) }
view.setOnLongClickListener {
onItemLongPress(viewHolder.adapterPosition)
onItemLongPress(message, viewHolder.adapterPosition)
true
}
}
@ -80,4 +89,9 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemLo
private fun getMessage(cursor: Cursor): MessageRecord? {
return messageDB.readerFor(cursor).current
}
fun toggleSelection(message: MessageRecord, position: Int) {
if (selectedItems.contains(message)) selectedItems.remove(message) else selectedItems.add(message)
notifyItemChanged(position)
}
}

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.conversation.v2
package org.thoughtcrime.securesms.conversation.v2.menus
import android.content.Context
import android.view.ActionMode
@ -7,21 +7,26 @@ import android.view.MenuItem
import network.loki.messenger.R
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.conversation.v2.ConversationAdapter
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
import java.util.*
class ConversationActionModeCallback(private val adapter: ConversationAdapter, private val threadID: Long,
private val context: Context) : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
// Prepare
val inflater = mode.menuInflater
inflater.inflate(R.menu.menu_conversation_item_action, menu)
updateActionModeMenu(menu)
return true
}
fun updateActionModeMenu(menu: Menu) {
// Prepare
val selectedItems = adapter.selectedItems
val containsControlMessage = selectedItems.any { it.isUpdate }
val hasText = selectedItems.any { it.body.isNotEmpty() }
if (selectedItems.isEmpty()) { mode.finish(); return false }
if (selectedItems.isEmpty()) { return }
val firstMessage = selectedItems.iterator().next()
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID)
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
@ -58,8 +63,6 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
// Reply
menu.findItem(R.id.menu_context_reply).isVisible =
(selectedItems.size == 1 && !firstMessage.isPending && !firstMessage.isFailed)
// Return
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu): Boolean {
@ -71,6 +74,7 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
}
override fun onDestroyActionMode(mode: ActionMode) {
adapter.selectedItems.clear()
adapter.notifyDataSetChanged()
}
}

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.conversation.v2
package org.thoughtcrime.securesms.conversation.v2.menus
import android.content.Context
import android.graphics.PorterDuff
@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.loki.utilities.getColorWithID
object ConversationMenuHelper {
fun onPrepareOptionsMenu(menu: Menu, inflater: MenuInflater, thread: Recipient, context: Context, onOptionsItemSelected: (MenuItem) -> Unit): Boolean {
fun onPrepareOptionsMenu(menu: Menu, inflater: MenuInflater, thread: Recipient, context: Context, onOptionsItemSelected: (MenuItem) -> Unit) {
// Prepare
menu.clear()
val isOpenGroup = thread.isOpenGroupRecipient
@ -62,7 +62,5 @@ object ConversationMenuHelper {
inflater.inflate(R.menu.menu_conversation_unmuted, menu)
}
// TODO: Implement search
// Return
return true
}
}

View File

@ -4,37 +4,11 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:title="@string/conversation_context__menu_message_details"
android:id="@+id/menu_context_details"
app:showAsAction="never" />
<item
android:title="@string/conversation_context__menu_delete_message"
android:id="@+id/menu_context_delete_message"
android:icon="?menu_trash_icon"
android:title="@string/conversation_context__menu_reply_to_message"
android:id="@+id/menu_context_reply"
android:icon="?menu_reply_icon"
app:showAsAction="ifRoom" />
<item
android:title="@string/conversation_context__menu_ban_user"
android:id="@+id/menu_context_ban_user"
app:showAsAction="never" />
<item
android:title="@string/conversation_context__menu_copy_text"
android:id="@+id/menu_context_copy"
android:icon="?menu_copy_icon"
app:showAsAction="ifRoom" />
<item
android:title="@string/activity_conversation_copy_public_key_button_title"
android:id="@+id/menu_context_copy_public_key"
app:showAsAction="never" />
<item
android:title="@string/conversation_context__menu_resend_message"
android:id="@+id/menu_context_resend"
app:showAsAction="never" />
<item
android:title="@string/conversation_context_image__save_attachment"
android:id="@+id/menu_context_save_attachment"
@ -42,9 +16,35 @@
app:showAsAction="ifRoom" />
<item
android:title="@string/conversation_context__menu_reply_to_message"
android:id="@+id/menu_context_reply"
android:icon="?menu_reply_icon"
android:title="@string/conversation_context__menu_delete_message"
android:id="@+id/menu_context_delete_message"
android:icon="?menu_trash_icon"
app:showAsAction="ifRoom" />
<item
android:title="@string/conversation_context__menu_copy_text"
android:id="@+id/menu_context_copy"
android:icon="?menu_copy_icon"
app:showAsAction="ifRoom" />
<item
android:title="@string/conversation_context__menu_resend_message"
android:id="@+id/menu_context_resend"
app:showAsAction="never" />
<item
android:title="@string/conversation_context__menu_message_details"
android:id="@+id/menu_context_details"
app:showAsAction="never" />
<item
android:title="@string/conversation_context__menu_ban_user"
android:id="@+id/menu_context_ban_user"
app:showAsAction="never" />
<item
android:title="@string/activity_conversation_copy_public_key_button_title"
android:id="@+id/menu_context_copy_public_key"
app:showAsAction="never" />
</menu>