mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Add "Group Members" section to ConversationList search results.
This commit is contained in:
committed by
Greyson Parrelli
parent
e84c6187b9
commit
09902e5d11
@@ -33,11 +33,12 @@ import org.thoughtcrime.securesms.util.visible
|
||||
open class ContactSearchAdapter(
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: DisplaySmsTag,
|
||||
recipientListener: (View, ContactSearchData.KnownRecipient, Boolean) -> Unit,
|
||||
storyListener: (View, ContactSearchData.Story, Boolean) -> Unit,
|
||||
recipientListener: Listener<ContactSearchData.KnownRecipient>,
|
||||
storyListener: Listener<ContactSearchData.Story>,
|
||||
storyContextMenuCallbacks: StoryContextMenuCallbacks,
|
||||
expandListener: (ContactSearchData.Expand) -> Unit
|
||||
) : PagingMappingAdapter<ContactSearchKey>() {
|
||||
|
||||
init {
|
||||
registerStoryItems(this, displayCheckBox, storyListener, storyContextMenuCallbacks)
|
||||
registerKnownRecipientItems(this, displayCheckBox, displaySmsTag, recipientListener)
|
||||
@@ -49,7 +50,7 @@ open class ContactSearchAdapter(
|
||||
fun registerStoryItems(
|
||||
mappingAdapter: MappingAdapter,
|
||||
displayCheckBox: Boolean = false,
|
||||
storyListener: (View, ContactSearchData.Story, Boolean) -> Unit,
|
||||
storyListener: Listener<ContactSearchData.Story>,
|
||||
storyContextMenuCallbacks: StoryContextMenuCallbacks? = null
|
||||
) {
|
||||
mappingAdapter.registerFactory(
|
||||
@@ -62,7 +63,7 @@ open class ContactSearchAdapter(
|
||||
mappingAdapter: MappingAdapter,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: DisplaySmsTag,
|
||||
recipientListener: (View, ContactSearchData.KnownRecipient, Boolean) -> Unit
|
||||
recipientListener: Listener<ContactSearchData.KnownRecipient>
|
||||
) {
|
||||
mappingAdapter.registerFactory(
|
||||
RecipientModel::class.java,
|
||||
@@ -97,6 +98,7 @@ open class ContactSearchAdapter(
|
||||
is ContactSearchData.Message -> MessageModel(it)
|
||||
is ContactSearchData.Thread -> ThreadModel(it)
|
||||
is ContactSearchData.Empty -> EmptyModel(it)
|
||||
is ContactSearchData.GroupWithMembers -> GroupWithMembersModel(it)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -133,7 +135,7 @@ open class ContactSearchAdapter(
|
||||
private class StoryViewHolder(
|
||||
itemView: View,
|
||||
displayCheckBox: Boolean,
|
||||
onClick: (View, ContactSearchData.Story, Boolean) -> Unit,
|
||||
onClick: Listener<ContactSearchData.Story>,
|
||||
private val storyContextMenuCallbacks: StoryContextMenuCallbacks?
|
||||
) : BaseRecipientViewHolder<StoryModel, ContactSearchData.Story>(itemView, displayCheckBox, DisplaySmsTag.NEVER, onClick) {
|
||||
override fun isSelected(model: StoryModel): Boolean = model.isSelected
|
||||
@@ -265,7 +267,7 @@ open class ContactSearchAdapter(
|
||||
itemView: View,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: DisplaySmsTag,
|
||||
onClick: (View, ContactSearchData.KnownRecipient, Boolean) -> Unit
|
||||
onClick: Listener<ContactSearchData.KnownRecipient>
|
||||
) : BaseRecipientViewHolder<RecipientModel, ContactSearchData.KnownRecipient>(itemView, displayCheckBox, displaySmsTag, onClick), LetterHeaderDecoration.LetterHeaderItem {
|
||||
|
||||
private var headerLetter: String? = null
|
||||
@@ -302,7 +304,7 @@ open class ContactSearchAdapter(
|
||||
itemView: View,
|
||||
private val displayCheckBox: Boolean,
|
||||
private val displaySmsTag: DisplaySmsTag,
|
||||
val onClick: (View, D, Boolean) -> Unit
|
||||
val onClick: Listener<D>
|
||||
) : MappingViewHolder<T>(itemView) {
|
||||
|
||||
protected val avatar: AvatarImageView = itemView.findViewById(R.id.contact_photo_image)
|
||||
@@ -316,7 +318,7 @@ open class ContactSearchAdapter(
|
||||
override fun bind(model: T) {
|
||||
checkbox.visible = displayCheckBox
|
||||
checkbox.isChecked = isSelected(model)
|
||||
itemView.setOnClickListener { onClick(avatar, getData(model), isSelected(model)) }
|
||||
itemView.setOnClickListener { onClick.listen(avatar, getData(model), isSelected(model)) }
|
||||
bindLongPress(model)
|
||||
|
||||
if (payload.isNotEmpty()) {
|
||||
@@ -420,6 +422,15 @@ open class ContactSearchAdapter(
|
||||
override fun areContentsTheSame(newItem: EmptyModel): Boolean = newItem.empty == empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping Model for [ContactSearchData.GroupWithMembers]
|
||||
*/
|
||||
class GroupWithMembersModel(val groupWithMembers: ContactSearchData.GroupWithMembers) : MappingModel<GroupWithMembersModel> {
|
||||
override fun areContentsTheSame(newItem: GroupWithMembersModel): Boolean = newItem.groupWithMembers == groupWithMembers
|
||||
|
||||
override fun areItemsTheSame(newItem: GroupWithMembersModel): Boolean = newItem.groupWithMembers.contactSearchKey == groupWithMembers.contactSearchKey
|
||||
}
|
||||
|
||||
/**
|
||||
* View Holder for section headers
|
||||
*/
|
||||
@@ -439,6 +450,7 @@ open class ContactSearchAdapter(
|
||||
ContactSearchConfiguration.SectionKey.GROUP_MEMBERS -> R.string.ContactsCursorLoader_group_members
|
||||
ContactSearchConfiguration.SectionKey.CHATS -> R.string.ContactsCursorLoader__chats
|
||||
ContactSearchConfiguration.SectionKey.MESSAGES -> R.string.ContactsCursorLoader__messages
|
||||
ContactSearchConfiguration.SectionKey.GROUPS_WITH_MEMBERS -> R.string.ContactsCursorLoader_group_members
|
||||
}
|
||||
)
|
||||
|
||||
@@ -495,4 +507,8 @@ open class ContactSearchAdapter(
|
||||
IF_NOT_REGISTERED,
|
||||
NEVER
|
||||
}
|
||||
|
||||
fun interface Listener<D : ContactSearchData> {
|
||||
fun listen(view: View, data: D, isSelected: Boolean)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,18 @@ class ContactSearchConfiguration private constructor(
|
||||
override val expandConfig: ExpandConfig? = null
|
||||
) : Section(SectionKey.GROUP_MEMBERS)
|
||||
|
||||
/**
|
||||
* Includes a list of groups with members whose search name match the search query.
|
||||
* This section will only be rendered if there is a non-null, non-empty query present.
|
||||
*
|
||||
* Key: [ContactSearchKey.GroupWithMembers]
|
||||
* Data: [ContactSearchData.GroupWithMembers]
|
||||
*/
|
||||
data class GroupsWithMembers(
|
||||
override val includeHeader: Boolean = true,
|
||||
override val expandConfig: ExpandConfig? = null
|
||||
) : Section(SectionKey.GROUPS_WITH_MEMBERS)
|
||||
|
||||
data class Chats(
|
||||
val isUnreadOnly: Boolean = false,
|
||||
override val includeHeader: Boolean = true,
|
||||
@@ -119,6 +131,11 @@ class ContactSearchConfiguration private constructor(
|
||||
*/
|
||||
GROUPS,
|
||||
|
||||
/**
|
||||
* Section Key for [Section.GroupsWithMembers]
|
||||
*/
|
||||
GROUPS_WITH_MEMBERS,
|
||||
|
||||
/**
|
||||
* Arbitrary row (think new group button, username row, etc)
|
||||
*/
|
||||
@@ -207,6 +224,13 @@ class ContactSearchConfiguration private constructor(
|
||||
addSection(Section.Arbitrary(setOf(first) + rest.toSet()))
|
||||
}
|
||||
|
||||
fun groupsWithMembers(
|
||||
includeHeader: Boolean = true,
|
||||
expandConfig: ExpandConfig? = null
|
||||
) {
|
||||
addSection(Section.GroupsWithMembers(includeHeader, expandConfig))
|
||||
}
|
||||
|
||||
fun addSection(section: Section)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.os.Bundle
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import org.thoughtcrime.securesms.contacts.HeaderAction
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.search.MessageResult
|
||||
@@ -50,6 +51,16 @@ sealed class ContactSearchData(val contactSearchKey: ContactSearchKey) {
|
||||
val threadRecord: ThreadRecord
|
||||
) : ContactSearchData(ContactSearchKey.Thread(threadRecord.threadId))
|
||||
|
||||
/**
|
||||
* A row displaying a group which has members that match the given query.
|
||||
* Rows of this type are only present if the query is non-empty and non-null.
|
||||
*/
|
||||
data class GroupWithMembers(
|
||||
val query: String,
|
||||
val groupRecord: GroupRecord,
|
||||
val date: Long
|
||||
) : ContactSearchData(ContactSearchKey.GroupWithMembers(groupRecord.id))
|
||||
|
||||
/**
|
||||
* A row containing a title for a given section
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.contacts.paged
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.sharing.ShareContact
|
||||
|
||||
@@ -48,6 +49,11 @@ sealed class ContactSearchKey {
|
||||
*/
|
||||
data class Thread(val threadId: Long) : ContactSearchKey()
|
||||
|
||||
/**
|
||||
* Search key for [ContactSearchData.GroupWithMembers]
|
||||
*/
|
||||
data class GroupWithMembers(val groupId: GroupId) : ContactSearchKey()
|
||||
|
||||
/**
|
||||
* Search key for a MessageRecord
|
||||
*/
|
||||
|
||||
@@ -168,8 +168,8 @@ class ContactSearchMediator(
|
||||
fun create(
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: ContactSearchAdapter.DisplaySmsTag,
|
||||
recipientListener: (View, ContactSearchData.KnownRecipient, Boolean) -> Unit,
|
||||
storyListener: (View, ContactSearchData.Story, Boolean) -> Unit,
|
||||
recipientListener: ContactSearchAdapter.Listener<ContactSearchData.KnownRecipient>,
|
||||
storyListener: ContactSearchAdapter.Listener<ContactSearchData.Story>,
|
||||
storyContextMenuCallbacks: ContactSearchAdapter.StoryContextMenuCallbacks,
|
||||
expandListener: (ContactSearchData.Expand) -> Unit
|
||||
): PagingMappingAdapter<ContactSearchKey>
|
||||
@@ -179,8 +179,8 @@ class ContactSearchMediator(
|
||||
override fun create(
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: ContactSearchAdapter.DisplaySmsTag,
|
||||
recipientListener: (View, ContactSearchData.KnownRecipient, Boolean) -> Unit,
|
||||
storyListener: (View, ContactSearchData.Story, Boolean) -> Unit,
|
||||
recipientListener: ContactSearchAdapter.Listener<ContactSearchData.KnownRecipient>,
|
||||
storyListener: ContactSearchAdapter.Listener<ContactSearchData.Story>,
|
||||
storyContextMenuCallbacks: ContactSearchAdapter.StoryContextMenuCallbacks,
|
||||
expandListener: (ContactSearchData.Expand) -> Unit
|
||||
): PagingMappingAdapter<ContactSearchKey> {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.thoughtcrime.securesms.contacts.paged
|
||||
|
||||
import android.database.Cursor
|
||||
import org.signal.core.util.requireLong
|
||||
import org.signal.paging.PagedDataSource
|
||||
import org.thoughtcrime.securesms.contacts.paged.collections.ContactSearchCollection
|
||||
import org.thoughtcrime.securesms.contacts.paged.collections.ContactSearchIterator
|
||||
import org.thoughtcrime.securesms.contacts.paged.collections.CursorSearchIterator
|
||||
import org.thoughtcrime.securesms.contacts.paged.collections.StoriesSearchCollection
|
||||
import org.thoughtcrime.securesms.database.GroupTable
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
@@ -112,6 +114,7 @@ class ContactSearchPagedDataSource(
|
||||
is ContactSearchConfiguration.Section.GroupMembers -> getGroupMembersSearchIterator(query).getCollectionSize(section, query, null)
|
||||
is ContactSearchConfiguration.Section.Chats -> getThreadData(query, section.isUnreadOnly).getCollectionSize(section, query, null)
|
||||
is ContactSearchConfiguration.Section.Messages -> getMessageData(query).getCollectionSize(section, query, null)
|
||||
is ContactSearchConfiguration.Section.GroupsWithMembers -> getGroupsWithMembersIterator(query).getCollectionSize(section, query, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +149,7 @@ class ContactSearchPagedDataSource(
|
||||
is ContactSearchConfiguration.Section.GroupMembers -> getGroupMembersContactData(section, query, startIndex, endIndex)
|
||||
is ContactSearchConfiguration.Section.Chats -> getThreadContactData(section, query, startIndex, endIndex)
|
||||
is ContactSearchConfiguration.Section.Messages -> getMessageContactData(section, query, startIndex, endIndex)
|
||||
is ContactSearchConfiguration.Section.GroupsWithMembers -> getGroupsWithMembersContactData(section, query, startIndex, endIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +172,14 @@ class ContactSearchPagedDataSource(
|
||||
return CursorSearchIterator(contactSearchPagedDataSourceRepository.getStories(query))
|
||||
}
|
||||
|
||||
private fun getGroupsWithMembersIterator(query: String?): ContactSearchIterator<Cursor> {
|
||||
return if (query.isNullOrEmpty()) {
|
||||
CursorSearchIterator(null)
|
||||
} else {
|
||||
CursorSearchIterator(contactSearchPagedDataSourceRepository.getGroupsWithMembers(query))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRecentsSearchIterator(section: ContactSearchConfiguration.Section.Recents, query: String?): ContactSearchIterator<Cursor> {
|
||||
if (!query.isNullOrEmpty()) {
|
||||
throw IllegalArgumentException("Searching Recents is not supported")
|
||||
@@ -216,6 +228,22 @@ class ContactSearchPagedDataSource(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGroupsWithMembersContactData(section: ContactSearchConfiguration.Section.GroupsWithMembers, query: String?, startIndex: Int, endIndex: Int): List<ContactSearchData> {
|
||||
return getGroupsWithMembersIterator(query).use { records ->
|
||||
readContactData(
|
||||
records = records,
|
||||
recordsPredicate = null,
|
||||
section = section,
|
||||
startIndex = startIndex,
|
||||
endIndex = endIndex,
|
||||
recordMapper = { cursor ->
|
||||
val record = GroupTable.Reader(cursor).getCurrent()
|
||||
ContactSearchData.GroupWithMembers(query!!, record!!, cursor.requireLong(GroupTable.THREAD_DATE))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRecentsContactData(section: ContactSearchConfiguration.Section.Recents, query: String?, startIndex: Int, endIndex: Int): List<ContactSearchData> {
|
||||
return getRecentsSearchIterator(section, query).use { records ->
|
||||
readContactData(
|
||||
|
||||
@@ -84,6 +84,10 @@ open class ContactSearchPagedDataSourceRepository(
|
||||
return SignalDatabase.distributionLists.getAllListsForContactSelectionUiCursor(query, myStoryContainsQuery(query ?: ""))
|
||||
}
|
||||
|
||||
open fun getGroupsWithMembers(query: String): Cursor {
|
||||
return SignalDatabase.groups.queryGroupsByMemberName(query)
|
||||
}
|
||||
|
||||
open fun getRecipientFromDistributionListCursor(cursor: Cursor): Recipient {
|
||||
return Recipient.resolved(RecipientId.from(CursorUtil.requireLong(cursor, DistributionListTables.RECIPIENT_ID)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user