Yuito-app-android/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeAutoCompleteAdapter.kt

176 lines
6.9 KiB
Kotlin

/* Copyright 2022 Tusky Contributors
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.components.compose
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.Filter
import android.widget.Filterable
import androidx.annotation.WorkerThread
import com.bumptech.glide.Glide
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.databinding.ItemAutocompleteAccountBinding
import com.keylesspalace.tusky.databinding.ItemAutocompleteEmojiBinding
import com.keylesspalace.tusky.databinding.ItemAutocompleteHashtagBinding
import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.entity.TimelineAccount
import com.keylesspalace.tusky.util.emojify
import com.keylesspalace.tusky.util.loadAvatar
import com.keylesspalace.tusky.util.visible
class ComposeAutoCompleteAdapter(
private val autocompletionProvider: AutocompletionProvider,
private val animateAvatar: Boolean,
private val animateEmojis: Boolean,
private val showBotBadge: Boolean
) : BaseAdapter(), Filterable {
private var resultList: List<AutocompleteResult> = emptyList()
override fun getCount() = resultList.size
override fun getItem(index: Int): AutocompleteResult {
return resultList[index]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getFilter(): Filter {
return object : Filter() {
override fun convertResultToString(resultValue: Any): CharSequence {
return when (resultValue) {
is AutocompleteResult.AccountResult -> formatUsername(resultValue)
is AutocompleteResult.HashtagResult -> formatHashtag(resultValue)
is AutocompleteResult.EmojiResult -> formatEmoji(resultValue)
else -> ""
}
}
@WorkerThread
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filterResults = FilterResults()
if (constraint != null) {
val results = autocompletionProvider.search(constraint.toString())
filterResults.values = results
filterResults.count = results.size
}
return filterResults
}
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
if (results.count > 0) {
resultList = results.values as List<AutocompleteResult>
notifyDataSetChanged()
} else {
notifyDataSetInvalidated()
}
}
}
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val itemViewType = getItemViewType(position)
val context = parent.context
val view: View = convertView ?: run {
val layoutInflater = LayoutInflater.from(context)
val binding = when (itemViewType) {
ACCOUNT_VIEW_TYPE -> ItemAutocompleteAccountBinding.inflate(layoutInflater)
HASHTAG_VIEW_TYPE -> ItemAutocompleteHashtagBinding.inflate(layoutInflater)
EMOJI_VIEW_TYPE -> ItemAutocompleteEmojiBinding.inflate(layoutInflater)
else -> throw AssertionError("unknown view type")
}
binding.root.tag = binding
binding.root
}
when (val binding = view.tag) {
is ItemAutocompleteAccountBinding -> {
val accountResult = getItem(position) as AutocompleteResult.AccountResult
val account = accountResult.account
binding.username.text = context.getString(R.string.post_username_format, account.username)
binding.displayName.text = account.name.emojify(account.emojis, binding.displayName, animateEmojis)
val avatarRadius = context.resources.getDimensionPixelSize(R.dimen.avatar_radius_42dp)
loadAvatar(
account.avatar,
binding.avatar,
avatarRadius,
animateAvatar
)
binding.avatarBadge.visible(showBotBadge && account.bot)
}
is ItemAutocompleteHashtagBinding -> {
val result = getItem(position) as AutocompleteResult.HashtagResult
binding.root.text = formatHashtag(result)
}
is ItemAutocompleteEmojiBinding -> {
val emojiResult = getItem(position) as AutocompleteResult.EmojiResult
val (shortcode, url) = emojiResult.emoji
binding.shortcode.text = context.getString(R.string.emoji_shortcode_format, shortcode)
Glide.with(binding.preview)
.load(url)
.into(binding.preview)
}
}
return view
}
override fun getViewTypeCount() = 3
override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
is AutocompleteResult.AccountResult -> ACCOUNT_VIEW_TYPE
is AutocompleteResult.HashtagResult -> HASHTAG_VIEW_TYPE
is AutocompleteResult.EmojiResult -> EMOJI_VIEW_TYPE
}
}
sealed class AutocompleteResult {
class AccountResult(val account: TimelineAccount) : AutocompleteResult()
class HashtagResult(val hashtag: String) : AutocompleteResult()
class EmojiResult(val emoji: Emoji) : AutocompleteResult()
}
interface AutocompletionProvider {
fun search(token: String): List<AutocompleteResult>
}
companion object {
private const val ACCOUNT_VIEW_TYPE = 0
private const val HASHTAG_VIEW_TYPE = 1
private const val EMOJI_VIEW_TYPE = 2
private fun formatUsername(result: AutocompleteResult.AccountResult): String {
return String.format("@%s", result.account.username)
}
private fun formatHashtag(result: AutocompleteResult.HashtagResult): String {
return String.format("#%s", result.hashtag)
}
private fun formatEmoji(result: AutocompleteResult.EmojiResult): String {
return String.format(":%s:", result.emoji.shortcode)
}
}
}