2018-01-04 19:52:25 +01:00
|
|
|
package jp.juggler.subwaytooter.dialog
|
|
|
|
|
|
|
|
import android.app.Dialog
|
2022-05-29 15:38:21 +02:00
|
|
|
import android.graphics.Rect
|
2019-09-14 22:09:52 +02:00
|
|
|
import android.graphics.drawable.PictureDrawable
|
2022-05-29 15:38:21 +02:00
|
|
|
import android.text.Editable
|
|
|
|
import android.text.TextWatcher
|
2021-11-08 10:29:11 +01:00
|
|
|
import android.util.TypedValue
|
2022-05-29 15:38:21 +02:00
|
|
|
import android.view.Gravity
|
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
|
|
|
import android.view.WindowManager
|
|
|
|
import android.widget.ImageButton
|
|
|
|
import android.widget.ImageView
|
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
|
|
import androidx.appcompat.widget.AppCompatButton
|
|
|
|
import androidx.appcompat.widget.AppCompatImageView
|
2021-11-18 18:09:22 +01:00
|
|
|
import androidx.appcompat.widget.AppCompatTextView
|
2022-05-29 15:38:21 +02:00
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
import androidx.recyclerview.widget.GridLayoutManager
|
|
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
|
|
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
|
2019-09-14 22:09:52 +02:00
|
|
|
import com.bumptech.glide.Glide
|
2022-05-29 15:38:21 +02:00
|
|
|
import com.google.android.flexbox.FlexboxLayout
|
|
|
|
import jp.juggler.subwaytooter.App1
|
|
|
|
import jp.juggler.subwaytooter.R
|
|
|
|
import jp.juggler.subwaytooter.databinding.DlgPickerEmojiBinding
|
2021-05-19 22:14:30 +02:00
|
|
|
import jp.juggler.subwaytooter.emoji.*
|
2021-11-20 01:36:43 +01:00
|
|
|
import jp.juggler.subwaytooter.global.appPref
|
2021-11-06 04:00:29 +01:00
|
|
|
import jp.juggler.subwaytooter.pref.PrefB
|
|
|
|
import jp.juggler.subwaytooter.pref.PrefS
|
|
|
|
import jp.juggler.subwaytooter.pref.put
|
2020-05-16 16:59:18 +02:00
|
|
|
import jp.juggler.subwaytooter.table.SavedAccount
|
2022-05-29 15:38:21 +02:00
|
|
|
import jp.juggler.subwaytooter.util.minHeightCompat
|
|
|
|
import jp.juggler.subwaytooter.util.minWidthCompat
|
2018-01-04 19:52:25 +01:00
|
|
|
import jp.juggler.subwaytooter.view.NetworkEmojiView
|
2019-01-19 03:36:40 +01:00
|
|
|
import jp.juggler.util.*
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private class EmojiPicker(
|
|
|
|
private val activity: AppCompatActivity,
|
2021-06-20 15:12:25 +02:00
|
|
|
private val accessInfo: SavedAccount?,
|
2022-05-29 15:38:21 +02:00
|
|
|
private val closeOnSelected: Boolean,
|
|
|
|
private val onPicked: (EmojiBase, bInstanceHasCustomEmoji: Boolean) -> Unit,
|
|
|
|
) {
|
2021-02-22 22:33:54 +01:00
|
|
|
companion object {
|
2022-05-29 15:38:21 +02:00
|
|
|
private val log = LogCategory("EmojiPicker")
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private const val VT_CATEGORY = 0
|
|
|
|
private const val VT_CUSTOM_EMOJI = 1
|
|
|
|
private const val VT_TWEMOJI = 2
|
|
|
|
private const val VT_COMPAT_EMOJI = 3
|
|
|
|
private const val VT_SPACE = 4
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private const val gridCols = 6
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private fun EmojiCategory.getUnicodeEmojis() =
|
|
|
|
emojiList.map { PickerItemUnicode(unicodeEmoji = it) }
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private class SkinTone(val codeInt: Int) {
|
|
|
|
val code = StringBuilder().apply { appendCodePoint(codeInt) }.toString()
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private sealed interface PickerItem
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private class PickerItemUnicode(val unicodeEmoji: UnicodeEmoji) : PickerItem
|
|
|
|
private class PickerItemCustom(val customEmoji: CustomEmoji) : PickerItem
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private object PickerItemSpace : PickerItem
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private class PickerItemCategory(
|
|
|
|
var name: String,
|
|
|
|
val category: EmojiCategory? = null,
|
|
|
|
) : PickerItem {
|
|
|
|
val items = ArrayList<PickerItem>()
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
fun createFiltered(keywordLower: String?) =
|
|
|
|
PickerItemCategory(name = name, category = category).also { dst ->
|
|
|
|
dst.items.addAll(
|
|
|
|
if (keywordLower.isNullOrEmpty()) {
|
|
|
|
items
|
|
|
|
} else {
|
|
|
|
items.filter {
|
|
|
|
when (it) {
|
|
|
|
is PickerItemCustom ->
|
|
|
|
it.customEmoji.shortcode.contains(keywordLower)
|
|
|
|
is PickerItemUnicode ->
|
|
|
|
it.unicodeEmoji.namesLower.any { n -> n.contains(keywordLower) }
|
|
|
|
else -> false
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-06-20 15:12:25 +02:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private val views = DlgPickerEmojiBinding.inflate(activity.layoutInflater)
|
|
|
|
|
|
|
|
private lateinit var pickerCategries: List<PickerItemCategory>
|
|
|
|
|
|
|
|
private val adapter = GridAdapter()
|
|
|
|
|
|
|
|
private val ibSkinTone = listOf(
|
|
|
|
Pair(R.id.btnSkinTone0, 0),
|
|
|
|
Pair(R.id.btnSkinTone1, 0x1F3FB),
|
|
|
|
Pair(R.id.btnSkinTone2, 0x1F3FC),
|
|
|
|
Pair(R.id.btnSkinTone3, 0x1F3FD),
|
|
|
|
Pair(R.id.btnSkinTone4, 0x1F3FE),
|
|
|
|
Pair(R.id.btnSkinTone5, 0x1F3FF),
|
|
|
|
).map { (btnId, skinToneCode) ->
|
|
|
|
views.root.findViewById<ImageButton>(btnId).apply {
|
|
|
|
tag = SkinTone(skinToneCode)
|
|
|
|
setOnClickListener {
|
|
|
|
selectedTone = (it.tag as SkinTone)
|
|
|
|
showSkinTone()
|
|
|
|
@Suppress("NotifyDataSetChanged")
|
|
|
|
adapter.notifyDataSetChanged()
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-15 08:45:22 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private val gridSize = (0.5f + 48f * activity.resources.displayMetrics.density).toInt()
|
|
|
|
private val matchParent = RecyclerView.LayoutParams.MATCH_PARENT
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private val useTwemoji = PrefB.bpUseTwemoji()
|
|
|
|
private val disableAnimation = PrefB.bpDisableEmojiAnimation()
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private var selectedTone: SkinTone = (ibSkinTone[0].tag as SkinTone)
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private lateinit var dialog: Dialog
|
2021-02-22 22:33:54 +01:00
|
|
|
|
|
|
|
private var bInstanceHasCustomEmoji = false
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private val textWatcher = object : TextWatcher {
|
|
|
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
|
|
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
|
|
|
|
override fun afterTextChanged(s: Editable?) {
|
|
|
|
showFiltered(null, s?.toString())
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-06-20 15:12:25 +02:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private fun updateRecent(
|
|
|
|
targetName: String,
|
|
|
|
targetInstance: String?,
|
|
|
|
) {
|
|
|
|
// Recentをロード(他インスタンスの絵文字を含む)
|
|
|
|
val list = try {
|
|
|
|
PrefS.spEmojiPickerRecent().decodeJsonArray().objectList()
|
|
|
|
} catch (_: Throwable) {
|
|
|
|
emptyList()
|
|
|
|
}.toMutableList()
|
2021-06-20 15:12:25 +02:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
// 選択された絵文字と同じ項目を除去
|
|
|
|
// 項目が増えすぎたら減らす
|
|
|
|
// ユニコード絵文字256個、カスタム絵文字はインスタンス別256個まで
|
|
|
|
var nCount = 0
|
|
|
|
val it = list.iterator()
|
|
|
|
while (it.hasNext()) {
|
|
|
|
val item = it.next()
|
|
|
|
val itemInstance = item.string("instance")
|
|
|
|
val itemName = item.string("name")
|
|
|
|
if (itemInstance == targetInstance) {
|
|
|
|
if (itemName == targetName || ++nCount >= 256) {
|
|
|
|
it.remove()
|
2021-06-20 15:12:25 +02:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
// 先頭に項目を追加
|
|
|
|
list.add(0, JsonObject().apply {
|
|
|
|
put("name", targetName)
|
|
|
|
targetInstance?.let { put("instance", it) }
|
|
|
|
})
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
// 保存する
|
2021-02-22 22:33:54 +01:00
|
|
|
try {
|
2022-05-29 15:38:21 +02:00
|
|
|
val sv = list.toJsonArray().toString()
|
|
|
|
appPref.edit().put(PrefS.spEmojiPickerRecent, sv).apply()
|
2021-02-22 22:33:54 +01:00
|
|
|
} catch (ex: Throwable) {
|
2022-05-29 15:38:21 +02:00
|
|
|
log.e(ex)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private fun setItemClick(view: View) {
|
|
|
|
view.setOnClickListener {
|
|
|
|
val targetEmoji: EmojiBase
|
|
|
|
val targetName: String
|
|
|
|
val targetInstance: String?
|
|
|
|
when (val item = it.getTag(R.id.btnAbout)) {
|
|
|
|
is PickerItemUnicode -> {
|
|
|
|
targetEmoji = applySkinTone(item.unicodeEmoji)
|
|
|
|
targetName = targetEmoji.unifiedName
|
|
|
|
targetInstance = null
|
|
|
|
}
|
|
|
|
is PickerItemCustom -> {
|
|
|
|
targetEmoji = item.customEmoji
|
|
|
|
targetName = accessInfo!!.apiHost.ascii
|
|
|
|
targetInstance = null
|
|
|
|
}
|
|
|
|
else -> return@setOnClickListener
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
if (closeOnSelected) dialog.dismissSafe()
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
updateRecent(targetName, targetInstance)
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
onPicked(targetEmoji, bInstanceHasCustomEmoji)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private sealed class ViewHolderBase(view: View) : RecyclerView.ViewHolder(view) {
|
|
|
|
abstract fun bind(item: PickerItem)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private inner class VhCategory(
|
|
|
|
val view: AppCompatTextView = AppCompatTextView(activity),
|
|
|
|
) : ViewHolderBase(view) {
|
|
|
|
init {
|
|
|
|
view.layoutParams = RecyclerView.LayoutParams(matchParent, gridSize)
|
|
|
|
view.gravity = Gravity.START or Gravity.CENTER_VERTICAL
|
|
|
|
view.includeFontPadding = false
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
override fun bind(item: PickerItem) {
|
|
|
|
if (item is PickerItemCategory) {
|
|
|
|
view.text = item.name
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private inner class VhSpace(
|
|
|
|
view: View = View(activity),
|
|
|
|
) : ViewHolderBase(view) {
|
|
|
|
init {
|
|
|
|
view.layoutParams = RecyclerView.LayoutParams(matchParent, gridSize)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
override fun bind(item: PickerItem) {
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private inner class VhCustomEmoji(
|
|
|
|
val view: NetworkEmojiView = NetworkEmojiView(activity),
|
|
|
|
) : ViewHolderBase(view) {
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
init {
|
|
|
|
setItemClick(view)
|
|
|
|
view.layoutParams = RecyclerView.LayoutParams(matchParent, gridSize)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
override fun bind(item: PickerItem) {
|
|
|
|
if (activity.isDestroyed) return
|
|
|
|
if (item is PickerItemCustom) {
|
|
|
|
view.setTag(R.id.btnAbout, item)
|
|
|
|
view.setEmoji(
|
|
|
|
if (disableAnimation) {
|
|
|
|
item.customEmoji.staticUrl
|
|
|
|
} else {
|
|
|
|
item.customEmoji.url
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private inner class VhTwemoji(
|
|
|
|
val view: AppCompatImageView = AppCompatImageView(activity),
|
|
|
|
) : ViewHolderBase(view) {
|
|
|
|
init {
|
|
|
|
setItemClick(view)
|
|
|
|
view.layoutParams = RecyclerView.LayoutParams(matchParent, gridSize)
|
|
|
|
view.scaleType = ImageView.ScaleType.FIT_CENTER
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
override fun bind(item: PickerItem) {
|
|
|
|
if (activity.isDestroyed) return
|
|
|
|
if (item is PickerItemUnicode) {
|
|
|
|
view.setTag(R.id.btnAbout, item)
|
|
|
|
val emoji = applySkinTone(item.unicodeEmoji)
|
|
|
|
if (emoji.isSvg) {
|
|
|
|
Glide.with(activity)
|
|
|
|
.`as`(PictureDrawable::class.java)
|
|
|
|
.load("file:///android_asset/${emoji.assetsName}")
|
|
|
|
.into(view)
|
|
|
|
} else {
|
|
|
|
Glide.with(activity)
|
|
|
|
.load(emoji.drawableId)
|
|
|
|
.into(view)
|
|
|
|
}
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private inner class VhAppCompatEmoji(
|
|
|
|
val view: AppCompatTextView = AppCompatTextView(activity),
|
|
|
|
) : ViewHolderBase(view) {
|
|
|
|
init {
|
|
|
|
setItemClick(view)
|
|
|
|
view.layoutParams = RecyclerView.LayoutParams(matchParent, gridSize)
|
|
|
|
view.gravity = Gravity.CENTER
|
|
|
|
view.setLineSpacing(0f, 0f)
|
|
|
|
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, gridSize.toFloat() * 0.7f)
|
|
|
|
view.includeFontPadding = false
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
override fun bind(item: PickerItem) {
|
|
|
|
if (activity.isDestroyed) return
|
|
|
|
if (item is PickerItemUnicode) {
|
2021-02-22 22:33:54 +01:00
|
|
|
view.setTag(R.id.btnAbout, item)
|
2022-05-29 15:38:21 +02:00
|
|
|
val unicodeEmoji = applySkinTone(item.unicodeEmoji)
|
|
|
|
view.text = unicodeEmoji.unifiedCode
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
class GridDecoration(private val space: Int) : ItemDecoration() {
|
|
|
|
override fun getItemOffsets(
|
|
|
|
outRect: Rect,
|
2021-06-20 15:12:25 +02:00
|
|
|
view: View,
|
2022-05-29 15:38:21 +02:00
|
|
|
parent: RecyclerView,
|
|
|
|
state: RecyclerView.State,
|
2021-06-20 15:12:25 +02:00
|
|
|
) {
|
2022-05-29 15:38:21 +02:00
|
|
|
outRect.left = space
|
|
|
|
outRect.right = space
|
|
|
|
outRect.bottom = space
|
|
|
|
// Add top margin only for the first item to avoid double space between items
|
|
|
|
outRect.top = if (parent.getChildLayoutPosition(view) == 0) {
|
|
|
|
space
|
|
|
|
} else {
|
|
|
|
0
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private inner class GridAdapter : RecyclerView.Adapter<ViewHolderBase>() {
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
var list: List<PickerItem> = emptyList()
|
|
|
|
set(value) {
|
|
|
|
field = value
|
|
|
|
@Suppress("NotifyDataSetChanged")
|
|
|
|
notifyDataSetChanged()
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
val spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
|
|
|
override fun getSpanSize(position: Int) =
|
|
|
|
when {
|
|
|
|
list[position] is PickerItemCategory -> gridCols
|
|
|
|
else -> 1
|
|
|
|
}
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
override fun getItemCount() = list.size
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
override fun getItemViewType(position: Int) =
|
|
|
|
when (list[position]) {
|
|
|
|
is PickerItemSpace -> VT_SPACE
|
|
|
|
is PickerItemCategory -> VT_CATEGORY
|
|
|
|
is PickerItemCustom -> VT_CUSTOM_EMOJI
|
|
|
|
is PickerItemUnicode -> when {
|
|
|
|
useTwemoji -> VT_TWEMOJI
|
|
|
|
else -> VT_COMPAT_EMOJI
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
|
|
|
|
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int) =
|
|
|
|
when (viewType) {
|
|
|
|
VT_CATEGORY -> VhCategory()
|
|
|
|
VT_CUSTOM_EMOJI -> VhCustomEmoji()
|
|
|
|
VT_TWEMOJI -> VhTwemoji()
|
|
|
|
VT_COMPAT_EMOJI -> VhAppCompatEmoji()
|
|
|
|
VT_SPACE -> VhSpace()
|
|
|
|
else -> error("unknown viewType=$viewType")
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
|
|
|
|
override fun onBindViewHolder(viewHolder: ViewHolderBase, position: Int) {
|
|
|
|
viewHolder.bind(list[position])
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
// 最近使用した絵文字のPickerCategoryを作る
|
|
|
|
private fun createRecentsCategory() =
|
|
|
|
PickerItemCategory(
|
|
|
|
name = activity.getString(R.string.emoji_category_recent),
|
|
|
|
category = EmojiCategory.Recent,
|
|
|
|
).apply {
|
|
|
|
val customEmojiMap = accessInfo?.let { App1.custom_emoji_lister.getMapNonBlocking(it) }
|
|
|
|
|
|
|
|
for (item in PrefS.spEmojiPickerRecent().decodeJsonArray().objectList()) {
|
|
|
|
val name = item.string("name")
|
|
|
|
val instance = item.string("instance")
|
|
|
|
try {
|
|
|
|
name ?: error("missing emoji name")
|
|
|
|
if (instance == null) {
|
|
|
|
EmojiMap.shortNameMap[name]?.let {
|
|
|
|
items.add(PickerItemUnicode(unicodeEmoji = it))
|
|
|
|
}
|
|
|
|
} else if (instance == accessInfo?.apiHost?.ascii) {
|
|
|
|
customEmojiMap?.get(name)?.let {
|
|
|
|
items.add(PickerItemCustom(customEmoji = it))
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
} catch (ex: Throwable) {
|
|
|
|
log.w(ex, "can't add emoji. $name, $instance")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}.takeIf { it.items.isNotEmpty() }
|
|
|
|
|
|
|
|
private suspend fun createCustomEmojiCategories(): List<PickerItemCategory> {
|
|
|
|
accessInfo ?: error("missing accessInfo")
|
|
|
|
val context = activity
|
|
|
|
val srcList = App1.custom_emoji_lister.getList(accessInfo)
|
|
|
|
val nameMap = HashMap<String, PickerItemCategory>()
|
|
|
|
for (emoji in srcList) {
|
|
|
|
if (!emoji.visibleInPicker) continue
|
|
|
|
val categoryName = emoji.category ?: ""
|
|
|
|
(nameMap[categoryName]
|
|
|
|
?: PickerItemCategory(
|
|
|
|
name = categoryName,
|
|
|
|
category = EmojiCategory.Custom,
|
|
|
|
).also { nameMap[categoryName] = it })
|
|
|
|
.items.add(PickerItemCustom(emoji))
|
|
|
|
}
|
|
|
|
val otherCategory = nameMap[""]
|
|
|
|
|
|
|
|
// カテゴリ名の頭に「カスタム」を追加
|
|
|
|
return nameMap.values.onEach {
|
|
|
|
it.name = when (it) {
|
|
|
|
otherCategory -> when (nameMap.size) {
|
|
|
|
0 -> context.getString(R.string.emoji_category_custom)
|
|
|
|
else -> context.getString(
|
|
|
|
R.string.emoji_picker_custom_of,
|
|
|
|
context.getString(R.string.others)
|
|
|
|
)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
else ->
|
|
|
|
context.getString(R.string.emoji_picker_custom_of, it.name)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}.sortedWith { l, r ->
|
|
|
|
if (l == otherCategory) 1
|
|
|
|
else if (r == otherCategory) -1
|
|
|
|
else l.name.compareTo(r.name)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private suspend fun buildCategoryList() = buildList {
|
|
|
|
// 最近使った絵文字
|
|
|
|
try {
|
|
|
|
createRecentsCategory()?.let { add(it) }
|
|
|
|
} catch (ex: Throwable) {
|
|
|
|
log.w(ex)
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
// カスタム絵文字
|
2021-02-22 22:33:54 +01:00
|
|
|
try {
|
2022-05-29 15:38:21 +02:00
|
|
|
addAll(createCustomEmojiCategories())
|
|
|
|
} catch (ex: Throwable) {
|
|
|
|
log.w(ex)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
arrayOf(
|
|
|
|
EmojiCategory.People,
|
|
|
|
EmojiCategory.ComplexTones,
|
|
|
|
EmojiCategory.Nature,
|
|
|
|
EmojiCategory.Foods,
|
|
|
|
EmojiCategory.Activities,
|
|
|
|
EmojiCategory.Places,
|
|
|
|
EmojiCategory.Objects,
|
|
|
|
EmojiCategory.Symbols,
|
|
|
|
EmojiCategory.Flags,
|
|
|
|
).forEach { category ->
|
|
|
|
val pc = PickerItemCategory(
|
|
|
|
category = category,
|
|
|
|
name = activity.getString(category.titleId),
|
|
|
|
)
|
|
|
|
pc.items.addAll(category.getUnicodeEmojis())
|
|
|
|
add(pc)
|
|
|
|
}
|
|
|
|
if (PrefB.bpEmojiPickerCategoryOther(activity)) {
|
|
|
|
val category = EmojiCategory.Others
|
|
|
|
val pc = PickerItemCategory(
|
|
|
|
category = category,
|
|
|
|
name = activity.getString(category.titleId),
|
|
|
|
)
|
|
|
|
pc.items.addAll(category.getUnicodeEmojis())
|
|
|
|
add(pc)
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private fun applySkinTone(emojiArg: UnicodeEmoji): UnicodeEmoji {
|
|
|
|
// トーン指定がないなら元のコード
|
|
|
|
val selectedTone = selectedTone.takeIf { it.codeInt > 0 } ?: return emojiArg
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
var emoji = emojiArg
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
// Recentなどでは既にsuffixがついた名前が用意されている
|
|
|
|
// suffixを除去する
|
|
|
|
emoji.toneParent?.let { emoji = it }
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
// 指定したトーンのサフィックスを追加して、絵文字が存在すればその名前にする
|
|
|
|
emoji.toneChildren.find { it.first == selectedTone.code }
|
|
|
|
?.let { return it.second }
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
// なければトーンなしの絵文字
|
|
|
|
return emoji
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private fun showSkinTone() {
|
|
|
|
val selectedTone = selectedTone
|
|
|
|
ibSkinTone.forEach {
|
|
|
|
if (selectedTone == it.tag) {
|
|
|
|
it.setImageResource(R.drawable.check_mark)
|
|
|
|
} else {
|
|
|
|
it.setImageDrawable(null)
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
private fun showFiltered(
|
|
|
|
selectedCategory: EmojiCategory?,
|
|
|
|
selectedKeyword: String?,
|
|
|
|
) {
|
|
|
|
adapter.list = buildList {
|
|
|
|
val keywordLower = selectedKeyword?.lowercase()?.trim()
|
|
|
|
pickerCategries.filter {
|
|
|
|
if (selectedCategory == null) {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
it.category == selectedCategory
|
|
|
|
}
|
|
|
|
}.mapNotNull { category ->
|
|
|
|
category.createFiltered(keywordLower)
|
|
|
|
.takeIf { it.items.isNotEmpty() }
|
|
|
|
}.forEach {
|
|
|
|
add(it)
|
|
|
|
addAll(it.items)
|
|
|
|
val mod = it.items.size % gridCols
|
|
|
|
if (mod > 0) {
|
|
|
|
repeat(gridCols - mod) {
|
|
|
|
add(PickerItemSpace)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
}
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
suspend fun start() {
|
|
|
|
pickerCategries = buildCategoryList()
|
|
|
|
|
|
|
|
bInstanceHasCustomEmoji = pickerCategries.any { it.category == EmojiCategory.Custom }
|
|
|
|
|
|
|
|
val wrapContent = FlexboxLayout.LayoutParams.WRAP_CONTENT
|
|
|
|
val density = activity.resources.displayMetrics.density
|
|
|
|
val minWidth = (density * 48f + 0.5f).toInt()
|
|
|
|
val padTb = (density * 4f + 0.5f).toInt()
|
|
|
|
val padLr = (density * 6f + 0.5f).toInt()
|
|
|
|
arrayOf(
|
|
|
|
null,
|
|
|
|
EmojiCategory.Recent,
|
|
|
|
EmojiCategory.Custom,
|
|
|
|
EmojiCategory.People,
|
|
|
|
EmojiCategory.ComplexTones,
|
|
|
|
EmojiCategory.Nature,
|
|
|
|
EmojiCategory.Foods,
|
|
|
|
EmojiCategory.Activities,
|
|
|
|
EmojiCategory.Places,
|
|
|
|
EmojiCategory.Objects,
|
|
|
|
EmojiCategory.Symbols,
|
|
|
|
EmojiCategory.Flags,
|
|
|
|
EmojiCategory.Others,
|
|
|
|
).forEach {
|
|
|
|
AppCompatButton(activity).apply {
|
|
|
|
layoutParams = FlexboxLayout.LayoutParams(wrapContent, wrapContent)
|
|
|
|
background =
|
|
|
|
ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent_round6dp)
|
|
|
|
minWidthCompat = minWidth
|
|
|
|
minHeightCompat = minWidth
|
|
|
|
setPadding(padLr, padTb, padLr, padTb)
|
|
|
|
text = activity?.getString(it?.titleId ?: R.string.all)
|
|
|
|
setOnClickListener { _ ->
|
|
|
|
views.etFilter.removeTextChangedListener(textWatcher)
|
|
|
|
views.etFilter.setText("")
|
|
|
|
views.etFilter.addTextChangedListener(textWatcher)
|
|
|
|
showFiltered(it, null)
|
|
|
|
}
|
|
|
|
}.let { views.llCategories.addView(it) }
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
views.etFilter.addTextChangedListener(textWatcher)
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
showFiltered(null, null)
|
2021-02-22 22:33:54 +01:00
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
views.rvGrid.adapter = adapter
|
|
|
|
views.rvGrid.layoutManager = GridLayoutManager(
|
|
|
|
activity,
|
|
|
|
gridCols,
|
|
|
|
RecyclerView.VERTICAL,
|
|
|
|
false
|
|
|
|
).also {
|
|
|
|
it.spanSizeLookup = adapter.spanSizeLookup
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
val cellSpacing = (density * 1f + 0.5f).toInt()
|
|
|
|
views.rvGrid.addItemDecoration(GridDecoration(cellSpacing))
|
|
|
|
showSkinTone()
|
|
|
|
|
|
|
|
this.dialog = Dialog(activity)
|
|
|
|
dialog.setContentView(views.root)
|
|
|
|
dialog.setCancelable(true)
|
|
|
|
dialog.setCanceledOnTouchOutside(true)
|
|
|
|
val w = dialog.window
|
|
|
|
// XXX Android 11 で SOFT_INPUT_ADJUST_RESIZE はdeprecatedになった
|
|
|
|
@Suppress("DEPRECATION")
|
|
|
|
w?.setSoftInputMode(
|
|
|
|
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE or
|
|
|
|
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
|
|
|
|
)
|
|
|
|
dialog.show()
|
2021-02-22 22:33:54 +01:00
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
2022-05-29 15:38:21 +02:00
|
|
|
|
|
|
|
fun launchEmojiPicker(
|
|
|
|
activity: AppCompatActivity,
|
|
|
|
accessInfo: SavedAccount?,
|
|
|
|
closeOnSelected: Boolean,
|
|
|
|
onPicked: (EmojiBase, bInstanceHasCustomEmoji: Boolean) -> Unit,
|
|
|
|
) = activity.launchAndShowError {
|
|
|
|
EmojiPicker(
|
|
|
|
activity = activity,
|
|
|
|
accessInfo = accessInfo,
|
|
|
|
closeOnSelected = closeOnSelected,
|
|
|
|
onPicked = onPicked,
|
|
|
|
).start()
|
|
|
|
}
|