mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-19 00:26:45 -06:00
Add messages sorting (#1262)
This commit is contained in:
parent
d73aa605f9
commit
b61e63249c
@ -4,6 +4,8 @@ import android.graphics.Typeface
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CompoundButton
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.NO_POSITION
|
||||
@ -11,60 +13,114 @@ import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.enums.MessageFolder
|
||||
import io.github.wulkanowy.databinding.ItemMessageBinding
|
||||
import io.github.wulkanowy.databinding.ItemMessageChipsBinding
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import javax.inject.Inject
|
||||
|
||||
class MessageTabAdapter @Inject constructor() :
|
||||
RecyclerView.Adapter<MessageTabAdapter.ItemViewHolder>() {
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
var onClickListener: (Message, position: Int) -> Unit = { _, _ -> }
|
||||
enum class ViewType { HEADER, ITEM }
|
||||
|
||||
var onItemClickListener: (Message, position: Int) -> Unit = { _, _ -> }
|
||||
var onHeaderClickListener: (chip: CompoundButton, isChecked: Boolean) -> Unit = { _, _ -> }
|
||||
|
||||
var onChangesDetectedListener = {}
|
||||
|
||||
private var items = mutableListOf<Message>()
|
||||
private var items = mutableListOf<MessageTabDataItem>()
|
||||
private var onlyUnread: Boolean? = null
|
||||
private var onlyWithAttachments = false
|
||||
|
||||
fun setDataItems(data: List<Message>) {
|
||||
fun setDataItems(
|
||||
data: List<MessageTabDataItem>,
|
||||
onlyUnread: Boolean?,
|
||||
onlyWithAttachments: Boolean
|
||||
) {
|
||||
if (items.size != data.size) onChangesDetectedListener()
|
||||
val diffResult = DiffUtil.calculateDiff(MessageTabDiffUtil(items, data))
|
||||
items = data.toMutableList()
|
||||
this.onlyUnread = onlyUnread
|
||||
this.onlyWithAttachments = onlyWithAttachments
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (position) {
|
||||
0 -> ViewType.HEADER.ordinal
|
||||
else -> ViewType.ITEM.ordinal
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemMessageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return when (viewType) {
|
||||
ViewType.ITEM.ordinal -> ItemViewHolder(
|
||||
ItemMessageBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
ViewType.HEADER.ordinal -> HeaderViewHolder(
|
||||
ItemMessageChipsBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is ItemViewHolder -> {
|
||||
val item = (items[position] as MessageTabDataItem.MessageItem).message
|
||||
|
||||
with(holder.binding) {
|
||||
val style = if (item.unread) Typeface.BOLD else Typeface.NORMAL
|
||||
with(holder.binding) {
|
||||
val style = if (item.unread) Typeface.BOLD else Typeface.NORMAL
|
||||
|
||||
messageItemAuthor.run {
|
||||
text = if (item.folderId == MessageFolder.SENT.id) item.recipient else item.sender
|
||||
setTypeface(null, style)
|
||||
messageItemAuthor.run {
|
||||
text =
|
||||
if (item.folderId == MessageFolder.SENT.id) item.recipient else item.sender
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemSubject.run {
|
||||
text =
|
||||
if (item.subject.isNotBlank()) item.subject else context.getString(R.string.message_no_subject)
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemDate.run {
|
||||
text = item.date.toFormattedString()
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemAttachmentIcon.visibility =
|
||||
if (item.hasAttachments) View.VISIBLE else View.GONE
|
||||
|
||||
root.setOnClickListener {
|
||||
holder.bindingAdapterPosition.let {
|
||||
if (it != NO_POSITION) onItemClickListener(item, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
messageItemSubject.run {
|
||||
text = if (item.subject.isNotBlank()) item.subject else context.getString(R.string.message_no_subject)
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemDate.run {
|
||||
text = item.date.toFormattedString()
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemAttachmentIcon.visibility = if (item.hasAttachments) View.VISIBLE else View.GONE
|
||||
|
||||
root.setOnClickListener {
|
||||
holder.bindingAdapterPosition.let { if (it != NO_POSITION) onClickListener(item, it) }
|
||||
is HeaderViewHolder -> {
|
||||
with(holder.binding) {
|
||||
if (onlyUnread == null) chipUnread.isVisible = false
|
||||
else {
|
||||
chipUnread.isVisible = true
|
||||
chipUnread.isChecked = onlyUnread!!
|
||||
chipUnread.setOnCheckedChangeListener(onHeaderClickListener)
|
||||
}
|
||||
chipAttachments.isChecked = onlyWithAttachments
|
||||
chipAttachments.setOnCheckedChangeListener(onHeaderClickListener)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
class HeaderViewHolder(val binding: ItemMessageChipsBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
private class MessageTabDiffUtil(private val old: List<Message>, private val new: List<Message>) :
|
||||
private class MessageTabDiffUtil(
|
||||
private val old: List<MessageTabDataItem>,
|
||||
private val new: List<MessageTabDataItem>
|
||||
) :
|
||||
DiffUtil.Callback() {
|
||||
override fun getOldListSize(): Int = old.size
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
package io.github.wulkanowy.ui.modules.message.tab
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
|
||||
sealed class MessageTabDataItem {
|
||||
data class MessageItem(val message: Message) : MessageTabDataItem() {
|
||||
override val id = message.id
|
||||
}
|
||||
|
||||
object Header : MessageTabDataItem() {
|
||||
override val id = Long.MIN_VALUE
|
||||
}
|
||||
|
||||
abstract val id: Long
|
||||
}
|
@ -7,6 +7,7 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.widget.CompoundButton
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@ -48,6 +49,10 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
||||
override val isViewEmpty
|
||||
get() = tabAdapter.itemCount == 0
|
||||
|
||||
override var onlyUnread: Boolean? = false
|
||||
|
||||
override var onlyWithAttachments = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
@ -58,26 +63,33 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentMessageTabBinding.bind(view)
|
||||
messageContainer = binding.messageTabRecycler
|
||||
presenter.onAttachView(this, MessageFolder.valueOf(
|
||||
(savedInstanceState ?: arguments)?.getString(MESSAGE_TAB_FOLDER_ID).orEmpty()
|
||||
))
|
||||
|
||||
val folder = MessageFolder.valueOf(
|
||||
(savedInstanceState ?: requireArguments()).getString(MESSAGE_TAB_FOLDER_ID).orEmpty()
|
||||
)
|
||||
presenter.onAttachView(this, folder)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
with(tabAdapter) {
|
||||
onClickListener = presenter::onMessageItemSelected
|
||||
onItemClickListener = presenter::onMessageItemSelected
|
||||
onHeaderClickListener = ::onChipChecked
|
||||
onChangesDetectedListener = ::resetListPosition
|
||||
}
|
||||
|
||||
with(binding.messageTabRecycler) {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = tabAdapter
|
||||
addItemDecoration(DividerItemDecoration(context))
|
||||
addItemDecoration(DividerItemDecoration(context, false))
|
||||
}
|
||||
with(binding) {
|
||||
messageTabSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||
messageTabSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||
messageTabSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||
messageTabSwipe.setProgressBackgroundColorSchemeColor(
|
||||
requireContext().getThemeAttrColor(
|
||||
R.attr.colorSwipeRefresh
|
||||
)
|
||||
)
|
||||
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
@ -99,8 +111,9 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
||||
})
|
||||
}
|
||||
|
||||
override fun updateData(data: List<Message>) {
|
||||
tabAdapter.setDataItems(data)
|
||||
override fun updateData(data: List<MessageTabDataItem>, hide: Boolean) {
|
||||
if (hide) onlyUnread = null
|
||||
tabAdapter.setDataItems(data, onlyUnread, onlyWithAttachments)
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
@ -143,8 +156,19 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
||||
(parentFragment as? MessageFragment)?.onChildFragmentLoaded()
|
||||
}
|
||||
|
||||
fun onParentLoadData(forceRefresh: Boolean) {
|
||||
presenter.onParentViewLoadData(forceRefresh)
|
||||
fun onParentLoadData(
|
||||
forceRefresh: Boolean,
|
||||
onlyUnread: Boolean? = this.onlyUnread,
|
||||
onlyWithAttachments: Boolean = this.onlyWithAttachments
|
||||
) {
|
||||
presenter.onParentViewLoadData(forceRefresh, onlyUnread, onlyWithAttachments)
|
||||
}
|
||||
|
||||
private fun onChipChecked(chip: CompoundButton, isChecked: Boolean) {
|
||||
when (chip.id) {
|
||||
R.id.chip_unread -> presenter.onUnreadFilterSelected(isChecked)
|
||||
R.id.chip_attachments -> presenter.onAttachmentsFilterSelected(isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
fun onParentDeleteMessage() {
|
||||
|
@ -55,15 +55,15 @@ class MessageTabPresenter @Inject constructor(
|
||||
|
||||
fun onSwipeRefresh() {
|
||||
Timber.i("Force refreshing the $folder message")
|
||||
onParentViewLoadData(true)
|
||||
view?.run { onParentViewLoadData(true, onlyUnread, onlyWithAttachments) }
|
||||
}
|
||||
|
||||
fun onRetry() {
|
||||
view?.run {
|
||||
showErrorView(false)
|
||||
showProgress(true)
|
||||
loadData(true, onlyUnread == true, onlyWithAttachments)
|
||||
}
|
||||
loadData(true)
|
||||
}
|
||||
|
||||
fun onDetailsClick() {
|
||||
@ -71,11 +71,15 @@ class MessageTabPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onDeleteMessage() {
|
||||
loadData(true)
|
||||
view?.run { loadData(true, onlyUnread == true, onlyWithAttachments) }
|
||||
}
|
||||
|
||||
fun onParentViewLoadData(forceRefresh: Boolean) {
|
||||
loadData(forceRefresh)
|
||||
fun onParentViewLoadData(
|
||||
forceRefresh: Boolean,
|
||||
onlyUnread: Boolean? = view?.onlyUnread,
|
||||
onlyWithAttachments: Boolean = view?.onlyWithAttachments == true
|
||||
) {
|
||||
loadData(forceRefresh, onlyUnread == true, onlyWithAttachments)
|
||||
}
|
||||
|
||||
fun onMessageItemSelected(message: Message, position: Int) {
|
||||
@ -83,7 +87,25 @@ class MessageTabPresenter @Inject constructor(
|
||||
view?.openMessage(message)
|
||||
}
|
||||
|
||||
private fun loadData(forceRefresh: Boolean) {
|
||||
fun onUnreadFilterSelected(isChecked: Boolean) {
|
||||
view?.run {
|
||||
onlyUnread = isChecked
|
||||
onParentViewLoadData(false, onlyUnread, onlyWithAttachments)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAttachmentsFilterSelected(isChecked: Boolean) {
|
||||
view?.run {
|
||||
onlyWithAttachments = isChecked
|
||||
onParentViewLoadData(false, onlyUnread, onlyWithAttachments)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData(
|
||||
forceRefresh: Boolean,
|
||||
onlyUnread: Boolean,
|
||||
onlyWithAttachments: Boolean
|
||||
) {
|
||||
Timber.i("Loading $folder message data started")
|
||||
|
||||
flowWithResourceIn {
|
||||
@ -100,7 +122,15 @@ class MessageTabPresenter @Inject constructor(
|
||||
showProgress(false)
|
||||
showContent(true)
|
||||
messages = it.data
|
||||
updateData(getFilteredData(lastSearchQuery))
|
||||
val filteredData = getFilteredData(
|
||||
lastSearchQuery,
|
||||
onlyUnread,
|
||||
onlyWithAttachments
|
||||
)
|
||||
val newItems = listOf(MessageTabDataItem.Header) + filteredData.map {
|
||||
MessageTabDataItem.MessageItem(it)
|
||||
}
|
||||
updateData(newItems, folder.id == MessageFolder.SENT.id)
|
||||
notifyParentDataLoaded()
|
||||
}
|
||||
}
|
||||
@ -108,7 +138,7 @@ class MessageTabPresenter @Inject constructor(
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Loading $folder message result: Success")
|
||||
messages = it.data!!
|
||||
updateData(getFilteredData(lastSearchQuery))
|
||||
updateData(getFilteredData(lastSearchQuery, onlyUnread, onlyWithAttachments))
|
||||
analytics.logEvent(
|
||||
"load_data",
|
||||
"type" to "messages",
|
||||
@ -166,24 +196,42 @@ class MessageTabPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFilteredData(query: String): List<Message> {
|
||||
return if (query.trim().isEmpty()) {
|
||||
messages.sortedByDescending { it.date }
|
||||
private fun getFilteredData(
|
||||
query: String,
|
||||
onlyUnread: Boolean = false,
|
||||
onlyWithAttachments: Boolean = false
|
||||
): List<Message> {
|
||||
if (query.trim().isEmpty()) {
|
||||
val sortedMessages = messages.sortedByDescending { it.date }
|
||||
return when {
|
||||
onlyUnread && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
|
||||
onlyUnread -> sortedMessages.filter { it.unread == onlyUnread }
|
||||
onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments }
|
||||
else -> sortedMessages
|
||||
}
|
||||
} else {
|
||||
messages
|
||||
val sortedMessages = messages
|
||||
.map { it to calculateMatchRatio(it, query) }
|
||||
.sortedByDescending { it.second }
|
||||
.filter { it.second > 5000 }
|
||||
.sortedWith(compareBy<Pair<Message, Int>> { -it.second }.thenByDescending { it.first.date })
|
||||
.filter { it.second > 6000 }
|
||||
.map { it.first }
|
||||
return when {
|
||||
onlyUnread && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
|
||||
onlyUnread -> sortedMessages.filter { it.unread == onlyUnread }
|
||||
onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments }
|
||||
else -> sortedMessages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateData(data: List<Message>) {
|
||||
view?.run {
|
||||
showEmpty(data.isEmpty())
|
||||
showContent(data.isNotEmpty())
|
||||
showContent(true)
|
||||
showErrorView(false)
|
||||
updateData(data)
|
||||
val newItems =
|
||||
listOf(MessageTabDataItem.Header) + data.map { MessageTabDataItem.MessageItem(it) }
|
||||
updateData(newItems, folder.id == MessageFolder.SENT.id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,14 +252,6 @@ class MessageTabPresenter @Inject constructor(
|
||||
FuzzySearch.ratio(
|
||||
query.lowercase(),
|
||||
message.date.toFormattedString("dd.MM.yyyy").lowercase()
|
||||
),
|
||||
FuzzySearch.ratio(
|
||||
query.lowercase(),
|
||||
message.date.toFormattedString("d MMMM").lowercase()
|
||||
),
|
||||
FuzzySearch.ratio(
|
||||
query.lowercase(),
|
||||
message.date.toFormattedString("d MMMM yyyy").lowercase()
|
||||
)
|
||||
).maxOrNull() ?: 0
|
||||
|
||||
|
@ -7,11 +7,15 @@ interface MessageTabView : BaseView {
|
||||
|
||||
val isViewEmpty: Boolean
|
||||
|
||||
var onlyUnread: Boolean?
|
||||
|
||||
var onlyWithAttachments: Boolean
|
||||
|
||||
fun initView()
|
||||
|
||||
fun resetListPosition()
|
||||
|
||||
fun updateData(data: List<Message>)
|
||||
fun updateData(data: List<MessageTabDataItem>, hide: Boolean)
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
||||
|
@ -5,7 +5,10 @@ import android.graphics.Canvas
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class DividerItemDecoration(context: Context) : DividerItemDecoration(context, VERTICAL) {
|
||||
class DividerItemDecoration(
|
||||
context: Context,
|
||||
private val showDividerWithFirstItem: Boolean = true
|
||||
) : DividerItemDecoration(context, VERTICAL) {
|
||||
|
||||
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
canvas.save()
|
||||
@ -13,6 +16,8 @@ class DividerItemDecoration(context: Context) : DividerItemDecoration(context, V
|
||||
val dividerRight = parent.width - parent.paddingRight
|
||||
|
||||
for (i in 0..parent.childCount - 2) {
|
||||
if (!showDividerWithFirstItem && i == 0) continue
|
||||
|
||||
val child = parent.getChildAt(i)
|
||||
val params = child.layoutParams as RecyclerView.LayoutParams
|
||||
val dividerTop = child.bottom + params.bottomMargin
|
||||
|
40
app/src/main/res/layout/item_message_chips.xml
Normal file
40
app/src/main/res/layout/item_message_chips.xml
Normal file
@ -0,0 +1,40 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
||||
android:id="@+id/messageChipsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="16dp"
|
||||
tools:context=".ui.modules.message.tab.MessageTabAdapter">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/messageChipGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_unread"
|
||||
style="@style/Widget.MaterialComponents.Chip.Choice"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/message_chip_only_unread"
|
||||
app:checkedIcon="@drawable/ic_mtrl_chip_checked_black"
|
||||
app:checkedIconEnabled="true"
|
||||
app:checkedIconTint="@color/mtrl_choice_chip_text_color" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_attachments"
|
||||
style="@style/Widget.MaterialComponents.Chip.Choice"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/message_chip_only_with_attachments"
|
||||
app:checkedIcon="@drawable/ic_mtrl_chip_checked_black"
|
||||
app:checkedIconEnabled="true"
|
||||
app:checkedIconTint="@color/mtrl_choice_chip_text_color" />
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -234,6 +234,8 @@
|
||||
<string name="message_not_exists">Message does not exist</string>
|
||||
<string name="message_required_recipients">You need to choose at least 1 recipient</string>
|
||||
<string name="message_content_min_length">The message content must be at least 3 characters</string>
|
||||
<string name="message_chip_only_unread">Only unread</string>
|
||||
<string name="message_chip_only_with_attachments">Only with attachments</string>
|
||||
<plurals name="message_number_item">
|
||||
<item quantity="one">%d message</item>
|
||||
<item quantity="other">%d messages</item>
|
||||
|
@ -36,6 +36,15 @@
|
||||
<item name="android:textColor">?android:textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Wulkanowy.Chip.Choice" parent="Widget.MaterialComponents.Chip.Choice">
|
||||
...
|
||||
<item name="materialThemeOverlay">@style/ThemeOverlay.Wulkanowy.Chip.Choice</item>
|
||||
</style>
|
||||
|
||||
<style name="ThemeOverlay.Wulkanowy.Chip.Choice" parent="">
|
||||
<item name="elevationOverlayEnabled">false</item>
|
||||
</style>
|
||||
|
||||
<style name="WulkanowyTheme.TextAppearanceBottomNavigation">
|
||||
<item name="android:textSize">11sp</item>
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user