mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Add indicator and story ring for stories in chat selection.
This commit is contained in:
@@ -5,10 +5,15 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckBox
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import org.signal.core.util.BreakIteratorCompat
|
||||
import org.signal.core.util.dp
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.avatar.view.AvatarView
|
||||
import org.thoughtcrime.securesms.badges.BadgeImageView
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView
|
||||
import org.thoughtcrime.securesms.components.FromTextView
|
||||
@@ -19,6 +24,7 @@ import org.thoughtcrime.securesms.contacts.LetterHeaderDecoration
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||
import org.thoughtcrime.securesms.database.model.StoryViewState
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
@@ -45,7 +51,7 @@ open class ContactSearchAdapter(
|
||||
) : PagingMappingAdapter<ContactSearchKey>(), FastScrollAdapter {
|
||||
|
||||
init {
|
||||
registerStoryItems(this, displayOptions.displayCheckBox, onClickCallbacks::onStoryClicked, storyContextMenuCallbacks)
|
||||
registerStoryItems(this, displayOptions.displayCheckBox, onClickCallbacks::onStoryClicked, storyContextMenuCallbacks, displayOptions.displayStoryRing)
|
||||
registerKnownRecipientItems(this, fixedContacts, displayOptions, onClickCallbacks::onKnownRecipientClicked, longClickCallbacks::onKnownRecipientLongClick, callButtonClickCallbacks)
|
||||
registerHeaders(this)
|
||||
registerExpands(this, onClickCallbacks::onExpandClicked)
|
||||
@@ -70,11 +76,12 @@ open class ContactSearchAdapter(
|
||||
mappingAdapter: MappingAdapter,
|
||||
displayCheckBox: Boolean = false,
|
||||
storyListener: OnClickedCallback<ContactSearchData.Story>,
|
||||
storyContextMenuCallbacks: StoryContextMenuCallbacks? = null
|
||||
storyContextMenuCallbacks: StoryContextMenuCallbacks? = null,
|
||||
showStoryRing: Boolean = false
|
||||
) {
|
||||
mappingAdapter.registerFactory(
|
||||
StoryModel::class.java,
|
||||
LayoutFactory({ StoryViewHolder(it, displayCheckBox, storyListener, storyContextMenuCallbacks) }, R.layout.contact_search_item)
|
||||
LayoutFactory({ StoryViewHolder(it, displayCheckBox, storyListener, storyContextMenuCallbacks, showStoryRing) }, R.layout.contact_search_story_item)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -158,15 +165,47 @@ open class ContactSearchAdapter(
|
||||
|
||||
private class StoryViewHolder(
|
||||
itemView: View,
|
||||
displayCheckBox: Boolean,
|
||||
onClick: OnClickedCallback<ContactSearchData.Story>,
|
||||
private val storyContextMenuCallbacks: StoryContextMenuCallbacks?
|
||||
) : 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
|
||||
val displayCheckBox: Boolean,
|
||||
val onClick: OnClickedCallback<ContactSearchData.Story>,
|
||||
private val storyContextMenuCallbacks: StoryContextMenuCallbacks?,
|
||||
private val showStoryRing: Boolean = false
|
||||
) : MappingViewHolder<StoryModel>(itemView) {
|
||||
|
||||
override fun bindNumberField(model: StoryModel) {
|
||||
val avatar: AvatarView = itemView.findViewById(R.id.contact_photo_image)
|
||||
val badge: BadgeImageView = itemView.findViewById(R.id.contact_badge)
|
||||
val checkbox: CheckBox = itemView.findViewById(R.id.check_box)
|
||||
val name: FromTextView = itemView.findViewById(R.id.name)
|
||||
val number: TextView = itemView.findViewById(R.id.number)
|
||||
val groupStoryIndicator: AppCompatImageView = itemView.findViewById(R.id.group_story_indicator)
|
||||
var storyViewState: Observable<StoryViewState>? = null
|
||||
var storyDisposable: Disposable? = null
|
||||
|
||||
override fun bind(model: StoryModel) {
|
||||
itemView.setOnClickListener { onClick.onClicked(avatar, getData(model), isSelected(model)) }
|
||||
bindLongPress(model)
|
||||
|
||||
bindCheckbox(model)
|
||||
|
||||
if (payload.isNotEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
storyViewState = if (showStoryRing) StoryViewState.getForRecipientId(getRecipient(model).id) else null
|
||||
avatar.setStoryRingFromState(StoryViewState.NONE)
|
||||
groupStoryIndicator.isActivated = false
|
||||
|
||||
name.setText(getRecipient(model))
|
||||
badge.setBadgeFromRecipient(getRecipient(model))
|
||||
|
||||
bindAvatar(model)
|
||||
bindNumberField(model)
|
||||
}
|
||||
|
||||
fun isSelected(model: StoryModel): Boolean = model.isSelected
|
||||
fun getData(model: StoryModel): ContactSearchData.Story = model.story
|
||||
fun getRecipient(model: StoryModel): Recipient = model.story.recipient
|
||||
|
||||
fun bindNumberField(model: StoryModel) {
|
||||
number.visible = true
|
||||
|
||||
val count = if (model.story.recipient.isGroup) {
|
||||
@@ -193,17 +232,23 @@ open class ContactSearchAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
override fun bindAvatar(model: StoryModel) {
|
||||
if (model.story.recipient.isMyStory) {
|
||||
avatar.setFallbackPhotoProvider(MyStoryFallbackPhotoProvider(Recipient.self().getDisplayName(context), 40.dp))
|
||||
avatar.setAvatarUsingProfile(Recipient.self())
|
||||
} else {
|
||||
avatar.setFallbackPhotoProvider(Recipient.DEFAULT_FALLBACK_PHOTO_PROVIDER)
|
||||
super.bindAvatar(model)
|
||||
}
|
||||
fun bindCheckbox(model: StoryModel) {
|
||||
checkbox.visible = displayCheckBox
|
||||
checkbox.isChecked = isSelected(model)
|
||||
}
|
||||
|
||||
override fun bindLongPress(model: StoryModel) {
|
||||
fun bindAvatar(model: StoryModel) {
|
||||
if (model.story.recipient.isMyStory) {
|
||||
avatar.setFallbackPhotoProvider(MyStoryFallbackPhotoProvider(Recipient.self().getDisplayName(context), 40.dp))
|
||||
avatar.displayProfileAvatar(Recipient.self())
|
||||
} else {
|
||||
avatar.setFallbackPhotoProvider(Recipient.DEFAULT_FALLBACK_PHOTO_PROVIDER)
|
||||
avatar.displayChatAvatar(getRecipient(model))
|
||||
}
|
||||
groupStoryIndicator.visible = showStoryRing && model.story.recipient.isGroup
|
||||
}
|
||||
|
||||
fun bindLongPress(model: StoryModel) {
|
||||
if (storyContextMenuCallbacks == null) {
|
||||
return
|
||||
}
|
||||
@@ -264,6 +309,20 @@ open class ContactSearchAdapter(
|
||||
return GeneratedContactPhoto(name, R.drawable.symbol_person_40, targetSize)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
storyDisposable = storyViewState?.observeOn(AndroidSchedulers.mainThread())?.subscribe {
|
||||
avatar.setStoryRingFromState(it)
|
||||
when (it) {
|
||||
StoryViewState.UNVIEWED -> groupStoryIndicator.isActivated = true
|
||||
else -> groupStoryIndicator.isActivated = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
storyDisposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -661,7 +720,8 @@ open class ContactSearchAdapter(
|
||||
val displayCheckBox: Boolean = false,
|
||||
val displaySmsTag: DisplaySmsTag = DisplaySmsTag.NEVER,
|
||||
val displaySecondaryInformation: DisplaySecondaryInformation = DisplaySecondaryInformation.NEVER,
|
||||
val displayCallButtons: Boolean = false
|
||||
val displayCallButtons: Boolean = false,
|
||||
val displayStoryRing: Boolean = false
|
||||
)
|
||||
|
||||
fun interface OnClickedCallback<D : ContactSearchData> {
|
||||
|
||||
Reference in New Issue
Block a user