mirror of
https://github.com/oxen-io/session-android.git
synced 2025-04-05 22:55:40 +00:00
Implement conversation item context menu interaction
This commit is contained in:
parent
4ecfd1f230
commit
e1345a8774
@ -14,15 +14,28 @@ import kotlinx.android.synthetic.main.activity_conversation_v2.*
|
|||||||
import kotlinx.android.synthetic.main.activity_conversation_v2_action_bar.*
|
import kotlinx.android.synthetic.main.activity_conversation_v2_action_bar.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
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.DatabaseFactory
|
||||||
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
|
|
||||||
class ConversationActivityV2 : PassphraseRequiredActionBarActivity() {
|
class ConversationActivityV2 : PassphraseRequiredActionBarActivity() {
|
||||||
private var threadID: Long = -1
|
private var threadID: Long = -1
|
||||||
|
private var actionMode: ActionMode? = null
|
||||||
|
|
||||||
private val adapter by lazy {
|
private val adapter by lazy {
|
||||||
val cursor = DatabaseFactory.getMmsSmsDatabase(this).getConversation(threadID)
|
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.setHasStableIds(true)
|
||||||
adapter
|
adapter
|
||||||
}
|
}
|
||||||
@ -84,7 +97,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
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
|
// endregion
|
||||||
|
|
||||||
@ -98,13 +114,37 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity() {
|
|||||||
Log.d("Loki", "Reply to message at position: $messagePosition.")
|
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)
|
val actionModeCallback = ConversationActionModeCallback(adapter, threadID, this)
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
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 (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)
|
startActionMode(actionModeCallback, ActionMode.TYPE_PRIMARY)
|
||||||
} else {
|
} else {
|
||||||
startActionMode(actionModeCallback)
|
startActionMode(actionModeCallback)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
adapter.toggleSelection(message, position)
|
||||||
|
actionModeCallback.updateActionModeMenu(actionMode.menu)
|
||||||
|
if (adapter.selectedItems.isEmpty()) {
|
||||||
|
actionMode.finish()
|
||||||
|
this.actionMode = null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
@ -2,19 +2,22 @@ package org.thoughtcrime.securesms.conversation.v2
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
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.ControlMessageView
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
||||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
|
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
|
|
||||||
class ConversationAdapter(context: Context, cursor: Cursor, private val onItemLongPress: (Int) -> Unit)
|
class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPress: (MessageRecord, Int) -> Unit,
|
||||||
: CursorRecyclerViewAdapter<ViewHolder>(context, cursor) {
|
private val onItemLongPress: (MessageRecord, Int) -> Unit) : CursorRecyclerViewAdapter<ViewHolder>(context, cursor) {
|
||||||
private val messageDB = DatabaseFactory.getMmsSmsDatabase(context)
|
private val messageDB = DatabaseFactory.getMmsSmsDatabase(context)
|
||||||
var selectedItems = setOf<MessageRecord>()
|
var selectedItems = mutableSetOf<MessageRecord>()
|
||||||
|
|
||||||
sealed class ViewType(val rawValue: Int) {
|
sealed class ViewType(val rawValue: Int) {
|
||||||
object Visible : ViewType(0)
|
object Visible : ViewType(0)
|
||||||
@ -59,9 +62,15 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemLo
|
|||||||
when (viewHolder) {
|
when (viewHolder) {
|
||||||
is VisibleMessageViewHolder -> {
|
is VisibleMessageViewHolder -> {
|
||||||
val view = viewHolder.view
|
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.bind(message)
|
||||||
|
view.setOnClickListener { onItemPress(message, viewHolder.adapterPosition) }
|
||||||
view.setOnLongClickListener {
|
view.setOnLongClickListener {
|
||||||
onItemLongPress(viewHolder.adapterPosition)
|
onItemLongPress(message, viewHolder.adapterPosition)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,4 +89,9 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemLo
|
|||||||
private fun getMessage(cursor: Cursor): MessageRecord? {
|
private fun getMessage(cursor: Cursor): MessageRecord? {
|
||||||
return messageDB.readerFor(cursor).current
|
return messageDB.readerFor(cursor).current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toggleSelection(message: MessageRecord, position: Int) {
|
||||||
|
if (selectedItems.contains(message)) selectedItems.remove(message) else selectedItems.add(message)
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.v2
|
package org.thoughtcrime.securesms.conversation.v2.menus
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.ActionMode
|
import android.view.ActionMode
|
||||||
@ -7,21 +7,26 @@ import android.view.MenuItem
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.ConversationAdapter
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class ConversationActionModeCallback(private val adapter: ConversationAdapter, private val threadID: Long,
|
class ConversationActionModeCallback(private val adapter: ConversationAdapter, private val threadID: Long,
|
||||||
private val context: Context) : ActionMode.Callback {
|
private val context: Context) : ActionMode.Callback {
|
||||||
|
|
||||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
// Prepare
|
|
||||||
val inflater = mode.menuInflater
|
val inflater = mode.menuInflater
|
||||||
inflater.inflate(R.menu.menu_conversation_item_action, menu)
|
inflater.inflate(R.menu.menu_conversation_item_action, menu)
|
||||||
|
updateActionModeMenu(menu)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateActionModeMenu(menu: Menu) {
|
||||||
|
// Prepare
|
||||||
val selectedItems = adapter.selectedItems
|
val selectedItems = adapter.selectedItems
|
||||||
val containsControlMessage = selectedItems.any { it.isUpdate }
|
val containsControlMessage = selectedItems.any { it.isUpdate }
|
||||||
val hasText = selectedItems.any { it.body.isNotEmpty() }
|
val hasText = selectedItems.any { it.body.isNotEmpty() }
|
||||||
if (selectedItems.isEmpty()) { mode.finish(); return false }
|
if (selectedItems.isEmpty()) { return }
|
||||||
val firstMessage = selectedItems.iterator().next()
|
val firstMessage = selectedItems.iterator().next()
|
||||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID)
|
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID)
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
||||||
@ -58,8 +63,6 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
|
|||||||
// Reply
|
// Reply
|
||||||
menu.findItem(R.id.menu_context_reply).isVisible =
|
menu.findItem(R.id.menu_context_reply).isVisible =
|
||||||
(selectedItems.size == 1 && !firstMessage.isPending && !firstMessage.isFailed)
|
(selectedItems.size == 1 && !firstMessage.isPending && !firstMessage.isFailed)
|
||||||
// Return
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu): Boolean {
|
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu): Boolean {
|
||||||
@ -71,6 +74,7 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyActionMode(mode: ActionMode) {
|
override fun onDestroyActionMode(mode: ActionMode) {
|
||||||
|
adapter.selectedItems.clear()
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.v2
|
package org.thoughtcrime.securesms.conversation.v2.menus
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
|||||||
|
|
||||||
object ConversationMenuHelper {
|
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
|
// Prepare
|
||||||
menu.clear()
|
menu.clear()
|
||||||
val isOpenGroup = thread.isOpenGroupRecipient
|
val isOpenGroup = thread.isOpenGroupRecipient
|
||||||
@ -62,7 +62,5 @@ object ConversationMenuHelper {
|
|||||||
inflater.inflate(R.menu.menu_conversation_unmuted, menu)
|
inflater.inflate(R.menu.menu_conversation_unmuted, menu)
|
||||||
}
|
}
|
||||||
// TODO: Implement search
|
// TODO: Implement search
|
||||||
// Return
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,37 +4,11 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:title="@string/conversation_context__menu_message_details"
|
android:title="@string/conversation_context__menu_reply_to_message"
|
||||||
android:id="@+id/menu_context_details"
|
android:id="@+id/menu_context_reply"
|
||||||
app:showAsAction="never" />
|
android:icon="?menu_reply_icon"
|
||||||
|
|
||||||
<item
|
|
||||||
android:title="@string/conversation_context__menu_delete_message"
|
|
||||||
android:id="@+id/menu_context_delete_message"
|
|
||||||
android:icon="?menu_trash_icon"
|
|
||||||
app:showAsAction="ifRoom" />
|
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
|
<item
|
||||||
android:title="@string/conversation_context_image__save_attachment"
|
android:title="@string/conversation_context_image__save_attachment"
|
||||||
android:id="@+id/menu_context_save_attachment"
|
android:id="@+id/menu_context_save_attachment"
|
||||||
@ -42,9 +16,35 @@
|
|||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:title="@string/conversation_context__menu_reply_to_message"
|
android:title="@string/conversation_context__menu_delete_message"
|
||||||
android:id="@+id/menu_context_reply"
|
android:id="@+id/menu_context_delete_message"
|
||||||
android:icon="?menu_reply_icon"
|
android:icon="?menu_trash_icon"
|
||||||
app:showAsAction="ifRoom" />
|
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>
|
</menu>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user