1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 07:19:08 -05:00

Add search in messages (#804)

This commit is contained in:
Mikołaj Pich 2020-05-20 14:12:32 +02:00 committed by GitHub
parent 6cd1877af7
commit 115da64167
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 133 additions and 20 deletions

View File

@ -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" />
<activity
android:name=".ui.modules.message.send.SendMessageActivity"
android:configChanges="orientation|screenSize"

View File

@ -5,6 +5,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SortedList
import androidx.recyclerview.widget.SortedListAdapterCallback
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageFolder
@ -15,11 +17,41 @@ import javax.inject.Inject
class MessageTabAdapter @Inject constructor() :
RecyclerView.Adapter<MessageTabAdapter.ItemViewHolder>() {
var items = mutableListOf<Message>()
var onClickListener: (Message, position: Int) -> Unit = { _, _ -> }
override fun getItemCount() = items.size
private val items = SortedList(Message::class.java, object :
SortedListAdapterCallback<Message>(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<Message>) {
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)

View File

@ -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<FragmentMessageTabBinding>(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<FragmentMessageTabBinding>(R.layout.frag
}
}
override fun updateData(data: List<Message>) {
with(tabAdapter) {
items = data.toMutableList()
notifyDataSetChanged()
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<Message>) {
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<FragmentMessageTabBinding>(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
}

View File

@ -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<Message>()
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<Message>()
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<Message>) {
view?.run {
showEmpty(data.isEmpty())
showContent(data.isNotEmpty())
showErrorView(false)
updateData(data)
resetListPosition()
}
}
}

View File

@ -9,6 +9,8 @@ interface MessageTabView : BaseView {
fun initView()
fun resetListPosition()
fun updateData(data: List<Message>)
fun updateItem(item: Message, position: Int)

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFF"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/mainContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -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" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/mainFragmentContainer"

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:icon="@drawable/ic_search"
android:title="@string/all_search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:iconTint="@color/material_on_surface_emphasis_medium"
app:showAsAction="collapseActionView|ifRoom" />
</menu>

View File

@ -298,6 +298,7 @@
<string name="all_subject">Przedmiot</string>
<string name="all_prev">Poprzedni</string>
<string name="all_next">Następny</string>
<string name="all_search">Szukaj</string>
<!--Timetable Widget-->
<string name="widget_timetable_no_items">Brak lekcji</string>
<string name="widget_timetable_theme_title">Wybierz motyw</string>

View File

@ -329,6 +329,8 @@
<string name="all_subject">Subject</string>
<string name="all_prev">Prev</string>
<string name="all_next">Next</string>
<string name="all_search">Search</string>
<string name="all_search_hint">Search...</string>
<!--Timetable Widget-->