()
for (i in 0 until size) {
- val item = parseItem(factory, host, src.jsonObject(i), log)
+ val item = parseItem(factory, apDomain, apiHost, src.jsonObject(i), log)
if (item != null) dst[item.mapKey] = item
}
if (dst.isNotEmpty()) return dst
@@ -183,6 +183,22 @@ inline fun parseItem(
}
}
+inline fun parseItem(
+ factory: (p1: P1, p2: P2, src: JsonObject) -> T,
+ p1: P1,
+ p2: P2,
+ src: JsonObject?,
+ log: LogCategory = EntityUtil.log,
+): T? {
+ if (src == null) return null
+ return try {
+ factory(p1, p2, src)
+ } catch (ex: Throwable) {
+ log.e(ex, "${T::class.simpleName} parse failed.")
+ null
+ }
+}
+
inline fun parseItem(
factory: (serviceType: ServiceType, src: JsonObject) -> T,
serviceType: ServiceType,
@@ -198,9 +214,9 @@ inline fun parseItem(
}
}
-inline fun parseList(
- factory: (parser: TootParser, src: JsonObject) -> T,
- parser: TootParser,
+inline fun parseListP1(
+ factory: (p1: P1, src: JsonObject) -> T,
+ p1: P1,
src: JsonArray?,
log: LogCategory = EntityUtil.log,
): ArrayList {
@@ -210,7 +226,28 @@ inline fun parseList(
if (src_length > 0) {
dst.ensureCapacity(src_length)
for (i in src.indices) {
- val item = parseItem(factory, parser, src.jsonObject(i), log)
+ val item = parseItem(factory, p1, src.jsonObject(i), log)
+ if (item != null) dst.add(item)
+ }
+ }
+ }
+ return dst
+}
+
+inline fun parseListP2(
+ factory: (p1: P1, p2: P2, src: JsonObject) -> T,
+ p1: P1,
+ p2: P2,
+ src: JsonArray?,
+ log: LogCategory = EntityUtil.log,
+): ArrayList {
+ val dst = ArrayList()
+ if (src != null) {
+ val src_length = src.size
+ if (src_length > 0) {
+ dst.ensureCapacity(src_length)
+ for (i in src.indices) {
+ val item = parseItem(factory, p1, p2, src.jsonObject(i), log)
if (item != null) dst.add(item)
}
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/MisskeyNoteUpdate.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/MisskeyNoteUpdate.kt
index e9261b37..4ba34069 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/MisskeyNoteUpdate.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/MisskeyNoteUpdate.kt
@@ -4,7 +4,7 @@ import jp.juggler.subwaytooter.emoji.CustomEmoji
import jp.juggler.util.JsonObject
import jp.juggler.util.LogCategory
-class MisskeyNoteUpdate(apDomain: Host, src: JsonObject) {
+class MisskeyNoteUpdate(apDomain: Host, apiHost: Host, src: JsonObject) {
companion object {
private val log = LogCategory("MisskeyNoteUpdate")
}
@@ -37,7 +37,7 @@ class MisskeyNoteUpdate(apDomain: Host, src: JsonObject) {
userId = EntityId.mayDefault(body2.string("userId"))
emoji = body2.jsonObject("emoji")?.let {
try {
- CustomEmoji.decodeMisskey(apDomain, it)
+ CustomEmoji.decodeMisskey(apDomain, apiHost, it)
} catch (ex: Throwable) {
log.e(ex, "can't parse custom emoji.")
null
diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt
index a6db5d5e..b7820b4a 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt
@@ -138,6 +138,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
parseMapOrNull(
CustomEmoji.decodeMisskey,
parser.apDomain,
+ parser.apiHost,
src.jsonArray("emojis")
)
this.profile_emojis = null
@@ -276,8 +277,12 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
else -> {
// 絵文字データは先に読んでおく
- this.custom_emojis =
- parseMapOrNull(CustomEmoji.decode, parser.apDomain, src.jsonArray("emojis"))
+ this.custom_emojis = parseMapOrNull(
+ CustomEmoji.decode,
+ parser.apDomain,
+ parser.apiHost,
+ src.jsonArray("emojis")
+ )
this.profile_emojis = when (val o = src["profile_emojis"]) {
is JsonArray -> parseMapOrNull(::NicoProfileEmoji, o, TootStatus.log)
diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAnnouncement.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAnnouncement.kt
index 03b5cd68..06552076 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAnnouncement.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAnnouncement.kt
@@ -7,7 +7,6 @@ import jp.juggler.subwaytooter.util.DecodeOptions
import jp.juggler.util.JsonObject
import jp.juggler.util.LogCategory
import jp.juggler.util.notEmpty
-import java.util.*
class TootAnnouncement(parser: TootParser, src: JsonObject) {
@@ -44,8 +43,13 @@ class TootAnnouncement(parser: TootParser, src: JsonObject) {
init {
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
- this.custom_emojis =
- parseMapOrNull(CustomEmoji.decode, parser.apDomain, src.jsonArray("emojis"), log)
+ this.custom_emojis = parseMapOrNull(
+ CustomEmoji.decode,
+ parser.apDomain,
+ parser.apiHost,
+ src.jsonArray("emojis"),
+ log
+ )
this.tags = TootTag.parseListOrNull(parser, src.jsonArray("tags"))
diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.kt
index 4be7ac50..a2f9b2f7 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.kt
@@ -239,11 +239,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
val canUseReference: Boolean?
get() = fedibird_capabilities?.contains("status_reference")
- fun versionGE(check: VersionString): Boolean {
- if (decoded_version.isEmpty || check.isEmpty) return false
- val i = VersionString.compare(decoded_version, check)
- return i >= 0
- }
+ fun versionGE(check: VersionString) = decoded_version.ge(check)
companion object {
private val log = LogCategory("TootInstance")
diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt
index efb628c7..af6adc04 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt
@@ -238,13 +238,13 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
// お気に入りカラムなどではパース直後に変更することがある
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
- this.custom_emojis =
- parseMapOrNull(
- CustomEmoji.decodeMisskey,
- parser.apDomain,
- src.jsonArray("emojis"),
- log
- )
+ this.custom_emojis = parseMapOrNull(
+ CustomEmoji.decodeMisskey,
+ parser.apDomain,
+ parser.apiHost,
+ src.jsonArray("emojis"),
+ log
+ )
this.profile_emojis = null
val who = parser.account(src.jsonObject("user"))
@@ -581,13 +581,13 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
this.created_at = src.string("created_at")
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
- this.custom_emojis =
- parseMapOrNull(
- CustomEmoji.decode,
- parser.apDomain,
- src.jsonArray("emojis"),
- log
- )
+ this.custom_emojis = parseMapOrNull(
+ CustomEmoji.decode,
+ parser.apDomain,
+ parser.apiHost,
+ src.jsonArray("emojis"),
+ log
+ )
this.profile_emojis = when (val o = src["profile_emojis"]) {
is JsonArray -> parseMapOrNull(::NicoProfileEmoji, o, log)
@@ -809,9 +809,9 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
}
private fun mergeMentions(
- mentions1: java.util.ArrayList?,
- mentions2: java.util.ArrayList?,
- ): java.util.ArrayList? {
+ mentions1: List?,
+ mentions2: List?,
+ ): ArrayList? {
val size = (mentions1?.size ?: 0) + (mentions2?.size ?: 0)
if (size == 0) return null
val dst = ArrayList(size)
diff --git a/app/src/main/java/jp/juggler/subwaytooter/appsetting/AppSettingItem.kt b/app/src/main/java/jp/juggler/subwaytooter/appsetting/AppSettingItem.kt
index bf8d5175..86664142 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/appsetting/AppSettingItem.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/appsetting/AppSettingItem.kt
@@ -466,7 +466,9 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
sw(PrefB.bpUseInternalMediaViewer, R.string.use_internal_media_viewer)
spinner(PrefI.ipMediaBackground, R.string.background_pattern) {
- MediaBackgroundDrawable.Kind.values().map { it.name }
+ MediaBackgroundDrawable.Kind.values()
+ .filter{it.isMediaBackground}
+ .map { it.name }
}
sw(PrefB.bpPriorLocalURL, R.string.prior_local_url_when_open_attachment)
diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/EmojiPicker.kt b/app/src/main/java/jp/juggler/subwaytooter/dialog/EmojiPicker.kt
index 03e34df4..0cb1618b 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/dialog/EmojiPicker.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/EmojiPicker.kt
@@ -23,6 +23,7 @@ import com.google.android.flexbox.FlexboxLayout
import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.databinding.EmojiPickerDialogBinding
+import jp.juggler.subwaytooter.drawable.MediaBackgroundDrawable
import jp.juggler.subwaytooter.emoji.*
import jp.juggler.subwaytooter.global.appPref
import jp.juggler.subwaytooter.pref.PrefB
@@ -574,7 +575,7 @@ private class EmojiPicker(
category.createFiltered(keywordLower)
.takeIf { it.items.isNotEmpty() }
}.forEach {
- if (selectedCategory == null) add(it)
+ if( it.category == EmojiCategory.Custom || selectedCategory == null) add(it)
addAll(it.items)
val mod = it.items.size % gridCols
if (mod > 0) {
@@ -743,8 +744,12 @@ private class EmojiPicker(
views.etFilter.addTextChangedListener(textWatcher)
+
+
showFiltered(null, null)
+ val density = activity.resources.displayMetrics.density
+
views.giGrid.intercept = { handleTouch(it, wasIntercept = false) }
views.giGrid.touch = { handleTouch(it, wasIntercept = true) }
diff --git a/app/src/main/java/jp/juggler/subwaytooter/drawable/MediaBackgroundDrawable.kt b/app/src/main/java/jp/juggler/subwaytooter/drawable/MediaBackgroundDrawable.kt
index ee00e7e7..5a4821be 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/drawable/MediaBackgroundDrawable.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/drawable/MediaBackgroundDrawable.kt
@@ -1,22 +1,35 @@
package jp.juggler.subwaytooter.drawable
+import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
-import androidx.annotation.ColorInt
+import jp.juggler.subwaytooter.R
+import jp.juggler.util.attrColor
import kotlin.math.min
class MediaBackgroundDrawable(
+ private val context: Context,
private val tileStep: Int,
- private val kind: Kind
+ private val kind: Kind,
) : Drawable() {
- enum class Kind(@ColorInt val c1: Int, @ColorInt val c2: Int = 0) {
- Black(Color.BLACK),
- BlackTile(Color.BLACK, Color.BLACK or 0x202020),
- Grey(Color.BLACK or 0x787878),
- GreyTile(Color.BLACK or 0x707070, Color.BLACK or 0x808080),
- White(Color.WHITE),
- WhiteTile(Color.WHITE, Color.BLACK or 0xe0e0e0),
+ enum class Kind(
+ val c1: Context.() -> Int,
+ val c2: Context.() -> Int,
+ val isMediaBackground: Boolean = true,
+ ) {
+ Black({ Color.BLACK }, { 0 }),
+ BlackTile({ Color.BLACK }, { Color.BLACK or 0x202020 }),
+ Grey({ Color.BLACK or 0x787878 }, { 0 }),
+ GreyTile({ Color.BLACK or 0x707070 }, { Color.BLACK or 0x808080 }),
+ White({ Color.WHITE }, { 0 }),
+ WhiteTile({ Color.WHITE }, { Color.BLACK or 0xe0e0e0 }),
+
+ EmojiPickerBg(
+ { attrColor(R.attr.colorWindowBackground) },
+ { attrColor(R.attr.colorTimeSmall) },
+ isMediaBackground = false
+ ),
;
@@ -51,8 +64,8 @@ class MediaBackgroundDrawable(
override fun draw(canvas: Canvas) {
val bounds = this.bounds
- val c1 = kind.c1
- val c2 = kind.c2
+ val c1 = kind.c1.invoke(context)
+ val c2 = kind.c2.invoke(context)
if (c2 == 0) {
paint.color = c1
canvas.drawRect(bounds, paint)
diff --git a/app/src/main/java/jp/juggler/subwaytooter/emoji/EmojiBase.kt b/app/src/main/java/jp/juggler/subwaytooter/emoji/EmojiBase.kt
index 8cdeb7f1..42ffe0a5 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/emoji/EmojiBase.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/emoji/EmojiBase.kt
@@ -7,7 +7,6 @@ import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.util.JsonArray
import jp.juggler.util.JsonObject
import jp.juggler.util.notEmpty
-import java.util.*
sealed interface EmojiBase
@@ -81,7 +80,7 @@ class CustomEmoji(
companion object {
- val decode: (Host, JsonObject) -> CustomEmoji = { apDomain, src ->
+ val decode: (Host, Host, JsonObject) -> CustomEmoji = { apDomain, _, src ->
CustomEmoji(
apDomain = apDomain,
shortcode = src.stringOrThrow("shortcode"),
@@ -92,7 +91,7 @@ class CustomEmoji(
)
}
- val decodeMisskey: (Host, JsonObject) -> CustomEmoji = { apDomain, src ->
+ val decodeMisskey: (Host, Host, JsonObject) -> CustomEmoji = { apDomain, _, src ->
val url = src.string("url") ?: error("missing url")
CustomEmoji(
@@ -105,6 +104,19 @@ class CustomEmoji(
)
}
+ val decodeMisskey13: (Host, Host, JsonObject) -> CustomEmoji = { apDomain, apiHost, src ->
+ val name = src.string("name") ?: error("missing name")
+ val url = "https://${apiHost.ascii}/emoji/$name.webp"
+ CustomEmoji(
+ apDomain = apDomain,
+ shortcode = name,
+ url = url,
+ staticUrl = url,
+ aliases = parseAliases(src.jsonArray("aliases")),
+ category = src.string("category"),
+ )
+ }
+
private fun parseAliases(src: JsonArray?): ArrayList? {
var dst = null as ArrayList?
if (src != null) {
diff --git a/app/src/main/java/jp/juggler/subwaytooter/streaming/StreamConnection.kt b/app/src/main/java/jp/juggler/subwaytooter/streaming/StreamConnection.kt
index a0dded7d..35152e8c 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/streaming/StreamConnection.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/streaming/StreamConnection.kt
@@ -197,7 +197,11 @@ class StreamConnection(
log.e("$name handleMisskeyMessage: noteUpdated body is null")
return
}
- fireNoteUpdated(MisskeyNoteUpdate(acctGroup.account.apDomain, body), channelId)
+ fireNoteUpdated(MisskeyNoteUpdate(
+ acctGroup.account.apDomain,
+ acctGroup.account.apiHost,
+ body
+ ), channelId)
}
"notification" -> {
diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/CustomEmojiLister.kt b/app/src/main/java/jp/juggler/subwaytooter/util/CustomEmojiLister.kt
index e65e8b6c..b52e7082 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/util/CustomEmojiLister.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/util/CustomEmojiLister.kt
@@ -4,7 +4,7 @@ import android.content.Context
import android.os.Handler
import android.os.SystemClock
import jp.juggler.subwaytooter.App1
-import jp.juggler.subwaytooter.api.entity.parseList
+import jp.juggler.subwaytooter.api.entity.parseListP2
import jp.juggler.subwaytooter.emoji.CustomEmoji
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.util.*
@@ -35,6 +35,7 @@ class CustomEmojiLister(
val key: String,
var list: List,
var listWithAliases: List,
+ var mapShortCode: Map,
// ロードした時刻
var timeUpdate: Long = elapsedTime,
// 参照された時刻
@@ -63,7 +64,12 @@ class CustomEmojiLister(
// エラーキャッシュ
internal val cacheError = ConcurrentHashMap()
- private val cacheErrorItem = CacheItem("error", emptyList(), emptyList())
+ private val cacheErrorItem = CacheItem(
+ key = "error",
+ list = emptyList(),
+ listWithAliases = emptyList(),
+ mapShortCode = emptyMap(),
+ )
// ロード要求
internal val queue = ConcurrentLinkedQueue()
@@ -75,18 +81,21 @@ class CustomEmojiLister(
cacheError.clear()
}
- private fun getCached(now: Long, accessInfo: SavedAccount): CacheItem? {
- val host = accessInfo.apiHost.ascii
+ private fun getCached(now: Long, accessInfo: SavedAccount) =
+ getCached(now, accessInfo.apiHost.ascii)
+
+ private fun getCached(now: Long, apiHostAscii: String?): CacheItem? {
+ apiHostAscii ?: return null
// 成功キャッシュ
- val item = cache[host]
+ val item = cache[apiHostAscii]
if (item != null && now - item.timeUpdate <= ERROR_EXPIRE) {
item.timeUsed = now
return item
}
// エラーキャッシュ
- val timeError = cacheError[host]
+ val timeError = cacheError[apiHostAscii]
if (timeError != null && now < timeError + ERROR_EXPIRE) {
return cacheErrorItem
}
@@ -143,6 +152,9 @@ class CustomEmojiLister(
}
}
+ fun getCachedEmoji(apiHostAscii: String?, shortcode: String): CustomEmoji? =
+ getCached(elapsedTime, apiHostAscii)?.mapShortCode?.get(shortcode)
+
private inner class Worker : WorkerBase() {
override fun cancel() {
@@ -186,39 +198,92 @@ class CustomEmojiLister(
val accessInfo = request.accessInfo
val cacheKey = accessInfo.apiHost.ascii
- val data = if (accessInfo.isMisskey) {
+
+ // v12のmetaからemojisをパース
+ suspend fun misskeyEmojis12(): List? =
App1.getHttpCachedString(
"https://$cacheKey/api/meta",
accessInfo = accessInfo
) { builder ->
builder.post(JsonObject().toRequestBody())
+ }?.decodeJsonObject()
+ ?.jsonArray("emojis")
+ ?.let { emojis12 ->
+ parseListP2(
+ CustomEmoji.decodeMisskey,
+ accessInfo.apDomain,
+ accessInfo.apiHost,
+ emojis12,
+ )
+ }
+
+ // v13のemojisを読む
+ suspend fun misskeyEmojis13(): List? =
+ App1.getHttpCachedString(
+ "https://$cacheKey/api/emojis",
+ accessInfo = accessInfo,
+ misskeyPost = true,
+ ) { builder ->
+ builder.post(JsonObject().toRequestBody())
}
- } else {
+ ?.decodeJsonObject()
+ ?.jsonArray("emojis")
+ ?.let { emojis13 ->
+ parseListP2(
+ CustomEmoji.decodeMisskey13,
+ accessInfo.apDomain,
+ accessInfo.apiHost,
+ emojis13,
+ )
+ }
+
+ // マストドンのカスタム絵文字一覧を読む
+ suspend fun mastodonEmojis() =
App1.getHttpCachedString(
"https://$cacheKey/api/v1/custom_emojis",
accessInfo = accessInfo
- )
- }
- var list: List? = null
- var listWithAlias: List? = null
- if (data != null) {
- val a = decodeEmojiList(data, accessInfo)
- list = a
- listWithAlias = makeListWithAlias(a)
- }
+ )?.let { data ->
+ parseListP2(
+ CustomEmoji.decode,
+ accessInfo.apDomain,
+ accessInfo.apiHost,
+ data.decodeJsonArray()
+ )
+ }
+
+ val list = when {
+ accessInfo.isMastodon -> mastodonEmojis()
+ else -> misskeyEmojis12() ?: misskeyEmojis13()
+ }?.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.shortcode })
+
+ val listWithAlias = list?.let { srcList ->
+ ArrayList(srcList).apply {
+ for (item in srcList) {
+ item.aliases
+ ?.filter { !it.equals(item.shortcode, ignoreCase = true) }
+ ?.forEach { add(item.makeAlias(it)) }
+ }
+ }
+ }?.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.alias ?: it.shortcode })
+
return synchronized(cache) {
val now = elapsedTime
if (list == null || listWithAlias == null) {
cacheError[cacheKey] = now
error("can't load custom emoji for ${accessInfo.apiHost}")
} else {
+ val mapShortCode = buildMap {
+ list.forEach { put(it.alias ?: it.shortcode, it) }
+ listWithAlias.forEach { put(it.alias ?: it.shortcode, it) }
+ }
var item = cache[cacheKey]
if (item == null) {
- item = CacheItem(cacheKey, list, listWithAlias)
+ item = CacheItem(cacheKey, list, listWithAlias, mapShortCode)
cache[cacheKey] = item
} else {
item.list = list
item.listWithAliases = listWithAlias
+ item.mapShortCode = mapShortCode
item.timeUpdate = now
}
item
@@ -249,39 +314,5 @@ class CustomEmojiLister(
if (++removed >= over) break
}
}
-
- private fun decodeEmojiList(
- data: String,
- accessInfo: SavedAccount,
- ): List =
- if (accessInfo.isMisskey) {
- parseList(
- CustomEmoji.decodeMisskey,
- accessInfo.apDomain,
- data.decodeJsonObject().jsonArray("emojis")
- )
- } else {
- parseList(
- CustomEmoji.decode,
- accessInfo.apDomain,
- data.decodeJsonArray()
- )
- }.apply {
- sortWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.shortcode })
- }
-
- private fun makeListWithAlias(
- list: List,
- ) = ArrayList().apply {
- addAll(list)
- for (item in list) {
- val aliases = item.aliases ?: continue
- for (alias in aliases) {
- if (alias.equals(item.shortcode, ignoreCase = true)) continue
- add(item.makeAlias(alias))
- }
- }
- sortWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.alias ?: it.shortcode })
- }
}
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt b/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt
index a53ca88e..144a2557 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt
@@ -1,10 +1,12 @@
package jp.juggler.subwaytooter.util
import android.content.Context
+import android.os.SystemClock
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.util.SparseBooleanArray
import androidx.annotation.DrawableRes
+import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.emoji.CustomEmoji
import jp.juggler.subwaytooter.emoji.EmojiMap
@@ -382,6 +384,11 @@ object EmojiDecoder {
// カスタム絵文字
val emojiCustom = emojiMapCustom?.get(name)
+ ?: App1.custom_emoji_lister.getCachedEmoji(
+ options.linkHelper?.apiHost?.ascii,
+ name
+ )
+
if (emojiCustom != null) {
val url = when {
PrefB.bpDisableEmojiAnimation() && emojiCustom.staticUrl?.isNotEmpty() == true -> emojiCustom.staticUrl
diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/VersionString.kt b/app/src/main/java/jp/juggler/subwaytooter/util/VersionString.kt
index 483f1992..aa1041d3 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/util/VersionString.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/util/VersionString.kt
@@ -161,4 +161,11 @@ class VersionString(src: String?) : Comparable {
}
}
}
+
+ // false if this is empty or argument is empty
+ // else, true is this is greater or equal argument version
+ fun ge(other: VersionString) = when {
+ this.isEmpty || other.isEmpty -> false
+ else -> this >= other
+ }
}
diff --git a/icon_material_symbols/.gitignore b/icon_material_symbols/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/icon_material_symbols/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/icon_material_symbols/build.gradle b/icon_material_symbols/build.gradle
new file mode 100644
index 00000000..95d80c3c
--- /dev/null
+++ b/icon_material_symbols/build.gradle
@@ -0,0 +1,41 @@
+plugins {
+ id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ namespace 'jp.juggler.icon_material_symbols'
+ compileSdk 32
+
+ defaultConfig {
+ minSdk 26
+ targetSdk 32
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.5.1'
+ implementation 'com.google.android.material:material:1.7.0'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.4'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
+}
\ No newline at end of file
diff --git a/icon_material_symbols/consumer-rules.pro b/icon_material_symbols/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/icon_material_symbols/proguard-rules.pro b/icon_material_symbols/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/icon_material_symbols/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/icon_material_symbols/src/androidTest/java/jp/juggler/icon_material_symbols/ExampleInstrumentedTest.kt b/icon_material_symbols/src/androidTest/java/jp/juggler/icon_material_symbols/ExampleInstrumentedTest.kt
new file mode 100644
index 00000000..652bf87e
--- /dev/null
+++ b/icon_material_symbols/src/androidTest/java/jp/juggler/icon_material_symbols/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package jp.juggler.icon_material_symbols
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("jp.juggler.icon_material_symbols.test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/icon_material_symbols/src/main/AndroidManifest.xml b/icon_material_symbols/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/icon_material_symbols/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/icon_material_symbols/src/test/java/jp/juggler/icon_material_symbols/ExampleUnitTest.kt b/icon_material_symbols/src/test/java/jp/juggler/icon_material_symbols/ExampleUnitTest.kt
new file mode 100644
index 00000000..2ac8d5db
--- /dev/null
+++ b/icon_material_symbols/src/test/java/jp/juggler/icon_material_symbols/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package jp.juggler.icon_material_symbols
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 7706b5a7..1a17f00e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,2 @@
include ':app', ':sample_apng', ':apng_android', ':apng', ':colorpicker', ':emoji'
+include ':icon_material_symbols'