絵文字ピッカーで長押しすると絵文字の詳細を表示する
This commit is contained in:
parent
11e3906f57
commit
934cf684a9
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue