絵文字ピッカーで長押しすると絵文字の詳細を表示する

This commit is contained in:
tateisu 2023-05-21 00:33:48 +09:00
parent 11e3906f57
commit 934cf684a9
8 changed files with 215 additions and 29 deletions

View File

@ -21,7 +21,7 @@ class APTag(parser: TootParser, jsonArray: JsonArray?) {
init {
jsonArray
?.mapNotNull { it.cast<JsonObject>() }
?.forEach { it ->
?.forEach {
try {
when (it.string("type")) {
@ -41,6 +41,7 @@ class APTag(parser: TootParser, jsonArray: JsonArray?) {
shortcode = shortcode,
url = iconUrl,
staticUrl = iconUrl,
json = it,
)
}
}

View File

@ -0,0 +1,42 @@
package jp.juggler.subwaytooter.dialog
import android.app.Dialog
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView
import jp.juggler.subwaytooter.databinding.DlgEmojiDetailBinding
import jp.juggler.subwaytooter.view.NetworkEmojiView
import jp.juggler.subwaytooter.R
import jp.juggler.util.ui.dismissSafe
import jp.juggler.util.ui.vg
fun AppCompatActivity.showEmojiDetailDialog(
detail: String,
initialzeNiv: (NetworkEmojiView.() -> Unit)? = null,
initializeImage: (AppCompatImageView.() -> Unit)? = null,
initializeText: (AppCompatTextView.() -> Unit)? = null,
) {
val dialog = Dialog(this)
val views = DlgEmojiDetailBinding.inflate(layoutInflater)
views.btnOk.setOnClickListener { dialog.dismissSafe() }
views.nivEmoji.vg(initialzeNiv != null)?.let {
initialzeNiv?.invoke(it)
}
views.ivEmoji.vg(initializeImage != null)?.let {
initializeImage?.invoke(it)
}
views.tvEmoji.vg(initializeText != null)?.let {
initializeText?.invoke(it)
}
views.etJson.setText(detail)
dialog.setTitle(R.string.emoji_detail)
dialog.setContentView(views.root)
dialog.window?.setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT
)
dialog.show()
}

View File

@ -26,7 +26,6 @@ import jp.juggler.subwaytooter.databinding.EmojiPickerDialogBinding
import jp.juggler.subwaytooter.emoji.*
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.subwaytooter.pref.PrefS
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.emojiSizeMode
import jp.juggler.subwaytooter.util.minHeightCompat
@ -95,9 +94,14 @@ private class EmojiPicker(
items.filter {
when (it) {
is PickerItemCustom ->
it.customEmoji.shortcode.contains(keywordLower)
it.customEmoji.shortcode.contains(keywordLower) ||
it.customEmoji.aliases?.any { a ->
a.contains(keywordLower, ignoreCase = true)
} == true
is PickerItemUnicode ->
it.unicodeEmoji.namesLower.any { n -> n.contains(keywordLower) }
else -> false
}
}
@ -130,6 +134,7 @@ private class EmojiPicker(
null -> EmojiMap.shortNameMap[name]?.let {
return PickerItemUnicode(unicodeEmoji = it)
}
accessInfo?.apiHost?.ascii ->
customEmojiMap?.get(name)?.let {
return PickerItemCustom(customEmoji = it)
@ -276,9 +281,8 @@ private class EmojiPicker(
val view: FrameLayout = FrameLayout(activity),
) : ViewHolderBase(view) {
val niv = NetworkEmojiView(
context = activity,
activity,
sizeMode = accessInfo.emojiSizeMode(),
maxEmojiWidth = NetworkEmojiSpan.maxEmojiWidth,
).apply {
layoutParams = FrameLayout.LayoutParams(gridSize, gridSize)
}
@ -286,6 +290,7 @@ private class EmojiPicker(
init {
view.setButtonBackground()
view.setOnClickListener(pickerItemClickListener)
view.setOnLongClickListener(pickerItemLongClickListener)
view.layoutParams = FlexboxLayoutManager.LayoutParams(wrapContent, wrapContent)
view.setPadding(cellMargin, cellMargin, cellMargin, cellMargin)
view.addView(niv)
@ -320,6 +325,7 @@ private class EmojiPicker(
init {
view.setButtonBackground()
view.setOnClickListener(pickerItemClickListener)
view.setOnLongClickListener(pickerItemLongClickListener)
view.layoutParams = FlexboxLayoutManager.LayoutParams(wrapContent, wrapContent)
view.setPadding(cellMargin, cellMargin, cellMargin, cellMargin)
view.addView(iv)
@ -359,6 +365,7 @@ private class EmojiPicker(
init {
view.setButtonBackground()
view.setOnClickListener(pickerItemClickListener)
view.setOnLongClickListener(pickerItemLongClickListener)
view.layoutParams = FlexboxLayoutManager.LayoutParams(wrapContent, wrapContent)
view.addView(tv)
}
@ -493,11 +500,13 @@ private class EmojiPicker(
targetName = targetEmoji.unifiedName
targetInstance = null
}
is PickerItemCustom -> {
targetEmoji = item.customEmoji
targetName = item.customEmoji.shortcode
targetInstance = accessInfo!!.apiHost.ascii
}
else -> return@OnClickListener
}
@ -517,6 +526,59 @@ private class EmojiPicker(
}
}
private val pickerItemLongClickListener = View.OnLongClickListener { v ->
when (val item = v.getTag(R.id.btnAbout)) {
is PickerItemCustom -> {
activity.showEmojiDetailDialog(
detail = item.customEmoji.json.toString(indentFactor = 2, sort = true),
initialzeNiv = {
setEmoji(
url = when {
disableAnimation -> item.customEmoji.staticUrl
else -> item.customEmoji.url
},
initialAspect = item.customEmoji.aspect,
defaultHeight = layoutParams.height,
)
}
)
}
is PickerItemUnicode -> when {
useTwemoji -> {
activity.showEmojiDetailDialog(
detail = item.unicodeEmoji.namesLower.joinToString("\n"),
initializeImage = {
val emoji = applySkinTone(item.unicodeEmoji)
if (emoji.isSvg) {
Glide.with(this@EmojiPicker.activity)
.`as`(PictureDrawable::class.java)
.load("file:///android_asset/${emoji.assetsName}")
.into(this)
} else {
Glide.with(this@EmojiPicker.activity)
.load(emoji.drawableId)
.into(this)
}
}
)
}
else -> {
activity.showEmojiDetailDialog(
detail = item.unicodeEmoji.namesLower.joinToString("\n"),
initializeText = {
this.text = applySkinTone(item.unicodeEmoji).unifiedCode
}
)
}
}
else -> Unit
}
true
}
private suspend fun createCustomEmojiCategories(): List<PickerItemCategory> {
accessInfo ?: error("missing accessInfo")
val context = activity
@ -544,6 +606,7 @@ private class EmojiPicker(
context.getString(R.string.others)
)
}
else ->
context.getString(R.string.emoji_picker_custom_of, it.name)
}
@ -699,6 +762,7 @@ private class EmojiPicker(
when {
oldScrollX > it.left ->
views.svCategories.smoothScrollTo(it.left, 0)
oldScrollX + visibleWidth < it.right ->
views.svCategories.smoothScrollTo(it.right - visibleWidth, 0)
}
@ -756,6 +820,7 @@ private class EmojiPicker(
dragging = FlickStatus.None
wasIntercept
}
MotionEvent.ACTION_UP -> {
try {
log.i("ACTION_UP wasIntercept=$wasIntercept")
@ -782,6 +847,7 @@ private class EmojiPicker(
}
wasIntercept
}
MotionEvent.ACTION_DOWN -> {
log.i("ACTION_DOWN wasIntercept=$wasIntercept")
// ドラッグ開始
@ -794,6 +860,7 @@ private class EmojiPicker(
startY = ev.y
wasIntercept
}
MotionEvent.ACTION_MOVE -> {
if (dragging == FlickStatus.None) {
wasIntercept
@ -819,6 +886,7 @@ private class EmojiPicker(
dragging = FlickStatus.Intercepted
true
}
else -> {
log.d("not yet intercept. $deltaX, $deltaY")
false
@ -826,6 +894,7 @@ private class EmojiPicker(
}
}
}
else -> log.w("handleTouch else $ev")
}
} catch (ex: Throwable) {

View File

@ -4,8 +4,8 @@ import androidx.annotation.DrawableRes
import jp.juggler.subwaytooter.api.entity.Host
import jp.juggler.subwaytooter.api.entity.Mappable
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.util.data.JsonArray
import jp.juggler.util.data.JsonObject
import jp.juggler.util.data.jsonObjectOf
import jp.juggler.util.data.notEmpty
import jp.juggler.util.data.toMutableMap
@ -56,18 +56,20 @@ class CustomEmoji(
val shortcode: String, // shortcode (コロンを含まない)
val url: String, // 画像URL
val staticUrl: String?, // アニメーションなしの画像URL
val aliases: ArrayList<String>? = null,
val aliases: List<String>? = null,
val alias: String? = null,
val visibleInPicker: Boolean = true,
val category: String? = null,
val aspect: Float? = null,
val json: JsonObject,
) : EmojiBase, Mappable<String> {
fun makeAlias(alias: String) = CustomEmoji(
shortcode = shortcode,
url = url,
staticUrl = staticUrl,
alias = alias
alias = alias,
json = json,
)
override val mapKey: String
@ -94,6 +96,7 @@ class CustomEmoji(
visibleInPicker = src.optBoolean("visible_in_picker", true),
category = src.string("category"),
aspect = aspect,
json = src,
)
}
@ -104,6 +107,7 @@ class CustomEmoji(
url = url,
staticUrl = url,
category = src.string("category"),
json = src,
)
}
@ -114,8 +118,10 @@ class CustomEmoji(
shortcode = name,
url = url,
staticUrl = url,
aliases = parseAliases(src.jsonArray("aliases")),
aliases = src.jsonArray("aliases")?.stringList()?.filter { it.isNotEmpty() }
?.notEmpty(),
category = src.string("category"),
json = src,
)
}
@ -129,23 +135,9 @@ class CustomEmoji(
shortcode = k,
url = url,
staticUrl = url + (if (url.contains('?')) '&' else '?') + "static=1",
json = jsonObjectOf("name" to k, "url" to url)
)
}
}?.notEmpty()?.toMutableMap()
private fun parseAliases(src: JsonArray?): ArrayList<String>? {
var dst = null as ArrayList<String>?
if (src != null) {
val size = src.size
if (size > 0) {
dst = ArrayList(size)
src.forEach {
val str = it?.toString()?.notEmpty()
if (str != null) dst.add(str)
}
}
}
return if (dst?.isNotEmpty() == true) dst else null
}
}
}

View File

@ -20,15 +20,15 @@ import jp.juggler.subwaytooter.util.EmojiSizeMode
import java.lang.ref.WeakReference
@SuppressLint("ViewConstructor")
class NetworkEmojiView(
class NetworkEmojiView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
sizeMode: EmojiSizeMode,
maxEmojiWidth: Float,
scale: Float = 1f,
private val errorDrawableId: Int = R.drawable.outline_broken_image_24,
sizeMode: EmojiSizeMode = EmojiSizeMode.Square,
maxEmojiWidth: Float = NetworkEmojiSpan.maxEmojiWidth,
) : View(context, attrs, defStyleAttr) {
private var errorDrawableId: Int = R.drawable.outline_broken_image_24
var scale: Float = 1f
private var url: String? = null

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="300dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<jp.juggler.subwaytooter.view.MaxHeightScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fadeScrollbars="false"
android:scrollbarStyle="outsideOverlay"
app:maxHeight="350dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="12dp"
android:paddingVertical="12dp">
<jp.juggler.subwaytooter.view.NetworkEmojiView
android:id="@+id/nivEmoji"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal"
android:visibility="gone"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivEmoji"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="fitCenter"
android:visibility="gone"
tools:src="@drawable/ic_face"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvEmoji"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textSize="200dp"
android:visibility="gone"
tools:ignore="SpUsage"
tools:text="❤"
tools:visibility="visible" />
<EditText
android:id="@+id/etJson"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:importantForAutofill="no"
android:inputType="none"
android:minLines="3"
tools:ignore="Deprecated,LabelFor"
tools:text="text\ntext\n" />
</LinearLayout>
</jp.juggler.subwaytooter.view.MaxHeightScrollView>
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnOk"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/ok" />
</LinearLayout>
</LinearLayout>

View File

@ -1287,5 +1287,7 @@
<string name="app_data_export_import">アプリデータのエクスポート/インポート</string>
<string name="translate_or_custom_share">翻訳ボタン / カスタム共有ボタン</string>
<string name="use_webp_format_if_server_accepts">可能ならWebPフォーマットを使う</string>
<string name="detail">Detail</string>
<string name="emoji_detail">Emoji detail</string>
</resources>

View File

@ -1298,4 +1298,6 @@
<string name="search_result" translatable="false">%1$d/%2$d%3$s</string>
<string name="toggle_regexp">.+\?</string>
<string name="use_webp_format_if_server_accepts">Use WebP format if server accepts.</string>
<string name="detail">Detail</string>
<string name="emoji_detail">Emoji detail</string>
</resources>