diff --git a/app/build.gradle.kts b/app/build.gradle.kts index afa6f09c4fd..3b56fec4acb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -278,8 +278,6 @@ dependencies { implementation("androidx.room:room-ktx:$roomVersion") implementation("org.parceler:parceler-api:$parcelerVersion") - implementation("com.github.ddB0515.FlexibleAdapter:flexible-adapter:5.1.1") - implementation("com.github.ddB0515.FlexibleAdapter:flexible-adapter-ui:5.1.1") implementation("org.apache.commons:commons-lang3:3.20.0") implementation("com.google.code.findbugs:jsr305:3.0.2") implementation("joda-time:joda-time:2.14.3") diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ContactItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/ContactItem.kt deleted file mode 100644 index 9bf8470dd52..00000000000 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ContactItem.kt +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Nextcloud Talk - Android Client - * - * SPDX-FileCopyrightText: 2022 Marcel Hibbe - * SPDX-FileCopyrightText: 2021 Andy Scherzinger - * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-License-Identifier: GPL-3.0-or-later - */ -package com.nextcloud.talk.adapters.items - -import android.text.TextUtils -import android.view.View -import androidx.core.content.res.ResourcesCompat -import androidx.recyclerview.widget.RecyclerView -import com.nextcloud.talk.R -import com.nextcloud.talk.adapters.items.ContactItem.ContactItemViewHolder -import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication -import com.nextcloud.talk.data.user.model.User -import com.nextcloud.talk.databinding.RvItemContactBinding -import com.nextcloud.talk.extensions.loadUserAvatar -import com.nextcloud.talk.models.json.participants.ParticipantDto -import com.nextcloud.talk.ui.theme.ViewThemeUtils -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFilterable -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.flexibleadapter.items.ISectionable -import eu.davidea.viewholders.FlexibleViewHolder -import java.util.Objects -import java.util.regex.Pattern - -class ContactItem( - /** - * @return the model object - */ - val model: ParticipantDto, - private val user: User, - private var header: GenericTextHeaderItem?, - private val viewThemeUtils: ViewThemeUtils -) : AbstractFlexibleItem(), - ISectionable, - IFilterable { - var isOnline: Boolean = true - - override fun equals(o: Any?): Boolean { - if (o is ContactItem) { - return model.calculatedActorType == o.model.calculatedActorType && - model.calculatedActorId == o.model.calculatedActorId - } - return false - } - override fun hashCode(): Int = model.hashCode() - - override fun filter(constraint: String?): Boolean = - model.displayName != null && - ( - Pattern.compile(constraint!!, Pattern.CASE_INSENSITIVE or Pattern.LITERAL) - .matcher(model.displayName!!.trim()) - .find() || - Pattern.compile(constraint!!, Pattern.CASE_INSENSITIVE or Pattern.LITERAL) - .matcher(model.calculatedActorId!!.trim()) - .find() - ) - - override fun getLayoutRes(): Int = R.layout.rv_item_contact - - override fun createViewHolder( - view: View?, - adapter: FlexibleAdapter>? - ): ContactItemViewHolder = ContactItemViewHolder(view, adapter) - - override fun bindViewHolder( - adapter: FlexibleAdapter>?, - holder: ContactItemViewHolder?, - position: Int, - payloads: List? - ) { - if (model.selected) { - holder?.binding?.checkedImageView?.let { viewThemeUtils.platform.colorImageView(it) } - holder?.binding?.checkedImageView?.visibility = View.VISIBLE - } else { - holder?.binding?.checkedImageView?.visibility = View.GONE - } - - if (!isOnline) { - holder?.binding?.nameText?.setTextColor( - ResourcesCompat.getColor( - holder.binding.nameText.context.resources, - R.color.medium_emphasis_text, - null - ) - ) - holder?.binding?.avatarView?.alpha = SEMI_TRANSPARENT - } else { - holder?.binding?.nameText?.setTextColor( - ResourcesCompat.getColor( - holder.binding.nameText.context.resources, - R.color.high_emphasis_text, - null - ) - ) - holder?.binding?.avatarView?.alpha = FULLY_OPAQUE - } - - holder?.binding?.nameText?.text = model.displayName - - if (adapter != null) { - if (adapter.hasFilter()) { - holder?.binding?.let { - viewThemeUtils.talk.themeAndHighlightText( - it.nameText, - model.displayName, - adapter.getFilter(String::class.java).toString() - ) - } - } - } - - if (TextUtils.isEmpty(model.displayName) && - ( - model.type == ParticipantDto.ParticipantType.GUEST || - model.type == ParticipantDto.ParticipantType.USER_FOLLOWING_LINK - ) - ) { - holder?.binding?.nameText?.text = sharedApplication!!.getString(R.string.nc_guest) - } - - setAvatar(holder) - } - - private fun setAvatar(holder: ContactItemViewHolder?) { - if (model.calculatedActorType == ParticipantDto.ActorType.GROUPS || - model.calculatedActorType == ParticipantDto.ActorType.CIRCLES - ) { - setGenericAvatar(holder!!, R.drawable.ic_avatar_group) - } else if (model.calculatedActorType == ParticipantDto.ActorType.EMAILS) { - setGenericAvatar(holder!!, R.drawable.ic_avatar_mail) - } else if (model.calculatedActorType == ParticipantDto.ActorType.GUESTS || - model.type == ParticipantDto.ParticipantType.GUEST || - model.type == ParticipantDto.ParticipantType.GUEST_MODERATOR - ) { - var displayName: String? - - displayName = if (!TextUtils.isEmpty(model.displayName)) { - model.displayName - } else { - Objects.requireNonNull(sharedApplication)!!.resources!!.getString(R.string.nc_guest) - } - - // absolute fallback to prevent NPE deference - if (displayName == null) { - displayName = "Guest" - } - - holder?.binding?.avatarView?.loadUserAvatar(user, displayName, true, false) - } else if (model.calculatedActorType == ParticipantDto.ActorType.USERS) { - holder?.binding?.avatarView - ?.loadUserAvatar( - user, - model.calculatedActorId!!, - true, - false - ) - } - } - - private fun setGenericAvatar(holder: ContactItemViewHolder, roundPlaceholderDrawable: Int) { - val avatar = - viewThemeUtils.talk.themePlaceholderAvatar( - holder.binding.avatarView, - roundPlaceholderDrawable - ) - - holder.binding.avatarView.loadUserAvatar(avatar) - } - - override fun getHeader(): GenericTextHeaderItem? = header - - override fun setHeader(p0: GenericTextHeaderItem?) { - this.header = header - } - - class ContactItemViewHolder(view: View?, adapter: FlexibleAdapter<*>?) : FlexibleViewHolder(view, adapter) { - var binding: RvItemContactBinding = - RvItemContactBinding.bind(view!!) - } - - companion object { - private const val FULLY_OPAQUE: Float = 1.0f - private const val SEMI_TRANSPARENT: Float = 0.38f - } -} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/FlexibleItemViewType.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/FlexibleItemViewType.kt index 6df9aeb6ef6..40fa4f50c06 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/FlexibleItemViewType.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/FlexibleItemViewType.kt @@ -7,10 +7,6 @@ package com.nextcloud.talk.adapters.items object FlexibleItemViewType { - const val CONVERSATION_ITEM: Int = 1120391230 - const val LOAD_MORE_RESULTS_ITEM: Int = 1120391231 - const val MESSAGE_RESULT_ITEM: Int = 1120391232 - const val MESSAGES_TEXT_HEADER_ITEM: Int = 1120391233 const val POLL_RESULT_HEADER_ITEM: Int = 1120391234 const val POLL_RESULT_VOTER_ITEM: Int = 1120391235 const val POLL_RESULT_VOTERS_OVERVIEW_ITEM: Int = 1120391236 diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/GenericTextHeaderItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/GenericTextHeaderItem.kt deleted file mode 100644 index aaf325fe921..00000000000 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/GenericTextHeaderItem.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Nextcloud Talk - Android Client - * - * SPDX-FileCopyrightText: 2022 Andy Scherzinger - * SPDX-FileCopyrightText: 2017-2018 Mario Danic - * SPDX-License-Identifier: GPL-3.0-or-later - */ -package com.nextcloud.talk.adapters.items - -import android.util.Log -import android.view.View -import androidx.recyclerview.widget.RecyclerView -import com.nextcloud.talk.R -import com.nextcloud.talk.databinding.RvItemTitleHeaderBinding -import com.nextcloud.talk.ui.theme.ViewThemeUtils -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractHeaderItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import java.util.Objects - -open class GenericTextHeaderItem(title: String, viewThemeUtils: ViewThemeUtils) : - AbstractHeaderItem() { - val model: String - private val viewThemeUtils: ViewThemeUtils - - init { - isHidden = false - isSelectable = false - this.model = title - this.viewThemeUtils = viewThemeUtils - } - - override fun equals(o: Any?): Boolean { - if (o is GenericTextHeaderItem) { - return model == o.model - } - return false - } - - override fun hashCode(): Int = Objects.hash(model) - - override fun getLayoutRes(): Int = R.layout.rv_item_title_header - - override fun createViewHolder( - view: View?, - adapter: FlexibleAdapter>? - ): HeaderViewHolder = HeaderViewHolder(view, adapter) - - override fun bindViewHolder( - adapter: FlexibleAdapter?>?, - holder: HeaderViewHolder, - position: Int, - payloads: List - ) { - if (payloads.size > 0) { - Log.d(TAG, "We have payloads, so ignoring!") - } else { - holder.binding.titleTextView.text = model - viewThemeUtils.platform.colorPrimaryTextViewElement(holder.binding.titleTextView) - } - } - - class HeaderViewHolder(view: View?, adapter: FlexibleAdapter<*>?) : FlexibleViewHolder(view, adapter, true) { - var binding: RvItemTitleHeaderBinding = - RvItemTitleHeaderBinding.bind(view!!) - } - - companion object { - private const val TAG = "GenericTextHeaderItem" - } -} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/LoadMoreResultsItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/LoadMoreResultsItem.kt deleted file mode 100644 index f47aec87f85..00000000000 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/LoadMoreResultsItem.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Nextcloud Talk - Android Client - * - * SPDX-FileCopyrightText: 2022 Álvaro Brey - * SPDX-License-Identifier: GPL-3.0-or-later - */ -package com.nextcloud.talk.adapters.items - -import android.view.View -import androidx.recyclerview.widget.RecyclerView -import com.nextcloud.talk.R -import com.nextcloud.talk.databinding.RvItemLoadMoreBinding -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFilterable -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder - -object LoadMoreResultsItem : - AbstractFlexibleItem(), - IFilterable { - - // layout is used as view type for uniqueness - const val VIEW_TYPE = FlexibleItemViewType.LOAD_MORE_RESULTS_ITEM - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { - var binding: RvItemLoadMoreBinding = RvItemLoadMoreBinding.bind(view) - } - - override fun getLayoutRes(): Int = R.layout.rv_item_load_more - - override fun createViewHolder( - view: View, - adapter: FlexibleAdapter> - ): ViewHolder = ViewHolder(view, adapter) - - override fun bindViewHolder( - adapter: FlexibleAdapter>, - holder: ViewHolder, - position: Int, - payloads: MutableList? - ) { - // nothing, it's immutable - } - - override fun filter(constraint: String?): Boolean = true - - override fun getItemViewType(): Int = VIEW_TYPE - - override fun equals(other: Any?): Boolean = other is LoadMoreResultsItem - - override fun hashCode(): Int = 0 -} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt deleted file mode 100644 index cbd91124215..00000000000 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Nextcloud Talk - Android Client - * - * SPDX-FileCopyrightText: 2022 Álvaro Brey - * SPDX-FileCopyrightText: 2022 Tim Krüger - * SPDX-FileCopyrightText: 2021 Andy Scherzinger - * SPDX-FileCopyrightText: 2017-2019 Mario Danic - * SPDX-License-Identifier: GPL-3.0-or-later - */ -package com.nextcloud.talk.adapters.items - -import android.content.Context -import android.view.View -import androidx.recyclerview.widget.RecyclerView -import com.nextcloud.talk.R -import com.nextcloud.talk.data.user.model.User -import com.nextcloud.talk.databinding.RvItemSearchMessageBinding -import com.nextcloud.talk.extensions.loadThumbnail -import com.nextcloud.talk.models.domain.SearchMessageEntry -import com.nextcloud.talk.ui.theme.ViewThemeUtils -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFilterable -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.flexibleadapter.items.ISectionable -import eu.davidea.viewholders.FlexibleViewHolder - -data class MessageResultItem( - private val context: Context, - private val currentUser: User, - val messageEntry: SearchMessageEntry, - var showHeader: Boolean = false, - private val viewThemeUtils: ViewThemeUtils -) : AbstractFlexibleItem(), - IFilterable, - ISectionable { - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { - var binding: RvItemSearchMessageBinding - - init { - binding = RvItemSearchMessageBinding.bind(view) - } - } - - override fun getLayoutRes(): Int = R.layout.rv_item_search_message - - override fun createViewHolder( - view: View, - adapter: FlexibleAdapter> - ): ViewHolder = ViewHolder(view, adapter) - - override fun bindViewHolder( - adapter: FlexibleAdapter>, - holder: ViewHolder, - position: Int, - payloads: MutableList? - ) { - holder.binding.conversationTitle.text = messageEntry.title - bindMessageExcerpt(holder) - messageEntry.thumbnailURL?.let { holder.binding.thumbnail.loadThumbnail(it, currentUser) } - } - - private fun bindMessageExcerpt(holder: ViewHolder) { - viewThemeUtils.platform.highlightText( - holder.binding.messageExcerpt, - messageEntry.messageExcerpt, - messageEntry.searchTerm - ) - } - - override fun filter(constraint: String?): Boolean = true - - override fun getItemViewType(): Int = VIEW_TYPE - - companion object { - const val VIEW_TYPE = FlexibleItemViewType.MESSAGE_RESULT_ITEM - } - - override fun getHeader(): GenericTextHeaderItem = - MessagesTextHeaderItem(context, viewThemeUtils) - .apply { - isHidden = showHeader // FlexibleAdapter needs this hack for some reason - } - - override fun setHeader(header: GenericTextHeaderItem?) { - // nothing, header is always the same - } -} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MessagesTextHeaderItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/MessagesTextHeaderItem.kt deleted file mode 100644 index 375128397d3..00000000000 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MessagesTextHeaderItem.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Nextcloud Talk - Android Client - * - * SPDX-FileCopyrightText: 2022 Álvaro Brey - * SPDX-License-Identifier: GPL-3.0-or-later - */ -package com.nextcloud.talk.adapters.items - -import android.content.Context -import com.nextcloud.talk.R -import com.nextcloud.talk.ui.theme.ViewThemeUtils - -class MessagesTextHeaderItem(context: Context, viewThemeUtils: ViewThemeUtils) : - GenericTextHeaderItem(context.getString(R.string.messages), viewThemeUtils) { - companion object { - const val VIEW_TYPE = FlexibleItemViewType.MESSAGES_TEXT_HEADER_ITEM - } - - override fun getItemViewType(): Int = VIEW_TYPE -} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/SpacerItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/SpacerItem.kt deleted file mode 100644 index 89ed9c23d06..00000000000 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/SpacerItem.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Nextcloud Talk - Android Client - * - * SPDX-FileCopyrightText: 2025 Marcel Hibbe - * SPDX-License-Identifier: GPL-3.0-or-later - */ -package com.nextcloud.talk.adapters.items - -import android.view.View -import com.nextcloud.talk.R -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder - -class SpacerItem(private val height: Int) : AbstractFlexibleItem() { - - override fun getLayoutRes(): Int = R.layout.item_spacer - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter?>?): ViewHolder = - ViewHolder(view!!, adapter!!) - - override fun bindViewHolder( - adapter: FlexibleAdapter?>?, - holder: ViewHolder, - position: Int, - payloads: MutableList? - ) { - holder.itemView.layoutParams.height = height - } - - override fun equals(other: Any?) = other is SpacerItem - - override fun hashCode(): Int = 0 - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) -} diff --git a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt index 7b8ac75a34f..4a23ed64d53 100644 --- a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt @@ -22,17 +22,12 @@ import autodagger.AutoInjector import com.google.android.material.snackbar.Snackbar import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity -import com.nextcloud.talk.adapters.items.LoadMoreResultsItem -import com.nextcloud.talk.adapters.items.MessageResultItem import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.conversationlist.ConversationsListActivity import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ActivityMessageSearchBinding import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.rx.SearchViewObservable.Companion.observeSearchView -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.viewholders.FlexibleViewHolder import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable @@ -54,7 +49,7 @@ class MessageSearchActivity : BaseActivity() { private lateinit var viewModel: MessageSearchViewModel private var searchViewDisposable: Disposable? = null - private var adapter: FlexibleAdapter>? = null + private lateinit var adapter: MessageSearchAdapter private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { @@ -76,6 +71,7 @@ class MessageSearchActivity : BaseActivity() { user = currentUserProviderOld.currentUser.blockingGet() val roomToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!! viewModel.initialize(roomToken) + setupAdapter() setupStateObserver() binding.swipeRefreshLayout.setOnRefreshListener { @@ -126,39 +122,23 @@ class MessageSearchActivity : BaseActivity() { setAdapterItems(state) } + private fun setupAdapter() { + adapter = MessageSearchAdapter( + user = user, + viewThemeUtils = viewThemeUtils, + onResultClick = { viewModel.selectMessage(it) }, + onLoadMoreClick = { viewModel.loadMore() } + ) + binding.messageSearchRecycler.adapter = adapter + } + private fun setAdapterItems(state: MessageSearchViewModel.LoadedState) { val loadMoreItems = if (state.hasMore) { - listOf(LoadMoreResultsItem) + listOf(MessageSearchAdapter.Item.LoadMore) } else { emptyList() } - val newItems = - state.results.map { MessageResultItem(this, user, it, false, viewThemeUtils) } + loadMoreItems - - if (adapter != null) { - adapter!!.updateDataSet(newItems) - } else { - createAdapter(newItems) - } - } - - private fun createAdapter(items: List>) { - adapter = FlexibleAdapter(items) - binding.messageSearchRecycler.adapter = adapter - adapter!!.addListener(object : FlexibleAdapter.OnItemClickListener { - override fun onItemClick(view: View?, position: Int): Boolean { - val item = adapter!!.getItem(position) - when (item) { - is LoadMoreResultsItem -> { - viewModel.loadMore() - } - is MessageResultItem -> { - viewModel.selectMessage(item.messageEntry) - } - } - return false - } - }) + adapter.submitList(state.results.map { MessageSearchAdapter.Item.Result(it) } + loadMoreItems) } private fun onFinish() { diff --git a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchAdapter.kt b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchAdapter.kt new file mode 100644 index 00000000000..7ef24f3f115 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchAdapter.kt @@ -0,0 +1,98 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package com.nextcloud.talk.messagesearch + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.databinding.RvItemLoadMoreBinding +import com.nextcloud.talk.databinding.RvItemSearchMessageBinding +import com.nextcloud.talk.extensions.loadThumbnail +import com.nextcloud.talk.models.domain.SearchMessageEntry +import com.nextcloud.talk.ui.theme.ViewThemeUtils + +class MessageSearchAdapter( + private val user: User, + private val viewThemeUtils: ViewThemeUtils, + private val onResultClick: (SearchMessageEntry) -> Unit, + private val onLoadMoreClick: () -> Unit +) : ListAdapter(ItemCallback) { + + sealed interface Item { + data class Result(val entry: SearchMessageEntry) : Item + data object LoadMore : Item + } + + inner class ResultViewHolder(private val binding: RvItemSearchMessageBinding) : + RecyclerView.ViewHolder(binding.root) { + + init { + binding.root.setOnClickListener { + val item = getItemOrNull(bindingAdapterPosition) + if (item is Item.Result) { + onResultClick(item.entry) + } + } + } + + fun bind(entry: SearchMessageEntry) { + binding.conversationTitle.text = entry.title + viewThemeUtils.platform.highlightText(binding.messageExcerpt, entry.messageExcerpt, entry.searchTerm) + entry.thumbnailURL?.let { binding.thumbnail.loadThumbnail(it, user) } + } + } + + inner class LoadMoreViewHolder(binding: RvItemLoadMoreBinding) : RecyclerView.ViewHolder(binding.root) { + init { + binding.root.setOnClickListener { onLoadMoreClick() } + } + } + + private fun getItemOrNull(position: Int): Item? = + if (position == RecyclerView.NO_POSITION) null else getItem(position) + + override fun getItemViewType(position: Int): Int = + when (getItem(position)) { + is Item.Result -> VIEW_TYPE_RESULT + Item.LoadMore -> VIEW_TYPE_LOAD_MORE + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return when (viewType) { + VIEW_TYPE_LOAD_MORE -> LoadMoreViewHolder(RvItemLoadMoreBinding.inflate(inflater, parent, false)) + else -> ResultViewHolder(RvItemSearchMessageBinding.inflate(inflater, parent, false)) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val item = getItem(position) + if (holder is ResultViewHolder && item is Item.Result) { + holder.bind(item.entry) + } + } + + private object ItemCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean = + when { + oldItem is Item.Result && newItem is Item.Result -> + oldItem.entry.conversationToken == newItem.entry.conversationToken && + oldItem.entry.messageId == newItem.entry.messageId + else -> oldItem == newItem + } + + override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean = oldItem == newItem + } + + companion object { + private const val VIEW_TYPE_RESULT = 0 + private const val VIEW_TYPE_LOAD_MORE = 1 + } +} diff --git a/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt b/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt index edcda2624f8..774a1270894 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt @@ -55,7 +55,7 @@ import com.nextcloud.talk.utils.DrawableUtils import com.nextcloud.talk.utils.message.MessageUtils import dynamiccolor.DynamicScheme import dynamiccolor.MaterialDynamicColors -import eu.davidea.flexibleadapter.utils.FlexibleUtils +import java.util.Locale import javax.inject.Inject import kotlin.math.roundToInt @@ -343,8 +343,8 @@ class TalkSpecificViewThemeUtils @Inject constructor( fun themeAndHighlightText(textView: TextView, originalText: String?, c: String?) { withScheme(textView) { scheme -> var constraint = c - constraint = FlexibleUtils.toLowerCase(constraint) - var start = FlexibleUtils.toLowerCase(originalText).indexOf(constraint) + constraint = (constraint ?: "").lowercase(Locale.getDefault()) + var start = (originalText ?: "").lowercase(Locale.getDefault()).indexOf(constraint) if (start != -1) { val spanText = Spannable.Factory.getInstance().newSpannable(originalText) do { @@ -356,7 +356,7 @@ class TalkSpecificViewThemeUtils @Inject constructor( Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) spanText.setSpan(StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - start = FlexibleUtils.toLowerCase(originalText) + start = (originalText ?: "").lowercase(Locale.getDefault()) .indexOf(constraint, end + 1) // +1 skips the consecutive span } while (start != -1) textView.setText(spanText, TextView.BufferType.SPANNABLE) diff --git a/app/src/main/res/layout/item_spacer.xml b/app/src/main/res/layout/item_spacer.xml deleted file mode 100644 index d68a36408c2..00000000000 --- a/app/src/main/res/layout/item_spacer.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_contact.xml b/app/src/main/res/layout/rv_item_contact.xml deleted file mode 100644 index 2a5a2abd587..00000000000 --- a/app/src/main/res/layout/rv_item_contact.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/layout/rv_item_title_header.xml b/app/src/main/res/layout/rv_item_title_header.xml deleted file mode 100644 index be489c2bee2..00000000000 --- a/app/src/main/res/layout/rv_item_title_header.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 4b7cd06cb46..1c04805e6dc 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -333,7 +333,6 @@ - @@ -19714,22 +19713,6 @@ - - - - - - - - - - - - - - - -