[Messages] Add a search bar. Fix Grades not loading.

This commit is contained in:
Kuba Szczodrzyński 2020-04-05 20:06:35 +02:00
parent 91a6366548
commit c214b48409
21 changed files with 435 additions and 93 deletions

View File

@ -572,7 +572,7 @@ fun CharSequence?.asBoldSpannable(): Spannable {
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
return spannable
}
fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false): Spannable {
fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false, ignoreDiacritics: Boolean = false): Spannable {
val spannable = SpannableString(this)
if (substring == null) {
spans.forEach {
@ -580,17 +580,44 @@ fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignor
}
}
else if (substring.isNotEmpty()) {
var index = indexOf(substring, ignoreCase = ignoreCase)
val string =
if (ignoreDiacritics)
this.cleanDiacritics()
else this
var index = string.indexOf(substring, ignoreCase = ignoreCase)
.takeIf { it != -1 } ?: indexOf(substring, ignoreCase = ignoreCase)
while (index >= 0) {
spans.forEach {
spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
index = indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
index = string.indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
.takeIf { it != -1 } ?: indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
}
}
return spannable
}
fun CharSequence.cleanDiacritics(): String {
val nameClean = StringBuilder()
forEach {
val ch = when (it) {
'ż' -> 'z'
'ó' -> 'o'
'ł' -> 'l'
'ć' -> 'c'
'ę' -> 'e'
'ś' -> 's'
'ą' -> 'a'
'ź' -> 'z'
'ń' -> 'n'
else -> it
}
nameClean.append(ch)
}
return nameClean.toString()
}
/**
* Returns a new read-only list only of those given elements, that are not empty.
* Applies for CharSequence and descendants.

View File

@ -3,6 +3,7 @@
*/
package pl.szczodrzynski.edziennik.data.db.full
import androidx.room.Ignore
import androidx.room.Relation
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
@ -24,6 +25,11 @@ class MessageFull(
return this
}
@Ignore
var filterWeight = 0
@Ignore
var searchHighlightText: String? = null
// metadata
var seen = false
var notified = false

View File

@ -166,7 +166,7 @@ class GradesAdapter(
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = items[position]
if (holder !is BindableViewHolder<*>)
if (holder !is BindableViewHolder<*, *>)
return
val viewType = when (holder) {

View File

@ -71,7 +71,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
val adapter = GradesAdapter(activity)
var firstRun = true
app.db.gradeDao().getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()).observe(this@GradesListFragment, Observer { items -> launch {
app.db.gradeDao().getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()).observe(this@GradesListFragment, Observer { items -> this@GradesListFragment.launch {
if (!isAdded) return@launch
// load & configure the adapter

View File

@ -6,8 +6,7 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
interface BindableViewHolder<T> {
fun onBind(activity: AppCompatActivity, app: App, item: T, position: Int, adapter: GradesAdapter)
interface BindableViewHolder<T, A> {
fun onBind(activity: AppCompatActivity, app: App, item: T, position: Int, adapter: A)
}

View File

@ -17,7 +17,7 @@ class EmptyViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemEmptyBinding = GradesItemEmptyBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesEmpty> {
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesEmpty, GradesAdapter> {
companion object {
private const val TAG = "EmptyViewHolder"
}

View File

@ -21,7 +21,7 @@ class GradeViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemGradeBinding = GradesItemGradeBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradeFull> {
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradeFull, GradesAdapter> {
companion object {
private const val TAG = "GradeViewHolder"
}

View File

@ -21,7 +21,7 @@ class SemesterViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemSemesterBinding = GradesItemSemesterBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesSemester> {
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesSemester, GradesAdapter> {
companion object {
private const val TAG = "SemesterViewHolder"
}

View File

@ -24,7 +24,7 @@ class StatsViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemStatsBinding = GradesItemStatsBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesStats> {
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesStats, GradesAdapter> {
companion object {
private const val TAG = "StatsViewHolder"
}

View File

@ -28,7 +28,7 @@ class SubjectViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemSubjectBinding = GradesItemSubjectBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesSubject> {
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesSubject, GradesAdapter> {
companion object {
private const val TAG = "SubjectViewHolder"
}

View File

@ -208,7 +208,7 @@ class MessageFragment : Fragment(), CoroutineScope {
}
private fun showMessage() {
b.body.text = MessagesUtils.htmlToSpannable(activity, message.body ?: "")
b.body.text = MessagesUtils.htmlToSpannable(activity, message.body.toString())
b.date.text = getString(R.string.messages_date_time_format, Date.fromMillis(message.addedDate).formattedStringShort, Time.fromMillis(message.addedDate).stringHM)
val messageInfo = MessagesUtils.getMessageInfo(app, message, 40, 20, 14, 10)

View File

@ -1,31 +1,38 @@
package pl.szczodrzynski.edziennik.ui.modules.messages
import android.graphics.Typeface
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.cleanDiacritics
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.databinding.MessagesListItemBinding
import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
import pl.szczodrzynski.edziennik.ui.modules.messages.viewholder.MessageViewHolder
import pl.szczodrzynski.edziennik.ui.modules.messages.viewholder.SearchViewHolder
import java.util.*
import kotlin.coroutines.CoroutineContext
import kotlin.math.min
class MessagesAdapter(
val activity: AppCompatActivity,
val teachers: List<Teacher>,
val onItemClick: ((item: MessageFull) -> Unit)? = null
) : RecyclerView.Adapter<MessagesAdapter.ViewHolder>(), CoroutineScope {
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), CoroutineScope, Filterable {
companion object {
private const val TAG = "TemplateAdapter"
private const val TAG = "MessagesAdapter"
private const val ITEM_TYPE_MESSAGE = 0
private const val ITEM_TYPE_SEARCH = 1
}
private val app = activity.applicationContext as App
@ -35,55 +42,184 @@ class MessagesAdapter(
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
var items = listOf<MessageFull>()
private val typefaceNormal by lazy { Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) }
private val typefaceBold by lazy { Typeface.create(Typeface.DEFAULT, Typeface.BOLD) }
var items = mutableListOf<Any>()
var allItems = mutableListOf<Any>()
val typefaceNormal: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) }
val typefaceBold: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.BOLD) }
private val comparator by lazy { Comparator { o1: Any, o2: Any ->
if (o1 !is MessageFull || o2 !is MessageFull)
return@Comparator 0
when {
// standard sorting
o1.filterWeight > o2.filterWeight -> return@Comparator 1
o1.filterWeight < o2.filterWeight -> return@Comparator -1
else -> when {
// reversed sorting
o1.addedDate > o2.addedDate -> return@Comparator -1
o1.addedDate < o2.addedDate -> return@Comparator 1
else -> return@Comparator 0
}
}
}}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val textWatcher by lazy {
object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
getFilter().filter(s.toString())
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
/*items.getOrNull(0)?.let {
if (it is MessagesSearch) {
it.searchText = s?.toString() ?: ""
}
}*/
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = MessagesListItemBinding.inflate(inflater, parent, false)
return ViewHolder(view)
return when (viewType) {
ITEM_TYPE_MESSAGE -> MessageViewHolder(inflater, parent)
ITEM_TYPE_SEARCH -> SearchViewHolder(inflater, parent)
else -> throw IllegalArgumentException("Incorrect viewType")
}
}
override fun getItemViewType(position: Int): Int {
return when (items[position]) {
is MessageFull -> ITEM_TYPE_MESSAGE
is MessagesSearch -> ITEM_TYPE_SEARCH
else -> throw IllegalArgumentException("Incorrect viewType")
}
}
@Suppress("DEPRECATION")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = items[position]
val b = holder.b
if (holder !is BindableViewHolder<*, *>)
return
item.recipients?.forEach { recipient ->
if (recipient.fullName == null) {
recipient.fullName = teachers.firstOrNull { it.id == recipient.id }?.fullName ?: ""
}
}
b.messageSubject.text = item.subject
b.messageDate.text = Date.fromMillis(item.addedDate).formattedStringShort
b.messageAttachmentImage.isVisible = item.hasAttachments
val text = item.body?.take(200) ?: ""
b.messageBody.text = MessagesUtils.htmlToSpannable(activity, text)
val isRead = item.type == Message.TYPE_SENT || item.type == Message.TYPE_DRAFT || item.seen
val typeface = if (isRead) typefaceNormal else typefaceBold
val style = if (isRead) R.style.NavView_TextView_Small else R.style.NavView_TextView_Normal
// set text styles
b.messageSender.setTextAppearance(activity, style)
b.messageSender.typeface = typeface
b.messageSubject.setTextAppearance(activity, style)
b.messageSubject.typeface = typeface
b.messageDate.setTextAppearance(activity, style)
b.messageDate.typeface = typeface
val messageInfo = MessagesUtils.getMessageInfo(app, item, 48, 24, 18, 12)
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage)
b.messageSender.text = messageInfo.profileName
onItemClick?.let { listener ->
b.root.onClick { listener(item) }
when {
holder is MessageViewHolder && item is MessageFull -> holder.onBind(activity, app, item, position, this)
holder is SearchViewHolder && item is MessagesSearch -> holder.onBind(activity, app, item, position, this)
}
}
override fun getItemCount() = items.size
override fun getFilter() = filter
private var prevCount = -1
private val filter by lazy { object : Filter() {
override fun performFiltering(prefix: CharSequence?): FilterResults {
val results = FilterResults()
class ViewHolder(val b: MessagesListItemBinding) : RecyclerView.ViewHolder(b.root)
if (prevCount == -1)
prevCount = allItems.size
if (prefix.isNullOrEmpty()) {
allItems.forEach {
if (it is MessageFull)
it.searchHighlightText = null
}
results.values = allItems.toList()
results.count = allItems.size
return results
}
val items = mutableListOf<Any>()
val prefixString = prefix.toString()
allItems.forEach {
if (it !is MessageFull) {
items.add(it)
return@forEach
}
it.filterWeight = 100
it.searchHighlightText = null
var weight = getMatchWeight(it.senderName, prefixString)
if (weight != 100) {
if (weight == 3)
weight = 31
it.filterWeight = min(it.filterWeight, 10 + weight)
}
weight = getMatchWeight(it.subject, prefixString)
if (weight != 100) {
if (weight == 3)
weight = 22
it.filterWeight = min(it.filterWeight, 20 + weight)
}
if (it.filterWeight != 100) {
it.searchHighlightText = prefixString
items.add(it)
}
}
Collections.sort(items, comparator)
results.values = items
results.count = items.size
return results
}
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
results.values?.let { items = it as MutableList<Any> }
// do not re-bind the search box
val count = results.count - 1
// this tries to update every item except the search field
when {
count > prevCount -> {
notifyItemRangeInserted(prevCount + 1, count - prevCount)
notifyItemRangeChanged(1, prevCount)
}
count < prevCount -> {
notifyItemRangeRemoved(prevCount + 1, prevCount - count)
notifyItemRangeChanged(1, count)
}
else -> {
notifyItemRangeChanged(1, count)
}
}
/*if (prevCount != count) {
items.getOrNull(0)?.let {
if (it is MessagesSearch) {
it.count = count
notifyItemChanged(0)
}
}
}*/
prevCount = count
}
}}
private fun getMatchWeight(name: CharSequence?, prefix: String): Int {
if (name == null)
return 100
val nameClean = name.cleanDiacritics()
// First match against the whole, non-split value
if (nameClean.startsWith(prefix, ignoreCase = true) || name.startsWith(prefix, ignoreCase = true)) {
return 1
} else {
// check if prefix matches any of the words
val words = nameClean.split(" ").toTypedArray() + name.split(" ").toTypedArray()
for (word in words) {
if (word.startsWith(prefix, ignoreCase = true)) {
return 2
}
}
}
// finally check if the prefix matches any part of the name
if (nameClean.contains(prefix, ignoreCase = true) || name.contains(prefix, ignoreCase = true)) {
return 3
}
return 100
}
}

View File

@ -20,6 +20,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.databinding.MessagesListFragmentBinding
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
@ -68,7 +69,11 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
}
// load & configure the adapter
adapter.items = items
adapter.items = items.toMutableList()
adapter.items.add(0, MessagesSearch().also {
it.count = items.size
})
adapter.allItems = adapter.items.toMutableList()
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
b.list.adapter = adapter
b.list.apply {

View File

@ -13,6 +13,7 @@ import android.widget.ImageView
import android.widget.TextView
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.asSpannable
import pl.szczodrzynski.edziennik.cleanDiacritics
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.resolveAttr
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils.getProfileImage
@ -85,36 +86,15 @@ class MessagesComposeSuggestionAdapter(
val list = mutableListOf<Teacher>()
originalList.forEach { teacher ->
val teacherFullName = teacher.fullName
teacher.recipientWeight = 0
teacher.recipientWeight = getMatchWeight(teacher.fullName, prefixString)
// First match against the whole, non-split value
var found = false
if (teacherFullName.startsWith(prefixString, ignoreCase = true)) {
teacher.recipientWeight = 1
found = true
} else {
// check if prefix matches any of the words
val words = teacherFullName.split(" ").toTypedArray()
for (word in words) {
if (word.startsWith(prefixString, ignoreCase = true)) {
teacher.recipientWeight = 2
found = true
break
}
}
}
// finally check if the prefix matches any part of the name
if (!found && teacherFullName.contains(prefixString, ignoreCase = true)) {
teacher.recipientWeight = 3
}
if (teacher.recipientWeight != 0) {
teacher.recipientDisplayName = teacherFullName.asSpannable(
if (teacher.recipientWeight != 100) {
teacher.recipientDisplayName = teacher.fullName.asSpannable(
StyleSpan(BOLD),
BackgroundColorSpan(R.attr.colorControlHighlight.resolveAttr(context)),
substring = prefixString,
ignoreCase = true
ignoreCase = true,
ignoreDiacritics = true
)
list += teacher
}
@ -137,4 +117,29 @@ class MessagesComposeSuggestionAdapter(
}
}
private fun getMatchWeight(name: CharSequence?, prefix: String): Int {
if (name == null)
return 100
val nameClean = name.cleanDiacritics()
// First match against the whole, non-split value
if (nameClean.startsWith(prefix, ignoreCase = true) || name.startsWith(prefix, ignoreCase = true)) {
return 1
} else {
// check if prefix matches any of the words
val words = nameClean.split(" ").toTypedArray() + name.split(" ").toTypedArray()
for (word in words) {
if (word.startsWith(prefix, ignoreCase = true)) {
return 2
}
}
}
// finally check if the prefix matches any part of the name
if (nameClean.contains(prefix, ignoreCase = true) || name.contains(prefix, ignoreCase = true)) {
return 3
}
return 100
}
}

View File

@ -0,0 +1,11 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-5.
*/
package pl.szczodrzynski.edziennik.ui.modules.messages.models
class MessagesSearch {
var isFocused = false
var searchText = ""
var count = 0
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-5.
*/
package pl.szczodrzynski.edziennik.ui.modules.messages.viewholder
import android.graphics.Typeface
import android.text.style.BackgroundColorSpan
import android.text.style.StyleSpan
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.databinding.MessagesListItemBinding
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils
import pl.szczodrzynski.edziennik.utils.models.Date
class MessageViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: MessagesListItemBinding = MessagesListItemBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<MessageFull, MessagesAdapter> {
companion object {
private const val TAG = "MessageViewHolder"
}
override fun onBind(activity: AppCompatActivity, app: App, item: MessageFull, position: Int, adapter: MessagesAdapter) {
val manager = app.gradesManager
item.recipients?.forEach { recipient ->
if (recipient.fullName == null) {
recipient.fullName = adapter.teachers.firstOrNull { it.id == recipient.id }?.fullName ?: ""
}
}
b.messageSubject.text = item.subject
b.messageDate.text = Date.fromMillis(item.addedDate).formattedStringShort
b.messageAttachmentImage.isVisible = item.hasAttachments
val text = item.body?.take(200) ?: ""
b.messageBody.text = MessagesUtils.htmlToSpannable(activity, text)
val isRead = item.type == Message.TYPE_SENT || item.type == Message.TYPE_DRAFT || item.seen
val typeface = if (isRead) adapter.typefaceNormal else adapter.typefaceBold
val style = if (isRead) R.style.NavView_TextView_Small else R.style.NavView_TextView_Normal
// set text styles
b.messageSender.setTextAppearance(activity, style)
b.messageSender.typeface = typeface
b.messageSubject.setTextAppearance(activity, style)
b.messageSubject.typeface = typeface
b.messageDate.setTextAppearance(activity, style)
b.messageDate.typeface = typeface
val messageInfo = MessagesUtils.getMessageInfo(app, item, 48, 24, 18, 12)
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage)
b.messageSender.text = messageInfo.profileName
item.searchHighlightText?.let { highlight ->
val colorHighlight = R.attr.colorControlHighlight.resolveAttr(activity)
b.messageSubject.text = b.messageSubject.text.asSpannable(
StyleSpan(Typeface.BOLD), BackgroundColorSpan(colorHighlight),
substring = highlight, ignoreCase = true, ignoreDiacritics = true)
b.messageSender.text = b.messageSender.text.asSpannable(
StyleSpan(Typeface.BOLD), BackgroundColorSpan(colorHighlight),
substring = highlight, ignoreCase = true, ignoreDiacritics = true)
}
adapter.onItemClick?.let { listener ->
b.root.onClick { listener(item) }
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-5.
*/
package pl.szczodrzynski.edziennik.ui.modules.messages.viewholder
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.databinding.MessagesListItemSearchBinding
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
class SearchViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: MessagesListItemSearchBinding = MessagesListItemSearchBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<MessagesSearch, MessagesAdapter> {
companion object {
private const val TAG = "SearchViewHolder"
}
override fun onBind(activity: AppCompatActivity, app: App, item: MessagesSearch, position: Int, adapter: MessagesAdapter) {
b.searchEdit.removeTextChangedListener(adapter.textWatcher)
b.searchEdit.addTextChangedListener(adapter.textWatcher)
/*b.searchEdit.setOnKeyboardListener(object : TextInputKeyboardEdit.KeyboardListener {
override fun onStateChanged(keyboardEditText: TextInputKeyboardEdit, showing: Boolean) {
item.isFocused = showing
}
})*/
/*if (b.searchEdit.text.toString() != item.searchText) {
b.searchEdit.setText(item.searchText)
b.searchEdit.setSelection(item.searchText.length)
}*/
//b.searchLayout.helperText = app.getString(R.string.messages_search_results, item.count)
/*if (item.isFocused && !b.searchEdit.isFocused)
b.searchEdit.requestFocus()*/
}
}

View File

@ -10,9 +10,9 @@ import android.util.AttributeSet
import android.view.KeyEvent
import android.view.KeyEvent.KEYCODE_BACK
import androidx.annotation.NonNull
import com.google.android.material.textfield.TextInputEditText
import androidx.appcompat.widget.AppCompatEditText
class TextInputKeyboardEdit : TextInputEditText {
class TextInputKeyboardEdit : AppCompatEditText {
/**
* Keyboard Listener
@ -53,4 +53,4 @@ class TextInputKeyboardEdit : TextInputEditText {
interface KeyboardListener {
fun onStateChanged(keyboardEditText: TextInputKeyboardEdit, showing: Boolean)
}
}
}

View File

@ -47,7 +47,7 @@
app:counterEnabled="true"
tools:counterMaxLength="180">
<com.google.android.material.textfield.TextInputEditText
<pl.szczodrzynski.edziennik.utils.TextInputKeyboardEdit
android:id="@+id/subject"
style="@style/Widget.MaterialComponents.TextInputEditText.FilledBox"
android:layout_width="match_parent"
@ -69,7 +69,7 @@
app:boxBackgroundMode="filled"
app:counterEnabled="true"
tools:counterMaxLength="1983">
<androidx.appcompat.widget.AppCompatEditText
<pl.szczodrzynski.edziennik.utils.TextInputKeyboardEdit
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-4-5.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/searchLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:hint="@string/messages_search"
app:hintEnabled="true"
tools:helperText="Znaleziono 400 wiadomości">
<pl.szczodrzynski.edziennik.utils.TextInputKeyboardEdit
android:id="@+id/searchEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
</layout>

View File

@ -1289,4 +1289,6 @@
<string name="menu_lab">Laboratorium</string>
<string name="messages_no_data">Nie masz żadnych wiadomości.</string>
<string name="messages_tab_deleted">Usunięte</string>
<string name="messages_search">Szukaj</string>
<string name="messages_search_results">Znaleziono %d wiadomości</string>
</resources>