mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-23 20:48:43 +00:00
Add "contacts without threads" section to Conversation List Search.
This commit is contained in:
committed by
Greyson Parrelli
parent
09902e5d11
commit
36dfa19aec
@@ -33,24 +33,22 @@ import org.thoughtcrime.securesms.util.visible
|
|||||||
open class ContactSearchAdapter(
|
open class ContactSearchAdapter(
|
||||||
displayCheckBox: Boolean,
|
displayCheckBox: Boolean,
|
||||||
displaySmsTag: DisplaySmsTag,
|
displaySmsTag: DisplaySmsTag,
|
||||||
recipientListener: Listener<ContactSearchData.KnownRecipient>,
|
onClickCallbacks: ClickCallbacks,
|
||||||
storyListener: Listener<ContactSearchData.Story>,
|
storyContextMenuCallbacks: StoryContextMenuCallbacks
|
||||||
storyContextMenuCallbacks: StoryContextMenuCallbacks,
|
|
||||||
expandListener: (ContactSearchData.Expand) -> Unit
|
|
||||||
) : PagingMappingAdapter<ContactSearchKey>() {
|
) : PagingMappingAdapter<ContactSearchKey>() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
registerStoryItems(this, displayCheckBox, storyListener, storyContextMenuCallbacks)
|
registerStoryItems(this, displayCheckBox, onClickCallbacks::onStoryClicked, storyContextMenuCallbacks)
|
||||||
registerKnownRecipientItems(this, displayCheckBox, displaySmsTag, recipientListener)
|
registerKnownRecipientItems(this, displayCheckBox, displaySmsTag, onClickCallbacks::onKnownRecipientClicked)
|
||||||
registerHeaders(this)
|
registerHeaders(this)
|
||||||
registerExpands(this, expandListener)
|
registerExpands(this, onClickCallbacks::onExpandClicked)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun registerStoryItems(
|
fun registerStoryItems(
|
||||||
mappingAdapter: MappingAdapter,
|
mappingAdapter: MappingAdapter,
|
||||||
displayCheckBox: Boolean = false,
|
displayCheckBox: Boolean = false,
|
||||||
storyListener: Listener<ContactSearchData.Story>,
|
storyListener: OnClickedCallback<ContactSearchData.Story>,
|
||||||
storyContextMenuCallbacks: StoryContextMenuCallbacks? = null
|
storyContextMenuCallbacks: StoryContextMenuCallbacks? = null
|
||||||
) {
|
) {
|
||||||
mappingAdapter.registerFactory(
|
mappingAdapter.registerFactory(
|
||||||
@@ -63,7 +61,7 @@ open class ContactSearchAdapter(
|
|||||||
mappingAdapter: MappingAdapter,
|
mappingAdapter: MappingAdapter,
|
||||||
displayCheckBox: Boolean,
|
displayCheckBox: Boolean,
|
||||||
displaySmsTag: DisplaySmsTag,
|
displaySmsTag: DisplaySmsTag,
|
||||||
recipientListener: Listener<ContactSearchData.KnownRecipient>
|
recipientListener: OnClickedCallback<ContactSearchData.KnownRecipient>
|
||||||
) {
|
) {
|
||||||
mappingAdapter.registerFactory(
|
mappingAdapter.registerFactory(
|
||||||
RecipientModel::class.java,
|
RecipientModel::class.java,
|
||||||
@@ -135,7 +133,7 @@ open class ContactSearchAdapter(
|
|||||||
private class StoryViewHolder(
|
private class StoryViewHolder(
|
||||||
itemView: View,
|
itemView: View,
|
||||||
displayCheckBox: Boolean,
|
displayCheckBox: Boolean,
|
||||||
onClick: Listener<ContactSearchData.Story>,
|
onClick: OnClickedCallback<ContactSearchData.Story>,
|
||||||
private val storyContextMenuCallbacks: StoryContextMenuCallbacks?
|
private val storyContextMenuCallbacks: StoryContextMenuCallbacks?
|
||||||
) : BaseRecipientViewHolder<StoryModel, ContactSearchData.Story>(itemView, displayCheckBox, DisplaySmsTag.NEVER, onClick) {
|
) : BaseRecipientViewHolder<StoryModel, ContactSearchData.Story>(itemView, displayCheckBox, DisplaySmsTag.NEVER, onClick) {
|
||||||
override fun isSelected(model: StoryModel): Boolean = model.isSelected
|
override fun isSelected(model: StoryModel): Boolean = model.isSelected
|
||||||
@@ -267,7 +265,7 @@ open class ContactSearchAdapter(
|
|||||||
itemView: View,
|
itemView: View,
|
||||||
displayCheckBox: Boolean,
|
displayCheckBox: Boolean,
|
||||||
displaySmsTag: DisplaySmsTag,
|
displaySmsTag: DisplaySmsTag,
|
||||||
onClick: Listener<ContactSearchData.KnownRecipient>
|
onClick: OnClickedCallback<ContactSearchData.KnownRecipient>
|
||||||
) : BaseRecipientViewHolder<RecipientModel, ContactSearchData.KnownRecipient>(itemView, displayCheckBox, displaySmsTag, onClick), LetterHeaderDecoration.LetterHeaderItem {
|
) : BaseRecipientViewHolder<RecipientModel, ContactSearchData.KnownRecipient>(itemView, displayCheckBox, displaySmsTag, onClick), LetterHeaderDecoration.LetterHeaderItem {
|
||||||
|
|
||||||
private var headerLetter: String? = null
|
private var headerLetter: String? = null
|
||||||
@@ -304,7 +302,7 @@ open class ContactSearchAdapter(
|
|||||||
itemView: View,
|
itemView: View,
|
||||||
private val displayCheckBox: Boolean,
|
private val displayCheckBox: Boolean,
|
||||||
private val displaySmsTag: DisplaySmsTag,
|
private val displaySmsTag: DisplaySmsTag,
|
||||||
val onClick: Listener<D>
|
val onClick: OnClickedCallback<D>
|
||||||
) : MappingViewHolder<T>(itemView) {
|
) : MappingViewHolder<T>(itemView) {
|
||||||
|
|
||||||
protected val avatar: AvatarImageView = itemView.findViewById(R.id.contact_photo_image)
|
protected val avatar: AvatarImageView = itemView.findViewById(R.id.contact_photo_image)
|
||||||
@@ -318,7 +316,7 @@ open class ContactSearchAdapter(
|
|||||||
override fun bind(model: T) {
|
override fun bind(model: T) {
|
||||||
checkbox.visible = displayCheckBox
|
checkbox.visible = displayCheckBox
|
||||||
checkbox.isChecked = isSelected(model)
|
checkbox.isChecked = isSelected(model)
|
||||||
itemView.setOnClickListener { onClick.listen(avatar, getData(model), isSelected(model)) }
|
itemView.setOnClickListener { onClick.onClicked(avatar, getData(model), isSelected(model)) }
|
||||||
bindLongPress(model)
|
bindLongPress(model)
|
||||||
|
|
||||||
if (payload.isNotEmpty()) {
|
if (payload.isNotEmpty()) {
|
||||||
@@ -451,6 +449,7 @@ open class ContactSearchAdapter(
|
|||||||
ContactSearchConfiguration.SectionKey.CHATS -> R.string.ContactsCursorLoader__chats
|
ContactSearchConfiguration.SectionKey.CHATS -> R.string.ContactsCursorLoader__chats
|
||||||
ContactSearchConfiguration.SectionKey.MESSAGES -> R.string.ContactsCursorLoader__messages
|
ContactSearchConfiguration.SectionKey.MESSAGES -> R.string.ContactsCursorLoader__messages
|
||||||
ContactSearchConfiguration.SectionKey.GROUPS_WITH_MEMBERS -> R.string.ContactsCursorLoader_group_members
|
ContactSearchConfiguration.SectionKey.GROUPS_WITH_MEMBERS -> R.string.ContactsCursorLoader_group_members
|
||||||
|
ContactSearchConfiguration.SectionKey.CONTACTS_WITHOUT_THREADS -> R.string.ContactsCursorLoader_contacts
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -508,7 +507,13 @@ open class ContactSearchAdapter(
|
|||||||
NEVER
|
NEVER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun interface Listener<D : ContactSearchData> {
|
fun interface OnClickedCallback<D : ContactSearchData> {
|
||||||
fun listen(view: View, data: D, isSelected: Boolean)
|
fun onClicked(view: View, data: D, isSelected: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ClickCallbacks {
|
||||||
|
fun onStoryClicked(view: View, story: ContactSearchData.Story, isSelected: Boolean)
|
||||||
|
fun onKnownRecipientClicked(view: View, knownRecipient: ContactSearchData.KnownRecipient, isSelected: Boolean)
|
||||||
|
fun onExpandClicked(expand: ContactSearchData.Expand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ class ContactSearchConfiguration private constructor(
|
|||||||
val hasEmptyState: Boolean,
|
val hasEmptyState: Boolean,
|
||||||
val sections: List<Section>
|
val sections: List<Section>
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the configuration for a given section of content in search results.
|
||||||
|
*/
|
||||||
sealed class Section(val sectionKey: SectionKey) {
|
sealed class Section(val sectionKey: SectionKey) {
|
||||||
|
|
||||||
abstract val includeHeader: Boolean
|
abstract val includeHeader: Boolean
|
||||||
@@ -18,6 +22,10 @@ class ContactSearchConfiguration private constructor(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Distribution lists and group stories.
|
* Distribution lists and group stories.
|
||||||
|
*
|
||||||
|
* Key: [ContactSearchKey.RecipientSearchKey]
|
||||||
|
* Data: [ContactSearchData.Story]
|
||||||
|
* Model: [ContactSearchAdapter.StoryModel]
|
||||||
*/
|
*/
|
||||||
data class Stories(
|
data class Stories(
|
||||||
val groupStories: Set<ContactSearchData.Story> = emptySet(),
|
val groupStories: Set<ContactSearchData.Story> = emptySet(),
|
||||||
@@ -28,6 +36,10 @@ class ContactSearchConfiguration private constructor(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Recent contacts
|
* Recent contacts
|
||||||
|
*
|
||||||
|
* Key: [ContactSearchKey.RecipientSearchKey]
|
||||||
|
* Data: [ContactSearchData.KnownRecipient]
|
||||||
|
* Model: [ContactSearchAdapter.RecipientModel]
|
||||||
*/
|
*/
|
||||||
data class Recents(
|
data class Recents(
|
||||||
val limit: Int = 25,
|
val limit: Int = 25,
|
||||||
@@ -47,7 +59,11 @@ class ContactSearchConfiguration private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1:1 Recipients
|
* 1:1 Recipients with whom the user has started a conversation.
|
||||||
|
*
|
||||||
|
* Key: [ContactSearchKey.RecipientSearchKey]
|
||||||
|
* Data: [ContactSearchData.KnownRecipient]
|
||||||
|
* Model: [ContactSearchAdapter.RecipientModel]
|
||||||
*/
|
*/
|
||||||
data class Individuals(
|
data class Individuals(
|
||||||
val includeSelf: Boolean,
|
val includeSelf: Boolean,
|
||||||
@@ -59,6 +75,10 @@ class ContactSearchConfiguration private constructor(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Group Recipients
|
* Group Recipients
|
||||||
|
*
|
||||||
|
* Key: [ContactSearchKey.RecipientSearchKey]
|
||||||
|
* Data: [ContactSearchData.KnownRecipient]
|
||||||
|
* Model: [ContactSearchAdapter.RecipientModel]
|
||||||
*/
|
*/
|
||||||
data class Groups(
|
data class Groups(
|
||||||
val includeMms: Boolean = false,
|
val includeMms: Boolean = false,
|
||||||
@@ -71,6 +91,14 @@ class ContactSearchConfiguration private constructor(
|
|||||||
override val expandConfig: ExpandConfig? = null
|
override val expandConfig: ExpandConfig? = null
|
||||||
) : Section(SectionKey.GROUPS)
|
) : Section(SectionKey.GROUPS)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of arbitrary rows, in the order given in the builder. Usage requires
|
||||||
|
* an implementation of [ArbitraryRepository] to be passed into [ContactSearchMediator]
|
||||||
|
*
|
||||||
|
* Key: [ContactSearchKey.Arbitrary]
|
||||||
|
* Data: [ContactSearchData.Arbitrary]
|
||||||
|
* Model: To be provided by an instance of [ArbitraryRepository]
|
||||||
|
*/
|
||||||
data class Arbitrary(
|
data class Arbitrary(
|
||||||
val types: Set<String>
|
val types: Set<String>
|
||||||
) : Section(SectionKey.ARBITRARY) {
|
) : Section(SectionKey.ARBITRARY) {
|
||||||
@@ -78,6 +106,14 @@ class ContactSearchConfiguration private constructor(
|
|||||||
override val expandConfig: ExpandConfig? = null
|
override val expandConfig: ExpandConfig? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Individuals who you have not started a conversation with, but are members of shared
|
||||||
|
* groups.
|
||||||
|
*
|
||||||
|
* Key: [ContactSearchKey.RecipientSearchKey]
|
||||||
|
* Data: [ContactSearchData.KnownRecipient]
|
||||||
|
* Model: [ContactSearchAdapter.RecipientModel]
|
||||||
|
*/
|
||||||
data class GroupMembers(
|
data class GroupMembers(
|
||||||
override val includeHeader: Boolean = true,
|
override val includeHeader: Boolean = true,
|
||||||
override val expandConfig: ExpandConfig? = null
|
override val expandConfig: ExpandConfig? = null
|
||||||
@@ -89,22 +125,53 @@ class ContactSearchConfiguration private constructor(
|
|||||||
*
|
*
|
||||||
* Key: [ContactSearchKey.GroupWithMembers]
|
* Key: [ContactSearchKey.GroupWithMembers]
|
||||||
* Data: [ContactSearchData.GroupWithMembers]
|
* Data: [ContactSearchData.GroupWithMembers]
|
||||||
|
* Model: [ContactSearchAdapter.GroupWithMembersModel]
|
||||||
*/
|
*/
|
||||||
data class GroupsWithMembers(
|
data class GroupsWithMembers(
|
||||||
override val includeHeader: Boolean = true,
|
override val includeHeader: Boolean = true,
|
||||||
override val expandConfig: ExpandConfig? = null
|
override val expandConfig: ExpandConfig? = null
|
||||||
) : Section(SectionKey.GROUPS_WITH_MEMBERS)
|
) : Section(SectionKey.GROUPS_WITH_MEMBERS)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1:1 and Group chat search results, whose data contains
|
||||||
|
* a ThreadRecord. Only displayed when there is a search query.
|
||||||
|
*
|
||||||
|
* Key: [ContactSearchKey.Thread]
|
||||||
|
* Data: [ContactSearchData.Thread]
|
||||||
|
* Model: [ContactSearchAdapter.ThreadModel]
|
||||||
|
*/
|
||||||
data class Chats(
|
data class Chats(
|
||||||
val isUnreadOnly: Boolean = false,
|
val isUnreadOnly: Boolean = false,
|
||||||
override val includeHeader: Boolean = true,
|
override val includeHeader: Boolean = true,
|
||||||
override val expandConfig: ExpandConfig? = null
|
override val expandConfig: ExpandConfig? = null
|
||||||
) : Section(SectionKey.CHATS)
|
) : Section(SectionKey.CHATS)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message search results, only displayed when there
|
||||||
|
* is a search query.
|
||||||
|
*
|
||||||
|
* Key: [ContactSearchKey.Message]
|
||||||
|
* Data: [ContactSearchData.Message]
|
||||||
|
* Model: [ContactSearchAdapter.MessageModel]
|
||||||
|
*/
|
||||||
data class Messages(
|
data class Messages(
|
||||||
override val includeHeader: Boolean = true,
|
override val includeHeader: Boolean = true,
|
||||||
override val expandConfig: ExpandConfig? = null
|
override val expandConfig: ExpandConfig? = null
|
||||||
) : Section(SectionKey.MESSAGES)
|
) : Section(SectionKey.MESSAGES)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contacts that the user has shared profile key data with or
|
||||||
|
* that exist in system contacts, but that do not have an associated
|
||||||
|
* thread.
|
||||||
|
*
|
||||||
|
* Key: [ContactSearchKey.RecipientSearchKey]
|
||||||
|
* Data: [ContactSearchData.KnownRecipient]
|
||||||
|
* Model: [ContactSearchAdapter.RecipientModel]
|
||||||
|
*/
|
||||||
|
data class ContactsWithoutThreads(
|
||||||
|
override val includeHeader: Boolean = true,
|
||||||
|
override val expandConfig: ExpandConfig? = null
|
||||||
|
) : Section(SectionKey.CONTACTS_WITHOUT_THREADS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,6 +203,11 @@ class ContactSearchConfiguration private constructor(
|
|||||||
*/
|
*/
|
||||||
GROUPS_WITH_MEMBERS,
|
GROUPS_WITH_MEMBERS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Section Key for [Section.ContactsWithoutThreads]
|
||||||
|
*/
|
||||||
|
CONTACTS_WITHOUT_THREADS,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arbitrary row (think new group button, username row, etc)
|
* Arbitrary row (think new group button, username row, etc)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -51,10 +51,21 @@ class ContactSearchMediator(
|
|||||||
val adapter = adapterFactory.create(
|
val adapter = adapterFactory.create(
|
||||||
displayCheckBox = displayCheckBox,
|
displayCheckBox = displayCheckBox,
|
||||||
displaySmsTag = displaySmsTag,
|
displaySmsTag = displaySmsTag,
|
||||||
recipientListener = this::toggleSelection,
|
callbacks = object : ContactSearchAdapter.ClickCallbacks {
|
||||||
storyListener = this::toggleStorySelection,
|
override fun onStoryClicked(view: View, story: ContactSearchData.Story, isSelected: Boolean) {
|
||||||
|
toggleStorySelection(view, story, isSelected)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onKnownRecipientClicked(view: View, knownRecipient: ContactSearchData.KnownRecipient, isSelected: Boolean) {
|
||||||
|
toggleSelection(view, knownRecipient, isSelected)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onExpandClicked(expand: ContactSearchData.Expand) {
|
||||||
|
viewModel.expandSection(expand.sectionKey)
|
||||||
|
}
|
||||||
|
},
|
||||||
storyContextMenuCallbacks = StoryContextMenuCallbacks()
|
storyContextMenuCallbacks = StoryContextMenuCallbacks()
|
||||||
) { viewModel.expandSection(it.sectionKey) }
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val dataAndSelection: LiveData<Pair<List<ContactSearchData>, Set<ContactSearchKey>>> = LiveDataUtil.combineLatest(
|
val dataAndSelection: LiveData<Pair<List<ContactSearchData>, Set<ContactSearchKey>>> = LiveDataUtil.combineLatest(
|
||||||
@@ -168,10 +179,8 @@ class ContactSearchMediator(
|
|||||||
fun create(
|
fun create(
|
||||||
displayCheckBox: Boolean,
|
displayCheckBox: Boolean,
|
||||||
displaySmsTag: ContactSearchAdapter.DisplaySmsTag,
|
displaySmsTag: ContactSearchAdapter.DisplaySmsTag,
|
||||||
recipientListener: ContactSearchAdapter.Listener<ContactSearchData.KnownRecipient>,
|
callbacks: ContactSearchAdapter.ClickCallbacks,
|
||||||
storyListener: ContactSearchAdapter.Listener<ContactSearchData.Story>,
|
storyContextMenuCallbacks: ContactSearchAdapter.StoryContextMenuCallbacks
|
||||||
storyContextMenuCallbacks: ContactSearchAdapter.StoryContextMenuCallbacks,
|
|
||||||
expandListener: (ContactSearchData.Expand) -> Unit
|
|
||||||
): PagingMappingAdapter<ContactSearchKey>
|
): PagingMappingAdapter<ContactSearchKey>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,12 +188,10 @@ class ContactSearchMediator(
|
|||||||
override fun create(
|
override fun create(
|
||||||
displayCheckBox: Boolean,
|
displayCheckBox: Boolean,
|
||||||
displaySmsTag: ContactSearchAdapter.DisplaySmsTag,
|
displaySmsTag: ContactSearchAdapter.DisplaySmsTag,
|
||||||
recipientListener: ContactSearchAdapter.Listener<ContactSearchData.KnownRecipient>,
|
callbacks: ContactSearchAdapter.ClickCallbacks,
|
||||||
storyListener: ContactSearchAdapter.Listener<ContactSearchData.Story>,
|
storyContextMenuCallbacks: ContactSearchAdapter.StoryContextMenuCallbacks
|
||||||
storyContextMenuCallbacks: ContactSearchAdapter.StoryContextMenuCallbacks,
|
|
||||||
expandListener: (ContactSearchData.Expand) -> Unit
|
|
||||||
): PagingMappingAdapter<ContactSearchKey> {
|
): PagingMappingAdapter<ContactSearchKey> {
|
||||||
return ContactSearchAdapter(displayCheckBox, displaySmsTag, recipientListener, storyListener, storyContextMenuCallbacks, expandListener)
|
return ContactSearchAdapter(displayCheckBox, displaySmsTag, callbacks, storyContextMenuCallbacks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ class ContactSearchPagedDataSource(
|
|||||||
is ContactSearchConfiguration.Section.Chats -> getThreadData(query, section.isUnreadOnly).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.Messages -> getMessageData(query).getCollectionSize(section, query, null)
|
||||||
is ContactSearchConfiguration.Section.GroupsWithMembers -> getGroupsWithMembersIterator(query).getCollectionSize(section, query, null)
|
is ContactSearchConfiguration.Section.GroupsWithMembers -> getGroupsWithMembersIterator(query).getCollectionSize(section, query, null)
|
||||||
|
is ContactSearchConfiguration.Section.ContactsWithoutThreads -> getContactsWithoutThreadsIterator(query).getCollectionSize(section, query, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +151,7 @@ class ContactSearchPagedDataSource(
|
|||||||
is ContactSearchConfiguration.Section.Chats -> getThreadContactData(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.Messages -> getMessageContactData(section, query, startIndex, endIndex)
|
||||||
is ContactSearchConfiguration.Section.GroupsWithMembers -> getGroupsWithMembersContactData(section, query, startIndex, endIndex)
|
is ContactSearchConfiguration.Section.GroupsWithMembers -> getGroupsWithMembersContactData(section, query, startIndex, endIndex)
|
||||||
|
is ContactSearchConfiguration.Section.ContactsWithoutThreads -> getContactsWithoutThreadsContactData(section, query, startIndex, endIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,6 +182,14 @@ class ContactSearchPagedDataSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getContactsWithoutThreadsIterator(query: String?): ContactSearchIterator<Cursor> {
|
||||||
|
return if (query.isNullOrEmpty()) {
|
||||||
|
CursorSearchIterator(null)
|
||||||
|
} else {
|
||||||
|
CursorSearchIterator(contactSearchPagedDataSourceRepository.getContactsWithoutThreads(query))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getRecentsSearchIterator(section: ContactSearchConfiguration.Section.Recents, query: String?): ContactSearchIterator<Cursor> {
|
private fun getRecentsSearchIterator(section: ContactSearchConfiguration.Section.Recents, query: String?): ContactSearchIterator<Cursor> {
|
||||||
if (!query.isNullOrEmpty()) {
|
if (!query.isNullOrEmpty()) {
|
||||||
throw IllegalArgumentException("Searching Recents is not supported")
|
throw IllegalArgumentException("Searching Recents is not supported")
|
||||||
@@ -259,6 +269,21 @@ class ContactSearchPagedDataSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getContactsWithoutThreadsContactData(section: ContactSearchConfiguration.Section.ContactsWithoutThreads, query: String?, startIndex: Int, endIndex: Int): List<ContactSearchData> {
|
||||||
|
return getContactsWithoutThreadsIterator(query).use { records ->
|
||||||
|
readContactData(
|
||||||
|
records = records,
|
||||||
|
recordsPredicate = null,
|
||||||
|
section = section,
|
||||||
|
startIndex = startIndex,
|
||||||
|
endIndex = endIndex,
|
||||||
|
recordMapper = {
|
||||||
|
ContactSearchData.KnownRecipient(section.sectionKey, contactSearchPagedDataSourceRepository.getRecipientFromRecipientCursor(it))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getNonGroupContactsData(section: ContactSearchConfiguration.Section.Individuals, query: String?, startIndex: Int, endIndex: Int): List<ContactSearchData> {
|
private fun getNonGroupContactsData(section: ContactSearchConfiguration.Section.Individuals, query: String?, startIndex: Int, endIndex: Int): List<ContactSearchData> {
|
||||||
val headerMap: Map<RecipientId, String> = if (section.includeLetterHeaders) {
|
val headerMap: Map<RecipientId, String> = if (section.includeLetterHeaders) {
|
||||||
getNonGroupHeaderLetterMap(section, query)
|
getNonGroupHeaderLetterMap(section, query)
|
||||||
@@ -274,7 +299,7 @@ class ContactSearchPagedDataSource(
|
|||||||
startIndex = startIndex,
|
startIndex = startIndex,
|
||||||
endIndex = endIndex,
|
endIndex = endIndex,
|
||||||
recordMapper = {
|
recordMapper = {
|
||||||
val recipient = contactSearchPagedDataSourceRepository.getRecipientFromRecipientCursor(it)
|
val recipient = contactSearchPagedDataSourceRepository.getRecipientFromSearchCursor(it)
|
||||||
ContactSearchData.KnownRecipient(section.sectionKey, recipient, headerLetter = headerMap[recipient.id])
|
ContactSearchData.KnownRecipient(section.sectionKey, recipient, headerLetter = headerMap[recipient.id])
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -319,7 +344,7 @@ class ContactSearchPagedDataSource(
|
|||||||
startIndex = startIndex,
|
startIndex = startIndex,
|
||||||
endIndex = endIndex,
|
endIndex = endIndex,
|
||||||
recordMapper = {
|
recordMapper = {
|
||||||
val recipient = contactSearchPagedDataSourceRepository.getRecipientFromRecipientCursor(it)
|
val recipient = contactSearchPagedDataSourceRepository.getRecipientFromSearchCursor(it)
|
||||||
val groupsInCommon = contactSearchPagedDataSourceRepository.getGroupsInCommon(recipient)
|
val groupsInCommon = contactSearchPagedDataSourceRepository.getGroupsInCommon(recipient)
|
||||||
ContactSearchData.KnownRecipient(section.sectionKey, recipient, groupsInCommon = groupsInCommon)
|
ContactSearchData.KnownRecipient(section.sectionKey, recipient, groupsInCommon = groupsInCommon)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.contacts.ContactRepository
|
|||||||
import org.thoughtcrime.securesms.contacts.paged.collections.ContactSearchIterator
|
import org.thoughtcrime.securesms.contacts.paged.collections.ContactSearchIterator
|
||||||
import org.thoughtcrime.securesms.database.DistributionListTables
|
import org.thoughtcrime.securesms.database.DistributionListTables
|
||||||
import org.thoughtcrime.securesms.database.GroupTable
|
import org.thoughtcrime.securesms.database.GroupTable
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientTable
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||||
import org.thoughtcrime.securesms.database.ThreadTable
|
import org.thoughtcrime.securesms.database.ThreadTable
|
||||||
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||||
@@ -88,6 +89,10 @@ open class ContactSearchPagedDataSourceRepository(
|
|||||||
return SignalDatabase.groups.queryGroupsByMemberName(query)
|
return SignalDatabase.groups.queryGroupsByMemberName(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun getContactsWithoutThreads(query: String): Cursor {
|
||||||
|
return SignalDatabase.recipients.getAllContactsWithoutThreads(query)
|
||||||
|
}
|
||||||
|
|
||||||
open fun getRecipientFromDistributionListCursor(cursor: Cursor): Recipient {
|
open fun getRecipientFromDistributionListCursor(cursor: Cursor): Recipient {
|
||||||
return Recipient.resolved(RecipientId.from(CursorUtil.requireLong(cursor, DistributionListTables.RECIPIENT_ID)))
|
return Recipient.resolved(RecipientId.from(CursorUtil.requireLong(cursor, DistributionListTables.RECIPIENT_ID)))
|
||||||
}
|
}
|
||||||
@@ -100,10 +105,14 @@ open class ContactSearchPagedDataSourceRepository(
|
|||||||
return Recipient.resolved(RecipientId.from(CursorUtil.requireLong(cursor, ThreadTable.RECIPIENT_ID)))
|
return Recipient.resolved(RecipientId.from(CursorUtil.requireLong(cursor, ThreadTable.RECIPIENT_ID)))
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getRecipientFromRecipientCursor(cursor: Cursor): Recipient {
|
open fun getRecipientFromSearchCursor(cursor: Cursor): Recipient {
|
||||||
return Recipient.resolved(RecipientId.from(CursorUtil.requireLong(cursor, ContactRepository.ID_COLUMN)))
|
return Recipient.resolved(RecipientId.from(CursorUtil.requireLong(cursor, ContactRepository.ID_COLUMN)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun getRecipientFromRecipientCursor(cursor: Cursor): Recipient {
|
||||||
|
return Recipient.resolved(RecipientId.from(CursorUtil.requireLong(cursor, RecipientTable.ID)))
|
||||||
|
}
|
||||||
|
|
||||||
open fun getGroupsInCommon(recipient: Recipient): GroupsInCommon {
|
open fun getGroupsInCommon(recipient: Recipient): GroupsInCommon {
|
||||||
val groupsInCommon = SignalDatabase.groups.getPushGroupsContainingMember(recipient.id)
|
val groupsInCommon = SignalDatabase.groups.getPushGroupsContainingMember(recipient.id)
|
||||||
val groupRecipientIds = groupsInCommon.take(2).map { it.recipientId }
|
val groupRecipientIds = groupsInCommon.take(2).map { it.recipientId }
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
|
|||||||
import org.thoughtcrime.securesms.components.voice.VoiceNotePlayerView;
|
import org.thoughtcrime.securesms.components.voice.VoiceNotePlayerView;
|
||||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchAdapter;
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchAdapter;
|
||||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration;
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration;
|
||||||
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchData;
|
||||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey;
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey;
|
||||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchMediator;
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchMediator;
|
||||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchState;
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchState;
|
||||||
@@ -214,6 +215,9 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||||||
private static final String TAG = Log.tag(ConversationListFragment.class);
|
private static final String TAG = Log.tag(ConversationListFragment.class);
|
||||||
|
|
||||||
private static final int MAXIMUM_PINNED_CONVERSATIONS = 4;
|
private static final int MAXIMUM_PINNED_CONVERSATIONS = 4;
|
||||||
|
private static final int MAX_CHATS_ABOVE_FOLD = 7;
|
||||||
|
private static final int MAX_CONTACTS_ABOVE_FOLD = 5;
|
||||||
|
private static final int MAX_GROUP_MEMBERSHIPS_ABOVE_FOLD = 5;
|
||||||
|
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
private View coordinator;
|
private View coordinator;
|
||||||
@@ -298,34 +302,17 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||||||
false,
|
false,
|
||||||
(displayCheckBox,
|
(displayCheckBox,
|
||||||
displaySmsTag,
|
displaySmsTag,
|
||||||
recipientListener,
|
callbacks,
|
||||||
storyListener,
|
storyContextMenuCallbacks
|
||||||
storyContextMenuCallbacks,
|
|
||||||
expandListener
|
|
||||||
) -> {
|
) -> {
|
||||||
//noinspection CodeBlock2Expr
|
//noinspection CodeBlock2Expr
|
||||||
return new ConversationListSearchAdapter(
|
return new ConversationListSearchAdapter(
|
||||||
displayCheckBox,
|
displayCheckBox,
|
||||||
displaySmsTag,
|
displaySmsTag,
|
||||||
recipientListener,
|
new ContactSearchClickCallbacks(callbacks),
|
||||||
storyListener,
|
|
||||||
storyContextMenuCallbacks,
|
storyContextMenuCallbacks,
|
||||||
expandListener,
|
|
||||||
(v, t, b) -> {
|
|
||||||
onConversationClicked(t.getThreadRecord());
|
|
||||||
},
|
|
||||||
(v, m, b) -> {
|
|
||||||
onMessageClicked(m.getMessageResult());
|
|
||||||
},
|
|
||||||
(v, m, b) -> {
|
|
||||||
onContactClicked(Recipient.resolved(m.getGroupRecord().getRecipientId()));
|
|
||||||
},
|
|
||||||
getViewLifecycleOwner(),
|
getViewLifecycleOwner(),
|
||||||
GlideApp.with(this),
|
GlideApp.with(this)
|
||||||
() -> {
|
|
||||||
onClearFilterClick();
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
new ConversationListSearchAdapter.ChatFilterRepository()
|
new ConversationListSearchAdapter.ChatFilterRepository()
|
||||||
@@ -611,7 +598,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||||||
true,
|
true,
|
||||||
new ContactSearchConfiguration.ExpandConfig(
|
new ContactSearchConfiguration.ExpandConfig(
|
||||||
state.getExpandedSections().contains(ContactSearchConfiguration.SectionKey.CHATS),
|
state.getExpandedSections().contains(ContactSearchConfiguration.SectionKey.CHATS),
|
||||||
(a) -> 7
|
(a) -> MAX_CHATS_ABOVE_FOLD
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -620,7 +607,15 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||||||
true,
|
true,
|
||||||
new ContactSearchConfiguration.ExpandConfig(
|
new ContactSearchConfiguration.ExpandConfig(
|
||||||
state.getExpandedSections().contains(ContactSearchConfiguration.SectionKey.GROUPS_WITH_MEMBERS),
|
state.getExpandedSections().contains(ContactSearchConfiguration.SectionKey.GROUPS_WITH_MEMBERS),
|
||||||
(a) -> 5
|
(a) -> MAX_GROUP_MEMBERSHIPS_ABOVE_FOLD
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
builder.addSection(new ContactSearchConfiguration.Section.ContactsWithoutThreads(
|
||||||
|
true,
|
||||||
|
new ContactSearchConfiguration.ExpandConfig(
|
||||||
|
state.getExpandedSections().contains(ContactSearchConfiguration.SectionKey.CONTACTS_WITHOUT_THREADS),
|
||||||
|
(a) -> MAX_CONTACTS_ABOVE_FOLD
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -1850,6 +1845,50 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ContactSearchClickCallbacks implements ConversationListSearchAdapter.ConversationListSearchClickCallbacks {
|
||||||
|
|
||||||
|
private final ContactSearchAdapter.ClickCallbacks delegate;
|
||||||
|
|
||||||
|
private ContactSearchClickCallbacks(@NonNull ContactSearchAdapter.ClickCallbacks delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onThreadClicked(@NonNull View view, @NonNull ContactSearchData.Thread thread, boolean isSelected) {
|
||||||
|
onConversationClicked(thread.getThreadRecord());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageClicked(@NonNull View view, @NonNull ContactSearchData.Message thread, boolean isSelected) {
|
||||||
|
ConversationListFragment.this.onMessageClicked(thread.getMessageResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGroupWithMembersClicked(@NonNull View view, @NonNull ContactSearchData.GroupWithMembers groupWithMembers, boolean isSelected) {
|
||||||
|
onContactClicked(Recipient.resolved(groupWithMembers.getGroupRecord().getRecipientId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClearFilterClicked() {
|
||||||
|
onClearFilterClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStoryClicked(@NonNull View view, @NonNull ContactSearchData.Story story, boolean isSelected) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onKnownRecipientClicked(@NonNull View view, @NonNull ContactSearchData.KnownRecipient knownRecipient, boolean isSelected) {
|
||||||
|
onContactClicked(knownRecipient.getRecipient());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExpandClicked(@NonNull ContactSearchData.Expand expand) {
|
||||||
|
delegate.onExpandClicked(expand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public interface Callback extends Material3OnScrollHelperBinder, SearchBinder {
|
public interface Callback extends Material3OnScrollHelperBinder, SearchBinder {
|
||||||
@NonNull Toolbar getToolbar();
|
@NonNull Toolbar getToolbar();
|
||||||
|
|
||||||
|
|||||||
@@ -24,33 +24,28 @@ import java.util.Locale
|
|||||||
class ConversationListSearchAdapter(
|
class ConversationListSearchAdapter(
|
||||||
displayCheckBox: Boolean,
|
displayCheckBox: Boolean,
|
||||||
displaySmsTag: DisplaySmsTag,
|
displaySmsTag: DisplaySmsTag,
|
||||||
recipientListener: Listener<ContactSearchData.KnownRecipient>,
|
onClickedCallbacks: ConversationListSearchClickCallbacks,
|
||||||
storyListener: Listener<ContactSearchData.Story>,
|
|
||||||
storyContextMenuCallbacks: StoryContextMenuCallbacks,
|
storyContextMenuCallbacks: StoryContextMenuCallbacks,
|
||||||
expandListener: (ContactSearchData.Expand) -> Unit,
|
|
||||||
threadListener: Listener<ContactSearchData.Thread>,
|
|
||||||
messageListener: Listener<ContactSearchData.Message>,
|
|
||||||
groupWithMembersListener: Listener<ContactSearchData.GroupWithMembers>,
|
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
glideRequests: GlideRequests,
|
glideRequests: GlideRequests
|
||||||
clearFilterListener: () -> Unit
|
) : ContactSearchAdapter(displayCheckBox, displaySmsTag, onClickedCallbacks, storyContextMenuCallbacks) {
|
||||||
) : ContactSearchAdapter(displayCheckBox, displaySmsTag, recipientListener, storyListener, storyContextMenuCallbacks, expandListener) {
|
|
||||||
init {
|
init {
|
||||||
registerFactory(
|
registerFactory(
|
||||||
ThreadModel::class.java,
|
ThreadModel::class.java,
|
||||||
LayoutFactory({ ThreadViewHolder(threadListener, lifecycleOwner, glideRequests, it) }, R.layout.conversation_list_item_view)
|
LayoutFactory({ ThreadViewHolder(onClickedCallbacks::onThreadClicked, lifecycleOwner, glideRequests, it) }, R.layout.conversation_list_item_view)
|
||||||
)
|
)
|
||||||
registerFactory(
|
registerFactory(
|
||||||
MessageModel::class.java,
|
MessageModel::class.java,
|
||||||
LayoutFactory({ MessageViewHolder(messageListener, lifecycleOwner, glideRequests, it) }, R.layout.conversation_list_item_view)
|
LayoutFactory({ MessageViewHolder(onClickedCallbacks::onMessageClicked, lifecycleOwner, glideRequests, it) }, R.layout.conversation_list_item_view)
|
||||||
)
|
)
|
||||||
registerFactory(
|
registerFactory(
|
||||||
ChatFilterMappingModel::class.java,
|
ChatFilterMappingModel::class.java,
|
||||||
LayoutFactory({ ChatFilterViewHolder(it, clearFilterListener) }, R.layout.conversation_list_item_clear_filter)
|
LayoutFactory({ ChatFilterViewHolder(it, onClickedCallbacks::onClearFilterClicked) }, R.layout.conversation_list_item_clear_filter)
|
||||||
)
|
)
|
||||||
registerFactory(
|
registerFactory(
|
||||||
ChatFilterEmptyMappingModel::class.java,
|
ChatFilterEmptyMappingModel::class.java,
|
||||||
LayoutFactory({ ChatFilterViewHolder(it, clearFilterListener) }, R.layout.conversation_list_item_clear_filter_empty)
|
LayoutFactory({ ChatFilterViewHolder(it, onClickedCallbacks::onClearFilterClicked) }, R.layout.conversation_list_item_clear_filter_empty)
|
||||||
)
|
)
|
||||||
registerFactory(
|
registerFactory(
|
||||||
EmptyModel::class.java,
|
EmptyModel::class.java,
|
||||||
@@ -58,7 +53,7 @@ class ConversationListSearchAdapter(
|
|||||||
)
|
)
|
||||||
registerFactory(
|
registerFactory(
|
||||||
GroupWithMembersModel::class.java,
|
GroupWithMembersModel::class.java,
|
||||||
LayoutFactory({ GroupWithMembersViewHolder(groupWithMembersListener, lifecycleOwner, glideRequests, it) }, R.layout.conversation_list_item_view)
|
LayoutFactory({ GroupWithMembersViewHolder(onClickedCallbacks::onGroupWithMembersClicked, lifecycleOwner, glideRequests, it) }, R.layout.conversation_list_item_view)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,14 +70,14 @@ class ConversationListSearchAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ThreadViewHolder(
|
private class ThreadViewHolder(
|
||||||
private val threadListener: Listener<ContactSearchData.Thread>,
|
private val threadListener: OnClickedCallback<ContactSearchData.Thread>,
|
||||||
private val lifecycleOwner: LifecycleOwner,
|
private val lifecycleOwner: LifecycleOwner,
|
||||||
private val glideRequests: GlideRequests,
|
private val glideRequests: GlideRequests,
|
||||||
itemView: View
|
itemView: View
|
||||||
) : MappingViewHolder<ThreadModel>(itemView) {
|
) : MappingViewHolder<ThreadModel>(itemView) {
|
||||||
override fun bind(model: ThreadModel) {
|
override fun bind(model: ThreadModel) {
|
||||||
itemView.setOnClickListener {
|
itemView.setOnClickListener {
|
||||||
threadListener.listen(itemView, model.thread, false)
|
threadListener.onClicked(itemView, model.thread, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
(itemView as ConversationListItem).bindThread(
|
(itemView as ConversationListItem).bindThread(
|
||||||
@@ -98,14 +93,14 @@ class ConversationListSearchAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class MessageViewHolder(
|
private class MessageViewHolder(
|
||||||
private val messageListener: Listener<ContactSearchData.Message>,
|
private val messageListener: OnClickedCallback<ContactSearchData.Message>,
|
||||||
private val lifecycleOwner: LifecycleOwner,
|
private val lifecycleOwner: LifecycleOwner,
|
||||||
private val glideRequests: GlideRequests,
|
private val glideRequests: GlideRequests,
|
||||||
itemView: View
|
itemView: View
|
||||||
) : MappingViewHolder<MessageModel>(itemView) {
|
) : MappingViewHolder<MessageModel>(itemView) {
|
||||||
override fun bind(model: MessageModel) {
|
override fun bind(model: MessageModel) {
|
||||||
itemView.setOnClickListener {
|
itemView.setOnClickListener {
|
||||||
messageListener.listen(itemView, model.message, false)
|
messageListener.onClicked(itemView, model.message, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
(itemView as ConversationListItem).bindMessage(
|
(itemView as ConversationListItem).bindMessage(
|
||||||
@@ -119,14 +114,14 @@ class ConversationListSearchAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class GroupWithMembersViewHolder(
|
private class GroupWithMembersViewHolder(
|
||||||
private val groupWithMembersListener: Listener<ContactSearchData.GroupWithMembers>,
|
private val groupWithMembersListener: OnClickedCallback<ContactSearchData.GroupWithMembers>,
|
||||||
private val lifecycleOwner: LifecycleOwner,
|
private val lifecycleOwner: LifecycleOwner,
|
||||||
private val glideRequests: GlideRequests,
|
private val glideRequests: GlideRequests,
|
||||||
itemView: View
|
itemView: View
|
||||||
) : MappingViewHolder<GroupWithMembersModel>(itemView) {
|
) : MappingViewHolder<GroupWithMembersModel>(itemView) {
|
||||||
override fun bind(model: GroupWithMembersModel) {
|
override fun bind(model: GroupWithMembersModel) {
|
||||||
itemView.setOnClickListener {
|
itemView.setOnClickListener {
|
||||||
groupWithMembersListener.listen(itemView, model.groupWithMembers, false)
|
groupWithMembersListener.onClicked(itemView, model.groupWithMembers, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
(itemView as ConversationListItem).bindGroupWithMembers(
|
(itemView as ConversationListItem).bindGroupWithMembers(
|
||||||
@@ -197,4 +192,11 @@ class ConversationListSearchAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ConversationListSearchClickCallbacks : ClickCallbacks {
|
||||||
|
fun onThreadClicked(view: View, thread: ContactSearchData.Thread, isSelected: Boolean)
|
||||||
|
fun onMessageClicked(view: View, thread: ContactSearchData.Message, isSelected: Boolean)
|
||||||
|
fun onGroupWithMembersClicked(view: View, groupWithMembers: ContactSearchData.GroupWithMembers, isSelected: Boolean)
|
||||||
|
fun onClearFilterClicked()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3201,6 +3201,24 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
return SqlUtil.Query(subquery, SqlUtil.buildArgs(0, 0, query, query, query, query))
|
return SqlUtil.Query(subquery, SqlUtil.buildArgs(0, 0, query, query, query, query))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAllContactsWithoutThreads(inputQuery: String): Cursor {
|
||||||
|
val query = SqlUtil.buildCaseInsensitiveGlobPattern(inputQuery)
|
||||||
|
|
||||||
|
//language=sql
|
||||||
|
val subquery = """
|
||||||
|
SELECT ${SEARCH_PROJECTION.joinToString(", ")} FROM $TABLE_NAME
|
||||||
|
WHERE $BLOCKED = ? AND $HIDDEN = ? AND NOT EXISTS (SELECT 1 FROM ${ThreadTable.TABLE_NAME} WHERE ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} = $TABLE_NAME.$ID LIMIT 1)
|
||||||
|
AND (
|
||||||
|
$SORT_NAME GLOB ? OR
|
||||||
|
$USERNAME GLOB ? OR
|
||||||
|
$PHONE GLOB ? OR
|
||||||
|
$EMAIL GLOB ?
|
||||||
|
)
|
||||||
|
""".toSingleLine()
|
||||||
|
|
||||||
|
return readableDatabase.query(subquery, SqlUtil.buildArgs(0, 0, query, query, query, query))
|
||||||
|
}
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun queryRecipientsForMentions(inputQuery: String, recipientIds: List<RecipientId>? = null): List<Recipient> {
|
fun queryRecipientsForMentions(inputQuery: String, recipientIds: List<RecipientId>? = null): List<Recipient> {
|
||||||
val query = SqlUtil.buildCaseInsensitiveGlobPattern(inputQuery)
|
val query = SqlUtil.buildCaseInsensitiveGlobPattern(inputQuery)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class ContactSearchPagedDataSourceTest {
|
|||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
whenever(repository.getRecipientFromGroupRecord(any())).thenReturn(Recipient.UNKNOWN)
|
whenever(repository.getRecipientFromGroupRecord(any())).thenReturn(Recipient.UNKNOWN)
|
||||||
whenever(repository.getRecipientFromRecipientCursor(cursor)).thenReturn(Recipient.UNKNOWN)
|
whenever(repository.getRecipientFromSearchCursor(cursor)).thenReturn(Recipient.UNKNOWN)
|
||||||
whenever(repository.getRecipientFromThreadCursor(cursor)).thenReturn(Recipient.UNKNOWN)
|
whenever(repository.getRecipientFromThreadCursor(cursor)).thenReturn(Recipient.UNKNOWN)
|
||||||
whenever(repository.getRecipientFromDistributionListCursor(cursor)).thenReturn(Recipient.UNKNOWN)
|
whenever(repository.getRecipientFromDistributionListCursor(cursor)).thenReturn(Recipient.UNKNOWN)
|
||||||
whenever(repository.getPrivacyModeFromDistributionListCursor(cursor)).thenReturn(DistributionListPrivacyMode.ALL)
|
whenever(repository.getPrivacyModeFromDistributionListCursor(cursor)).thenReturn(DistributionListPrivacyMode.ALL)
|
||||||
|
|||||||
Reference in New Issue
Block a user