From 115da641671e91d5678e1fd3d201b531cd074f84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Wed, 20 May 2020 14:12:32 +0200 Subject: [PATCH] Add search in messages (#804) --- app/src/main/AndroidManifest.xml | 3 +- .../modules/message/tab/MessageTabAdapter.kt | 38 ++++++++++++++-- .../modules/message/tab/MessageTabFragment.kt | 40 +++++++++++++---- .../message/tab/MessageTabPresenter.kt | 43 ++++++++++++++++--- .../ui/modules/message/tab/MessageTabView.kt | 2 + app/src/main/res/drawable/ic_search.xml | 9 ++++ app/src/main/res/layout/activity_main.xml | 4 +- .../main/res/menu/action_menu_message_tab.xml | 11 +++++ app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values/strings.xml | 2 + 10 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 app/src/main/res/drawable/ic_search.xml create mode 100644 app/src/main/res/menu/action_menu_message_tab.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4478f4087..4dd70721e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -39,7 +39,8 @@ android:name=".ui.modules.main.MainActivity" android:configChanges="orientation|screenSize" android:label="@string/main_title" - android:theme="@style/WulkanowyTheme.NoActionBar" /> + android:theme="@style/WulkanowyTheme.NoActionBar" + android:windowSoftInputMode="adjustPan" /> () { - var items = mutableListOf() - var onClickListener: (Message, position: Int) -> Unit = { _, _ -> } - override fun getItemCount() = items.size + private val items = SortedList(Message::class.java, object : + SortedListAdapterCallback(this) { + + override fun compare(item1: Message, item2: Message): Int { + return item2.date.compareTo(item1.date) + } + + override fun areContentsTheSame(oldItem: Message?, newItem: Message?): Boolean { + return oldItem == newItem + } + + override fun areItemsTheSame(item1: Message, item2: Message): Boolean { + return item1 == item2 + } + }) + + fun replaceAll(models: List) { + items.beginBatchedUpdates() + for (i in items.size() - 1 downTo 0) { + val model = items.get(i) + if (model !in models) { + items.remove(model) + } + } + items.addAll(models) + items.endBatchedUpdates() + } + + fun updateItem(position: Int, item: Message) { + items.updateItemAt(position, item) + } + + override fun getItemCount() = items.size() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( ItemMessageBinding.inflate(LayoutInflater.from(parent.context), parent, false) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt index 3fdf16845..909bb6873 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -1,10 +1,13 @@ package io.github.wulkanowy.ui.modules.message.tab import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater import android.view.View import android.view.View.GONE import android.view.View.INVISIBLE import android.view.View.VISIBLE +import androidx.appcompat.widget.SearchView import androidx.recyclerview.widget.LinearLayoutManager import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message @@ -39,7 +42,12 @@ class MessageTabFragment : BaseFragment(R.layout.frag } override val isViewEmpty - get() = tabAdapter.items.isEmpty() + get() = tabAdapter.itemCount == 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -65,18 +73,28 @@ class MessageTabFragment : BaseFragment(R.layout.frag } } + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.action_menu_message_tab, menu) + + val searchView = menu.findItem(R.id.action_search).actionView as SearchView + searchView.queryHint = getString(R.string.all_search_hint) + searchView.maxWidth = Int.MAX_VALUE + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String) = false + override fun onQueryTextChange(query: String): Boolean { + presenter.onSearchQueryTextChange(query) + return true + } + }) + } + override fun updateData(data: List) { - with(tabAdapter) { - items = data.toMutableList() - notifyDataSetChanged() - } + tabAdapter.replaceAll(data) } override fun updateItem(item: Message, position: Int) { - with(tabAdapter) { - items[position] = item - notifyItemChanged(position) - } + tabAdapter.updateItem(position, item) } override fun showProgress(show: Boolean) { @@ -87,6 +105,10 @@ class MessageTabFragment : BaseFragment(R.layout.frag binding.messageTabSwipe.isEnabled = enable } + override fun resetListPosition() { + binding.messageTabRecycler.scrollToPosition(0) + } + override fun showContent(show: Boolean) { binding.messageTabRecycler.visibility = if (show) VISIBLE else INVISIBLE } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index 37b45d03d..f96fb6c2a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.message.tab +import android.annotation.SuppressLint import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.repositories.message.MessageFolder import io.github.wulkanowy.data.repositories.message.MessageRepository @@ -9,6 +10,7 @@ import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.toFormattedString import timber.log.Timber import javax.inject.Inject @@ -25,6 +27,10 @@ class MessageTabPresenter @Inject constructor( private lateinit var lastError: Throwable + private var lastSearchQuery = "" + + private var messages = emptyList() + fun onAttachView(view: MessageTabView, folder: MessageFolder) { super.onAttachView(view) view.initView() @@ -89,12 +95,8 @@ class MessageTabPresenter @Inject constructor( } .subscribe({ Timber.i("Loading $folder message result: Success") - view?.run { - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - showErrorView(false) - updateData(it) - } + messages = it + onSearchQueryTextChange(lastSearchQuery) analytics.logEvent("load_messages", "items" to it.size, "folder" to folder.name) }) { Timber.i("Loading $folder message result: An exception occurred") @@ -113,4 +115,33 @@ class MessageTabPresenter @Inject constructor( } else showError(message, error) } } + + @SuppressLint("DefaultLocale") + fun onSearchQueryTextChange(query: String) { + lastSearchQuery = query + + val lowerCaseQuery = query.toLowerCase() + val filteredList = mutableListOf() + messages.forEach { + if (lowerCaseQuery in it.subject.toLowerCase() || + lowerCaseQuery in it.sender.toLowerCase() || + lowerCaseQuery in it.recipient.toLowerCase() || + lowerCaseQuery in it.date.toFormattedString() + ) { + filteredList.add(it) + } + } + + updateData(filteredList) + } + + private fun updateData(data: List) { + view?.run { + showEmpty(data.isEmpty()) + showContent(data.isNotEmpty()) + showErrorView(false) + updateData(data) + resetListPosition() + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt index 94ece8ec2..f521191cf 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt @@ -9,6 +9,8 @@ interface MessageTabView : BaseView { fun initView() + fun resetListPosition() + fun updateData(data: List) fun updateItem(item: Message, position: Int) diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 000000000..cd9985cb1 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index d07dbbd8a..2ea0a4d39 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,6 @@ @@ -8,7 +9,8 @@ android:id="@+id/mainToolbar" style="@style/Widget.MaterialComponents.Toolbar.Surface" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + app:contentInsetStartWithNavigation="0dp" /> + + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 523221f01..bff422927 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -298,6 +298,7 @@ Przedmiot Poprzedni Następny + Szukaj Brak lekcji Wybierz motyw diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4deba2130..0a837c46b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -329,6 +329,8 @@ Subject Prev Next + Search + Search...