mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Add new call screen for calls tab.
This commit is contained in:
committed by
Greyson Parrelli
parent
1210b2af0f
commit
ce3770a0fb
@@ -1,5 +1,7 @@
|
||||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public final class ContactSelectionDisplayMode {
|
||||
public static final int FLAG_PUSH = 1;
|
||||
public static final int FLAG_SMS = 1 << 1;
|
||||
@@ -11,5 +13,50 @@ public final class ContactSelectionDisplayMode {
|
||||
public static final int FLAG_HIDE_NEW = 1 << 6;
|
||||
public static final int FLAG_HIDE_RECENT_HEADER = 1 << 7;
|
||||
public static final int FLAG_GROUPS_AFTER_CONTACTS = 1 << 8;
|
||||
|
||||
public static final int FLAG_GROUP_MEMBERS = 1 << 9;
|
||||
public static final int FLAG_ALL = FLAG_PUSH | FLAG_SMS | FLAG_ACTIVE_GROUPS | FLAG_INACTIVE_GROUPS | FLAG_SELF;
|
||||
|
||||
public static Builder all() {
|
||||
return new Builder(FLAG_ALL);
|
||||
}
|
||||
|
||||
public static Builder none() {
|
||||
return new Builder(0);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
int displayMode = 0;
|
||||
|
||||
public Builder(int displayMode) {
|
||||
this.displayMode = displayMode;
|
||||
}
|
||||
|
||||
public @NonNull Builder withPush() {
|
||||
displayMode = setFlag(displayMode, FLAG_PUSH);
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull Builder withActiveGroups() {
|
||||
displayMode = setFlag(displayMode, FLAG_ACTIVE_GROUPS);
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull Builder withGroupMembers() {
|
||||
displayMode = setFlag(displayMode, FLAG_GROUP_MEMBERS);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int build() {
|
||||
return displayMode;
|
||||
}
|
||||
|
||||
private static int setFlag(int displayMode, int flag) {
|
||||
return displayMode | flag;
|
||||
}
|
||||
|
||||
private static int clearFlag(int displayMode, int flag) {
|
||||
return displayMode & ~flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,20 +37,19 @@ import org.thoughtcrime.securesms.util.visible
|
||||
open class ContactSearchAdapter(
|
||||
private val context: Context,
|
||||
fixedContacts: Set<ContactSearchKey>,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: DisplaySmsTag,
|
||||
displaySecondaryInformation: DisplaySecondaryInformation,
|
||||
displayOptions: DisplayOptions,
|
||||
onClickCallbacks: ClickCallbacks,
|
||||
longClickCallbacks: LongClickCallbacks,
|
||||
storyContextMenuCallbacks: StoryContextMenuCallbacks
|
||||
storyContextMenuCallbacks: StoryContextMenuCallbacks,
|
||||
callButtonClickCallbacks: CallButtonClickCallbacks
|
||||
) : PagingMappingAdapter<ContactSearchKey>(), FastScrollAdapter {
|
||||
|
||||
init {
|
||||
registerStoryItems(this, displayCheckBox, onClickCallbacks::onStoryClicked, storyContextMenuCallbacks)
|
||||
registerKnownRecipientItems(this, fixedContacts, displayCheckBox, displaySmsTag, displaySecondaryInformation, onClickCallbacks::onKnownRecipientClicked, longClickCallbacks::onKnownRecipientLongClick)
|
||||
registerStoryItems(this, displayOptions.displayCheckBox, onClickCallbacks::onStoryClicked, storyContextMenuCallbacks)
|
||||
registerKnownRecipientItems(this, fixedContacts, displayOptions, onClickCallbacks::onKnownRecipientClicked, longClickCallbacks::onKnownRecipientLongClick, callButtonClickCallbacks)
|
||||
registerHeaders(this)
|
||||
registerExpands(this, onClickCallbacks::onExpandClicked)
|
||||
registerFactory(UnknownRecipientModel::class.java, LayoutFactory({ UnknownRecipientViewHolder(it, onClickCallbacks::onUnknownRecipientClicked, displayCheckBox) }, R.layout.contact_search_unknown_item))
|
||||
registerFactory(UnknownRecipientModel::class.java, LayoutFactory({ UnknownRecipientViewHolder(it, onClickCallbacks::onUnknownRecipientClicked, displayOptions.displayCheckBox) }, R.layout.contact_search_unknown_item))
|
||||
}
|
||||
|
||||
override fun getBubbleText(position: Int): CharSequence {
|
||||
@@ -82,15 +81,16 @@ open class ContactSearchAdapter(
|
||||
fun registerKnownRecipientItems(
|
||||
mappingAdapter: MappingAdapter,
|
||||
fixedContacts: Set<ContactSearchKey>,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: DisplaySmsTag,
|
||||
displaySecondaryInformation: DisplaySecondaryInformation,
|
||||
displayOptions: DisplayOptions,
|
||||
recipientListener: OnClickedCallback<ContactSearchData.KnownRecipient>,
|
||||
recipientLongClickCallback: OnLongClickedCallback<ContactSearchData.KnownRecipient>
|
||||
recipientLongClickCallback: OnLongClickedCallback<ContactSearchData.KnownRecipient>,
|
||||
recipientCallButtonClickCallbacks: CallButtonClickCallbacks
|
||||
) {
|
||||
mappingAdapter.registerFactory(
|
||||
RecipientModel::class.java,
|
||||
LayoutFactory({ KnownRecipientViewHolder(it, fixedContacts, displayCheckBox, displaySmsTag, displaySecondaryInformation, recipientListener, recipientLongClickCallback) }, R.layout.contact_search_item)
|
||||
LayoutFactory({
|
||||
KnownRecipientViewHolder(it, fixedContacts, displayOptions, recipientListener, recipientLongClickCallback, recipientCallButtonClickCallbacks)
|
||||
}, R.layout.contact_search_item)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ open class ContactSearchAdapter(
|
||||
displayCheckBox: Boolean,
|
||||
onClick: OnClickedCallback<ContactSearchData.Story>,
|
||||
private val storyContextMenuCallbacks: StoryContextMenuCallbacks?
|
||||
) : BaseRecipientViewHolder<StoryModel, ContactSearchData.Story>(itemView, displayCheckBox, DisplaySmsTag.NEVER, onClick) {
|
||||
) : BaseRecipientViewHolder<StoryModel, ContactSearchData.Story>(itemView, DisplayOptions(displayCheckBox = displayCheckBox), onClick, EmptyCallButtonClickCallbacks) {
|
||||
override fun isSelected(model: StoryModel): Boolean = model.isSelected
|
||||
override fun getData(model: StoryModel): ContactSearchData.Story = model.story
|
||||
override fun getRecipient(model: StoryModel): Recipient = model.story.recipient
|
||||
@@ -334,6 +334,7 @@ open class ContactSearchAdapter(
|
||||
checkbox.isSelected = false
|
||||
name.setText(
|
||||
when (model.data.mode) {
|
||||
ContactSearchConfiguration.NewRowMode.NEW_CALL -> R.string.contact_selection_list__new_call
|
||||
ContactSearchConfiguration.NewRowMode.NEW_CONVERSATION -> R.string.contact_selection_list__unknown_contact
|
||||
ContactSearchConfiguration.NewRowMode.BLOCK -> R.string.contact_selection_list__unknown_contact_block
|
||||
ContactSearchConfiguration.NewRowMode.ADD_TO_GROUP -> R.string.contact_selection_list__unknown_contact_add_to_group
|
||||
@@ -349,12 +350,11 @@ open class ContactSearchAdapter(
|
||||
private class KnownRecipientViewHolder(
|
||||
itemView: View,
|
||||
private val fixedContacts: Set<ContactSearchKey>,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: DisplaySmsTag,
|
||||
private val displaySecondaryInformation: DisplaySecondaryInformation,
|
||||
displayOptions: DisplayOptions,
|
||||
onClick: OnClickedCallback<ContactSearchData.KnownRecipient>,
|
||||
private val onLongClick: OnLongClickedCallback<ContactSearchData.KnownRecipient>
|
||||
) : BaseRecipientViewHolder<RecipientModel, ContactSearchData.KnownRecipient>(itemView, displayCheckBox, displaySmsTag, onClick), LetterHeaderDecoration.LetterHeaderItem {
|
||||
private val onLongClick: OnLongClickedCallback<ContactSearchData.KnownRecipient>,
|
||||
callButtonClickCallbacks: CallButtonClickCallbacks
|
||||
) : BaseRecipientViewHolder<RecipientModel, ContactSearchData.KnownRecipient>(itemView, displayOptions, onClick, callButtonClickCallbacks), LetterHeaderDecoration.LetterHeaderItem {
|
||||
|
||||
private var headerLetter: String? = null
|
||||
|
||||
@@ -370,10 +370,10 @@ open class ContactSearchAdapter(
|
||||
val count = recipient.participantIds.size
|
||||
number.text = context.resources.getQuantityString(R.plurals.ContactSearchItems__group_d_members, count, count)
|
||||
number.visible = true
|
||||
} else if (displaySecondaryInformation == DisplaySecondaryInformation.ALWAYS && recipient.combinedAboutAndEmoji != null) {
|
||||
} else if (displayOptions.displaySecondaryInformation == DisplaySecondaryInformation.ALWAYS && recipient.combinedAboutAndEmoji != null) {
|
||||
number.text = recipient.combinedAboutAndEmoji
|
||||
number.visible = true
|
||||
} else if (displaySecondaryInformation == DisplaySecondaryInformation.ALWAYS && recipient.hasE164()) {
|
||||
} else if (displayOptions.displaySecondaryInformation == DisplaySecondaryInformation.ALWAYS && recipient.hasE164()) {
|
||||
number.text = PhoneNumberFormatter.prettyPrint(recipient.requireE164())
|
||||
number.visible = true
|
||||
} else {
|
||||
@@ -410,9 +410,9 @@ open class ContactSearchAdapter(
|
||||
*/
|
||||
abstract class BaseRecipientViewHolder<T : MappingModel<T>, D : ContactSearchData>(
|
||||
itemView: View,
|
||||
private val displayCheckBox: Boolean,
|
||||
private val displaySmsTag: DisplaySmsTag,
|
||||
val onClick: OnClickedCallback<D>
|
||||
val displayOptions: DisplayOptions,
|
||||
val onClick: OnClickedCallback<D>,
|
||||
val onCallButtonClickCallbacks: CallButtonClickCallbacks
|
||||
) : MappingViewHolder<T>(itemView) {
|
||||
|
||||
protected val avatar: AvatarImageView = itemView.findViewById(R.id.contact_photo_image)
|
||||
@@ -422,6 +422,8 @@ open class ContactSearchAdapter(
|
||||
protected val number: TextView = itemView.findViewById(R.id.number)
|
||||
protected val label: TextView = itemView.findViewById(R.id.label)
|
||||
protected val smsTag: View = itemView.findViewById(R.id.sms_tag)
|
||||
private val startAudio: View = itemView.findViewById(R.id.start_audio)
|
||||
private val startVideo: View = itemView.findViewById(R.id.start_video)
|
||||
|
||||
override fun bind(model: T) {
|
||||
if (isEnabled(model)) {
|
||||
@@ -442,10 +444,11 @@ open class ContactSearchAdapter(
|
||||
bindNumberField(model)
|
||||
bindLabelField(model)
|
||||
bindSmsTagField(model)
|
||||
bindCallButtons(model)
|
||||
}
|
||||
|
||||
protected open fun bindCheckbox(model: T) {
|
||||
checkbox.visible = displayCheckBox
|
||||
checkbox.visible = displayOptions.displayCheckBox
|
||||
checkbox.isChecked = isSelected(model)
|
||||
}
|
||||
|
||||
@@ -476,7 +479,7 @@ open class ContactSearchAdapter(
|
||||
}
|
||||
|
||||
protected open fun bindSmsTagField(model: T) {
|
||||
smsTag.visible = when (displaySmsTag) {
|
||||
smsTag.visible = when (displayOptions.displaySmsTag) {
|
||||
DisplaySmsTag.DEFAULT -> isSmsContact(model)
|
||||
DisplaySmsTag.IF_NOT_REGISTERED -> isNotRegistered(model)
|
||||
DisplaySmsTag.NEVER -> false
|
||||
@@ -485,6 +488,25 @@ open class ContactSearchAdapter(
|
||||
|
||||
protected open fun bindLongPress(model: T) = Unit
|
||||
|
||||
private fun bindCallButtons(model: T) {
|
||||
val recipient = getRecipient(model)
|
||||
if (displayOptions.displayCallButtons && (recipient.isPushGroup || recipient.isRegistered)) {
|
||||
startVideo.visible = true
|
||||
startAudio.visible = !recipient.isPushGroup
|
||||
|
||||
startVideo.setOnClickListener {
|
||||
onCallButtonClickCallbacks.onVideoCallButtonClicked(recipient)
|
||||
}
|
||||
|
||||
startAudio.setOnClickListener {
|
||||
onCallButtonClickCallbacks.onAudioCallButtonClicked(recipient)
|
||||
}
|
||||
} else {
|
||||
startVideo.visible = false
|
||||
startAudio.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSmsContact(model: T): Boolean {
|
||||
return (getRecipient(model).isForceSmsSelection || getRecipient(model).isUnregistered) && !getRecipient(model).isDistributionList
|
||||
}
|
||||
@@ -635,6 +657,13 @@ open class ContactSearchAdapter(
|
||||
ALWAYS
|
||||
}
|
||||
|
||||
data class DisplayOptions(
|
||||
val displayCheckBox: Boolean = false,
|
||||
val displaySmsTag: DisplaySmsTag = DisplaySmsTag.NEVER,
|
||||
val displaySecondaryInformation: DisplaySecondaryInformation = DisplaySecondaryInformation.NEVER,
|
||||
val displayCallButtons: Boolean = false
|
||||
)
|
||||
|
||||
fun interface OnClickedCallback<D : ContactSearchData> {
|
||||
fun onClicked(view: View, data: D, isSelected: Boolean)
|
||||
}
|
||||
@@ -652,6 +681,16 @@ open class ContactSearchAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
interface CallButtonClickCallbacks {
|
||||
fun onVideoCallButtonClicked(recipient: Recipient)
|
||||
fun onAudioCallButtonClicked(recipient: Recipient)
|
||||
}
|
||||
|
||||
object EmptyCallButtonClickCallbacks : CallButtonClickCallbacks {
|
||||
override fun onVideoCallButtonClicked(recipient: Recipient) = Unit
|
||||
override fun onAudioCallButtonClicked(recipient: Recipient) = Unit
|
||||
}
|
||||
|
||||
interface LongClickCallbacks {
|
||||
fun onKnownRecipientLongClick(view: View, data: ContactSearchData.KnownRecipient): Boolean
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import org.thoughtcrime.securesms.contacts.HeaderAction
|
||||
*/
|
||||
class ContactSearchConfiguration private constructor(
|
||||
val query: String?,
|
||||
val hasEmptyState: Boolean,
|
||||
val sections: List<Section>
|
||||
val sections: List<Section>,
|
||||
val emptyStateSections: List<Section>
|
||||
) {
|
||||
|
||||
/**
|
||||
@@ -20,6 +20,14 @@ class ContactSearchConfiguration private constructor(
|
||||
open val headerAction: HeaderAction? = null
|
||||
abstract val expandConfig: ExpandConfig?
|
||||
|
||||
/**
|
||||
* Section representing the "extra" item.
|
||||
*/
|
||||
object Empty : Section(SectionKey.EMPTY) {
|
||||
override val includeHeader: Boolean = false
|
||||
override val expandConfig: ExpandConfig? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Distribution lists and group stories.
|
||||
*
|
||||
@@ -188,6 +196,11 @@ class ContactSearchConfiguration private constructor(
|
||||
* Describes a given section. Useful for labeling sections and managing expansion state.
|
||||
*/
|
||||
enum class SectionKey {
|
||||
/**
|
||||
* A generic empty item
|
||||
*/
|
||||
EMPTY,
|
||||
|
||||
/**
|
||||
* Lists My Stories, distribution lists, as well as group stories.
|
||||
*/
|
||||
@@ -271,6 +284,7 @@ class ContactSearchConfiguration private constructor(
|
||||
* Describes the mode for 'Username' or 'PhoneNumber'
|
||||
*/
|
||||
enum class NewRowMode {
|
||||
NEW_CALL,
|
||||
NEW_CONVERSATION,
|
||||
BLOCK,
|
||||
ADD_TO_GROUP
|
||||
@@ -296,21 +310,47 @@ class ContactSearchConfiguration private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal builder class with build method.
|
||||
*/
|
||||
private class ConfigurationBuilder : Builder {
|
||||
private class EmptyStateBuilder : Builder {
|
||||
private val sections: MutableList<Section> = mutableListOf()
|
||||
|
||||
override var query: String? = null
|
||||
override var hasEmptyState: Boolean = false
|
||||
|
||||
override fun addSection(section: Section) {
|
||||
sections.add(section)
|
||||
}
|
||||
|
||||
override fun withEmptyState(emptyStateBuilderFn: Builder.() -> Unit) {
|
||||
error("Unsupported operation: Already in empty state.")
|
||||
}
|
||||
|
||||
fun build(): List<Section> {
|
||||
return sections
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal builder class with build method.
|
||||
*/
|
||||
private class ConfigurationBuilder : Builder {
|
||||
private val sections: MutableList<Section> = mutableListOf()
|
||||
private val emptyState = EmptyStateBuilder()
|
||||
|
||||
override var query: String? = null
|
||||
|
||||
override fun addSection(section: Section) {
|
||||
sections.add(section)
|
||||
}
|
||||
|
||||
override fun withEmptyState(emptyStateBuilderFn: Builder.() -> Unit) {
|
||||
emptyState.emptyStateBuilderFn()
|
||||
}
|
||||
|
||||
fun build(): ContactSearchConfiguration {
|
||||
return ContactSearchConfiguration(query, hasEmptyState, sections)
|
||||
return ContactSearchConfiguration(
|
||||
query = query,
|
||||
sections = sections,
|
||||
emptyStateSections = emptyState.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +359,6 @@ class ContactSearchConfiguration private constructor(
|
||||
*/
|
||||
interface Builder {
|
||||
var query: String?
|
||||
var hasEmptyState: Boolean
|
||||
|
||||
fun arbitrary(first: String, vararg rest: String) {
|
||||
addSection(Section.Arbitrary(setOf(first) + rest.toSet()))
|
||||
@@ -333,6 +372,8 @@ class ContactSearchConfiguration private constructor(
|
||||
addSection(Section.PhoneNumber(newRowMode))
|
||||
}
|
||||
|
||||
fun withEmptyState(emptyStateBuilderFn: Builder.() -> Unit)
|
||||
|
||||
fun addSection(section: Section)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,7 @@ class ContactSearchMediator(
|
||||
private val fragment: Fragment,
|
||||
private val fixedContacts: Set<ContactSearchKey> = setOf(),
|
||||
selectionLimits: SelectionLimits,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: ContactSearchAdapter.DisplaySmsTag,
|
||||
displaySecondaryInformation: ContactSearchAdapter.DisplaySecondaryInformation,
|
||||
displayOptions: ContactSearchAdapter.DisplayOptions,
|
||||
mapStateToConfiguration: (ContactSearchState) -> ContactSearchConfiguration,
|
||||
private val callbacks: Callbacks = SimpleCallbacks(),
|
||||
performSafetyNumberChecks: Boolean = true,
|
||||
@@ -69,9 +67,7 @@ class ContactSearchMediator(
|
||||
val adapter = adapterFactory.create(
|
||||
context = fragment.requireContext(),
|
||||
fixedContacts = fixedContacts,
|
||||
displayCheckBox = displayCheckBox,
|
||||
displaySmsTag = displaySmsTag,
|
||||
displaySecondaryInformation = displaySecondaryInformation,
|
||||
displayOptions = displayOptions,
|
||||
callbacks = object : ContactSearchAdapter.ClickCallbacks {
|
||||
override fun onStoryClicked(view: View, story: ContactSearchData.Story, isSelected: Boolean) {
|
||||
toggleStorySelection(view, story, isSelected)
|
||||
@@ -86,7 +82,8 @@ class ContactSearchMediator(
|
||||
}
|
||||
},
|
||||
longClickCallbacks = ContactSearchAdapter.LongClickCallbacksAdapter(),
|
||||
storyContextMenuCallbacks = StoryContextMenuCallbacks()
|
||||
storyContextMenuCallbacks = StoryContextMenuCallbacks(),
|
||||
callButtonClickCallbacks = ContactSearchAdapter.EmptyCallButtonClickCallbacks
|
||||
)
|
||||
|
||||
init {
|
||||
@@ -230,12 +227,11 @@ class ContactSearchMediator(
|
||||
fun create(
|
||||
context: Context,
|
||||
fixedContacts: Set<ContactSearchKey>,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: ContactSearchAdapter.DisplaySmsTag,
|
||||
displaySecondaryInformation: ContactSearchAdapter.DisplaySecondaryInformation,
|
||||
displayOptions: ContactSearchAdapter.DisplayOptions,
|
||||
callbacks: ContactSearchAdapter.ClickCallbacks,
|
||||
longClickCallbacks: ContactSearchAdapter.LongClickCallbacks,
|
||||
storyContextMenuCallbacks: ContactSearchAdapter.StoryContextMenuCallbacks
|
||||
storyContextMenuCallbacks: ContactSearchAdapter.StoryContextMenuCallbacks,
|
||||
callButtonClickCallbacks: ContactSearchAdapter.CallButtonClickCallbacks
|
||||
): PagingMappingAdapter<ContactSearchKey>
|
||||
}
|
||||
|
||||
@@ -243,14 +239,13 @@ class ContactSearchMediator(
|
||||
override fun create(
|
||||
context: Context,
|
||||
fixedContacts: Set<ContactSearchKey>,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: ContactSearchAdapter.DisplaySmsTag,
|
||||
displaySecondaryInformation: ContactSearchAdapter.DisplaySecondaryInformation,
|
||||
displayOptions: ContactSearchAdapter.DisplayOptions,
|
||||
callbacks: ContactSearchAdapter.ClickCallbacks,
|
||||
longClickCallbacks: ContactSearchAdapter.LongClickCallbacks,
|
||||
storyContextMenuCallbacks: ContactSearchAdapter.StoryContextMenuCallbacks
|
||||
storyContextMenuCallbacks: ContactSearchAdapter.StoryContextMenuCallbacks,
|
||||
callButtonClickCallbacks: ContactSearchAdapter.CallButtonClickCallbacks
|
||||
): PagingMappingAdapter<ContactSearchKey> {
|
||||
return ContactSearchAdapter(context, fixedContacts, displayCheckBox, displaySmsTag, displaySecondaryInformation, callbacks, longClickCallbacks, storyContextMenuCallbacks)
|
||||
return ContactSearchAdapter(context, fixedContacts, displayOptions, callbacks, longClickCallbacks, storyContextMenuCallbacks, callButtonClickCallbacks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,32 +44,51 @@ class ContactSearchPagedDataSource(
|
||||
|
||||
private var searchCache = SearchCache()
|
||||
private var searchSize = -1
|
||||
private var displayEmptyState: Boolean = false
|
||||
|
||||
/**
|
||||
* When determining when the list is in an empty state, we ignore any arbitrary items, since in general
|
||||
* they are always present. If you'd like arbitrary items to appear even when the list is empty, ensure
|
||||
* they are added to the empty state configuration.
|
||||
*/
|
||||
override fun size(): Int {
|
||||
searchSize = contactConfiguration.sections.sumOf {
|
||||
val (arbitrarySections, nonArbitrarySections) = contactConfiguration.sections.partition {
|
||||
it is ContactSearchConfiguration.Section.Arbitrary
|
||||
}
|
||||
|
||||
val sizeOfNonArbitrarySections = nonArbitrarySections.sumOf {
|
||||
getSectionSize(it, contactConfiguration.query)
|
||||
}
|
||||
|
||||
return if (searchSize == 0 && contactConfiguration.hasEmptyState) {
|
||||
1
|
||||
displayEmptyState = sizeOfNonArbitrarySections == 0
|
||||
searchSize = if (displayEmptyState) {
|
||||
contactConfiguration.emptyStateSections.sumOf {
|
||||
getSectionSize(it, contactConfiguration.query)
|
||||
}
|
||||
} else {
|
||||
searchSize
|
||||
arbitrarySections.sumOf {
|
||||
getSectionSize(it, contactConfiguration.query)
|
||||
} + sizeOfNonArbitrarySections
|
||||
}
|
||||
|
||||
return searchSize
|
||||
}
|
||||
|
||||
override fun load(start: Int, length: Int, cancellationSignal: PagedDataSource.CancellationSignal): MutableList<ContactSearchData> {
|
||||
if (searchSize == 0 && contactConfiguration.hasEmptyState) {
|
||||
return mutableListOf(ContactSearchData.Empty(contactConfiguration.query))
|
||||
val sections: List<ContactSearchConfiguration.Section> = if (displayEmptyState) {
|
||||
contactConfiguration.emptyStateSections
|
||||
} else {
|
||||
contactConfiguration.sections
|
||||
}
|
||||
|
||||
val sizeMap: Map<ContactSearchConfiguration.Section, Int> = contactConfiguration.sections.associateWith { getSectionSize(it, contactConfiguration.query) }
|
||||
val sizeMap: Map<ContactSearchConfiguration.Section, Int> = sections.associateWith { getSectionSize(it, contactConfiguration.query) }
|
||||
val startIndex: Index = findIndex(sizeMap, start)
|
||||
val endIndex: Index = findIndex(sizeMap, start + length)
|
||||
|
||||
val indexOfStartSection = contactConfiguration.sections.indexOf(startIndex.category)
|
||||
val indexOfEndSection = contactConfiguration.sections.indexOf(endIndex.category)
|
||||
val indexOfStartSection = sections.indexOf(startIndex.category)
|
||||
val indexOfEndSection = sections.indexOf(endIndex.category)
|
||||
|
||||
val results: List<List<ContactSearchData>> = contactConfiguration.sections.mapIndexed { index, section ->
|
||||
val results: List<List<ContactSearchData>> = sections.mapIndexed { index, section ->
|
||||
if (index in indexOfStartSection..indexOfEndSection) {
|
||||
getSectionData(
|
||||
section = section,
|
||||
@@ -122,6 +141,7 @@ class ContactSearchPagedDataSource(
|
||||
is ContactSearchConfiguration.Section.ContactsWithoutThreads -> getContactsWithoutThreadsIterator(query).getCollectionSize(section, query, null)
|
||||
is ContactSearchConfiguration.Section.PhoneNumber -> if (isPossiblyPhoneNumber(query)) 1 else 0
|
||||
is ContactSearchConfiguration.Section.Username -> if (isPossiblyUsername(query)) 1 else 0
|
||||
is ContactSearchConfiguration.Section.Empty -> 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +180,7 @@ class ContactSearchPagedDataSource(
|
||||
is ContactSearchConfiguration.Section.ContactsWithoutThreads -> getContactsWithoutThreadsContactData(section, query, startIndex, endIndex)
|
||||
is ContactSearchConfiguration.Section.PhoneNumber -> getPossiblePhoneNumber(section, query)
|
||||
is ContactSearchConfiguration.Section.Username -> getPossibleUsername(section, query)
|
||||
is ContactSearchConfiguration.Section.Empty -> listOf(ContactSearchData.Empty(query))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user