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 1f3cc38599..e9326925d4 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 @@ -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 diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt index 5361a0e8fa..9866c852c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt @@ -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(context, cursor) { +class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPress: (MessageRecord, Int) -> Unit, + private val onItemLongPress: (MessageRecord, Int) -> Unit) : CursorRecyclerViewAdapter(context, cursor) { private val messageDB = DatabaseFactory.getMmsSmsDatabase(context) - var selectedItems = setOf() + var selectedItems = mutableSetOf() 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) + } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActionModeCallback.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt similarity index 91% rename from app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActionModeCallback.kt rename to app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt index a58a88cd36..00d62efcbb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActionModeCallback.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt @@ -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() } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationMenuHelper.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt similarity index 96% rename from app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationMenuHelper.kt rename to app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt index a918faf3d4..7dc18130d7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationMenuHelper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt @@ -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 } } \ No newline at end of file diff --git a/app/src/main/res/menu/menu_conversation_item_action.xml b/app/src/main/res/menu/menu_conversation_item_action.xml index 109ca2ccb6..e9e1401837 100644 --- a/app/src/main/res/menu/menu_conversation_item_action.xml +++ b/app/src/main/res/menu/menu_conversation_item_action.xml @@ -4,37 +4,11 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> - - - - - - - - - - + + + + + + + + + +