(Mastodon 3.0)カスタム絵文字のカテゴリ表記。カスタム絵文字ロードの並列化。
This commit is contained in:
parent
7ae2f5a748
commit
40dce24593
|
@ -47,6 +47,7 @@
|
|||
<w>idat</w>
|
||||
<w>idempotency</w>
|
||||
<w>ihdr</w>
|
||||
<w>infos</w>
|
||||
<w>kapt</w>
|
||||
<w>kddi</w>
|
||||
<w>kenglxn</w>
|
||||
|
|
|
@ -495,7 +495,7 @@ class App1 : Application() {
|
|||
}
|
||||
|
||||
internal val CACHE_CONTROL = CacheControl.Builder()
|
||||
.maxAge(5, TimeUnit.MINUTES) // キャッシュが新鮮であると考えられる時間
|
||||
.maxAge(1, TimeUnit.DAYS) // キャッシュが新鮮であると考えられる時間
|
||||
.build()
|
||||
|
||||
fun getHttpCached(url : String) : ByteArray? {
|
||||
|
|
|
@ -12,7 +12,8 @@ class CustomEmoji(
|
|||
val static_url : String?, // アニメーションなしの画像URL
|
||||
val aliases : ArrayList<String>? = null,
|
||||
val alias : String? = null,
|
||||
val visible_in_picker : Boolean = true
|
||||
val visible_in_picker : Boolean = true,
|
||||
val category: String? = null
|
||||
) : Mappable<String> {
|
||||
|
||||
fun makeAlias(alias : String) = CustomEmoji(
|
||||
|
@ -26,14 +27,17 @@ class CustomEmoji(
|
|||
get() = shortcode
|
||||
|
||||
companion object {
|
||||
|
||||
val decode : (JSONObject) -> CustomEmoji = { src ->
|
||||
CustomEmoji(
|
||||
shortcode = src.notEmptyOrThrow("shortcode"),
|
||||
url = src.notEmptyOrThrow("url"),
|
||||
static_url = src.parseString("static_url"),
|
||||
visible_in_picker = src.optBoolean("visible_in_picker", true)
|
||||
visible_in_picker = src.optBoolean("visible_in_picker", true),
|
||||
category =src.parseString("category")
|
||||
)
|
||||
}
|
||||
|
||||
val decodeMisskey : (JSONObject) -> CustomEmoji = { src ->
|
||||
val url = src.parseString("url") ?: error("missing url")
|
||||
|
||||
|
|
|
@ -17,9 +17,12 @@ import com.bumptech.glide.Glide
|
|||
import jp.juggler.emoji.EmojiMap
|
||||
import jp.juggler.subwaytooter.*
|
||||
import jp.juggler.subwaytooter.api.entity.CustomEmoji
|
||||
import jp.juggler.subwaytooter.view.HeaderGridView
|
||||
import jp.juggler.subwaytooter.view.MyViewPager
|
||||
import jp.juggler.subwaytooter.view.NetworkEmojiView
|
||||
import jp.juggler.util.*
|
||||
import org.jetbrains.anko.padding
|
||||
import org.jetbrains.anko.textColor
|
||||
import org.json.JSONObject
|
||||
import java.util.*
|
||||
|
||||
|
@ -32,6 +35,22 @@ class EmojiPicker(
|
|||
// onEmojiPickedのinstance引数は通常の絵文字ならnull、カスタム絵文字なら非null、
|
||||
) : View.OnClickListener, ViewPager.OnPageChangeListener {
|
||||
|
||||
class SkinTone(val suffix_list : Array<out String>) {
|
||||
companion object {
|
||||
fun create(vararg suffix_list : String) : SkinTone {
|
||||
return SkinTone(suffix_list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class EmojiItem(val name : String, val instance : String?)
|
||||
|
||||
internal class CustomCategory(
|
||||
val rangeStart : Int,
|
||||
val rangeLength : Int,
|
||||
val view : View
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
internal val log = LogCategory("EmojiPicker")
|
||||
|
@ -66,7 +85,8 @@ class EmojiPicker(
|
|||
|
||||
private val recent_list = ArrayList<EmojiItem>()
|
||||
|
||||
private val custom_list = ArrayList<EmojiItem>()
|
||||
private var custom_list = ArrayList<EmojiItem>()
|
||||
private var custom_categories = ArrayList<CustomCategory>()
|
||||
|
||||
private val emoji_url_map = HashMap<String, String>()
|
||||
|
||||
|
@ -74,16 +94,6 @@ class EmojiPicker(
|
|||
|
||||
private val custom_page_idx : Int
|
||||
|
||||
class SkinTone(val suffix_list : Array<out String>) {
|
||||
companion object {
|
||||
fun create(vararg suffix_list : String) : SkinTone {
|
||||
return SkinTone(suffix_list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class EmojiItem(val name : String, val instance : String?)
|
||||
|
||||
init {
|
||||
|
||||
// recentをロードする
|
||||
|
@ -213,13 +223,52 @@ class EmojiPicker(
|
|||
private fun setCustomEmojiList(list : ArrayList<CustomEmoji>?) {
|
||||
if(list == null) return
|
||||
bInstanceHasCustomEmoji = true
|
||||
custom_list.clear()
|
||||
|
||||
// make categories
|
||||
val newList = TreeMap<String, ArrayList<EmojiItem>>()
|
||||
for(emoji in list) {
|
||||
if(! emoji.visible_in_picker) continue
|
||||
custom_list.add(EmojiItem(emoji.shortcode, instance))
|
||||
val category = emoji.category ?: ""
|
||||
var subList = newList[category]
|
||||
if(subList == null) {
|
||||
subList = ArrayList()
|
||||
newList[category] = subList
|
||||
}
|
||||
subList.add(EmojiItem(emoji.shortcode, instance))
|
||||
emoji_url_map[emoji.shortcode] = emoji.url
|
||||
}
|
||||
pager_adapter.getPageViewHolder(custom_page_idx)?.notifyDataSetChanged()
|
||||
// compose categories data list
|
||||
val entries = newList.entries
|
||||
custom_list.clear()
|
||||
custom_categories.clear()
|
||||
custom_list.ensureCapacity(entries.sumBy { it.value.size })
|
||||
custom_categories.ensureCapacity(entries.size)
|
||||
entries.forEach {
|
||||
val rangeStart = custom_list.size
|
||||
custom_list.addAll(it.value)
|
||||
val rangeLength = custom_list.size - rangeStart
|
||||
|
||||
custom_categories.add(CustomCategory(
|
||||
rangeStart,
|
||||
rangeLength,
|
||||
TextView(activity).apply {
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
text = when(val name = it.key) {
|
||||
"" -> this@EmojiPicker.activity.getString(R.string.custom_emoji)
|
||||
else -> name
|
||||
}
|
||||
textColor =
|
||||
getAttributeColor(this@EmojiPicker.activity, R.attr.colorContentText)
|
||||
textSize = 16f // SP単位
|
||||
padding = (resources.displayMetrics.density * 2f + 0.5f).toInt()
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
pager_adapter.getPageViewHolder(custom_page_idx)?.reloadCustomEmoji()
|
||||
pager_adapter.getPageViewHolder(recent_page_idx)?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
@ -293,12 +342,12 @@ class EmojiPicker(
|
|||
val id = view.id
|
||||
selected_tone = if(selected_tone == id) 0 else id
|
||||
showSkinTone()
|
||||
pager_adapter.eachViewHolder { _, vh -> vh.reload() }
|
||||
pager_adapter.eachViewHolder { _, vh -> vh.reloadSkinTone() }
|
||||
}
|
||||
|
||||
internal inner class EmojiPickerPage(
|
||||
val hasSkinTone : Boolean,
|
||||
category_id : Int,
|
||||
val category_id : Int,
|
||||
title_id : Int
|
||||
) {
|
||||
|
||||
|
@ -326,29 +375,45 @@ class EmojiPicker(
|
|||
inner class EmojiPickerPageViewHolder(activity : Activity, root : View) : BaseAdapter(),
|
||||
AdapterView.OnItemClickListener {
|
||||
|
||||
private val gridView : GridView
|
||||
private val wh : Int
|
||||
private val gridView : HeaderGridView = root.findViewById(R.id.gridView)
|
||||
private val wh = (0.5f + 48f * activity.resources.displayMetrics.density).toInt()
|
||||
|
||||
private var page : EmojiPickerPage? = null
|
||||
|
||||
init {
|
||||
this.gridView = root.findViewById(R.id.gridView)
|
||||
gridView.adapter = this
|
||||
gridView.onItemClickListener = this
|
||||
|
||||
this.wh = (0.5f + 48f * activity.resources.displayMetrics.density).toInt()
|
||||
}
|
||||
|
||||
internal fun onPageCreate(page : EmojiPickerPage) {
|
||||
this.page = page
|
||||
if(page.category_id != CATEGORY_CUSTOM) {
|
||||
gridView.adapter = this
|
||||
} else {
|
||||
reloadCustomEmoji()
|
||||
}
|
||||
gridView.onItemClickListener = this
|
||||
}
|
||||
|
||||
internal fun onPageDestroy() {
|
||||
|
||||
}
|
||||
|
||||
internal fun reload() {
|
||||
this.notifyDataSetChanged()
|
||||
internal fun reloadSkinTone() {
|
||||
val page = this.page ?: throw RuntimeException("page is not assigned")
|
||||
if(page.category_id != CATEGORY_CUSTOM) {
|
||||
this.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun reloadCustomEmoji() {
|
||||
gridView.reset()
|
||||
if(custom_categories.size >= 2) {
|
||||
for(item in custom_categories) {
|
||||
gridView.addHeaderView(
|
||||
rangeStart = item.rangeStart,
|
||||
rangeLength = item.rangeLength,
|
||||
itemHeight = wh,
|
||||
v = item.view,
|
||||
isSelectable = false
|
||||
)
|
||||
}
|
||||
}
|
||||
gridView.adapter = this
|
||||
}
|
||||
|
||||
override fun getCount() : Int {
|
||||
|
@ -376,17 +441,11 @@ class EmojiPicker(
|
|||
val view : View
|
||||
val item = page.emoji_list[position]
|
||||
if(item.instance != null) {
|
||||
if(viewOld == null) {
|
||||
view = NetworkEmojiView(activity)
|
||||
val lp = AbsListView.LayoutParams(wh, wh)
|
||||
view.layoutParams = lp
|
||||
} else {
|
||||
view = viewOld
|
||||
}
|
||||
view.setTag(R.id.btnAbout,item)
|
||||
if(view is NetworkEmojiView) {
|
||||
view.setEmoji(emoji_url_map[item.name])
|
||||
view = viewOld ?: NetworkEmojiView(activity).apply {
|
||||
layoutParams = AbsListView.LayoutParams(wh, wh)
|
||||
}
|
||||
view.setTag(R.id.btnAbout, item)
|
||||
(view as? NetworkEmojiView)?.setEmoji(emoji_url_map[item.name])
|
||||
} else {
|
||||
if(viewOld == null) {
|
||||
view = ImageView(activity)
|
||||
|
@ -395,7 +454,7 @@ class EmojiPicker(
|
|||
} else {
|
||||
view = viewOld
|
||||
}
|
||||
view.setTag(R.id.btnAbout,item)
|
||||
view.setTag(R.id.btnAbout, item)
|
||||
if(view is ImageView) {
|
||||
val name = if(page.hasSkinTone) {
|
||||
applySkinTone(item.name)
|
||||
|
@ -406,50 +465,56 @@ class EmojiPicker(
|
|||
val info = EmojiMap.sShortNameToEmojiInfo[name]
|
||||
if(info != null) {
|
||||
val er = info.er
|
||||
if(er.isSvg){
|
||||
if(er.isSvg) {
|
||||
Glide.with(activity)
|
||||
.`as`(PictureDrawable::class.java)
|
||||
|
||||
.load("file:///android_asset/${er.assetsName}")
|
||||
.into(view)
|
||||
}else{
|
||||
} else {
|
||||
Glide.with(activity)
|
||||
.load(er.drawableId)
|
||||
.into(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onItemClick(adapterView : AdapterView<*>, view : View, idx : Int, l : Long) {
|
||||
|
||||
override fun onItemClick(
|
||||
adapterView : AdapterView<*>,
|
||||
view : View,
|
||||
idxArg : Int,
|
||||
l : Long
|
||||
) {
|
||||
val page = this.page ?: return
|
||||
|
||||
val item = page.emoji_list[idx]
|
||||
var name = item.name
|
||||
if(item.instance != null && item.instance.isNotEmpty()) {
|
||||
// カスタム絵文字
|
||||
selected(name, item.instance)
|
||||
} else {
|
||||
// 普通の絵文字
|
||||
EmojiMap.sShortNameToEmojiInfo[name] ?: return
|
||||
|
||||
if(page.hasSkinTone) {
|
||||
val sv = applySkinTone(name)
|
||||
if(EmojiMap.sShortNameToEmojiInfo[sv] != null) {
|
||||
name = sv
|
||||
val idx = gridView.findListItemIndex(idxArg)
|
||||
|
||||
if(idx in 0 until page.emoji_list.size) {
|
||||
val item = page.emoji_list[idx]
|
||||
var name = item.name
|
||||
if(item.instance != null && item.instance.isNotEmpty()) {
|
||||
// カスタム絵文字
|
||||
selected(name, item.instance)
|
||||
} else {
|
||||
// 普通の絵文字
|
||||
EmojiMap.sShortNameToEmojiInfo[name] ?: return
|
||||
|
||||
if(page.hasSkinTone) {
|
||||
val sv = applySkinTone(name)
|
||||
if(EmojiMap.sShortNameToEmojiInfo[sv] != null) {
|
||||
name = sv
|
||||
}
|
||||
}
|
||||
|
||||
selected(name, null)
|
||||
}
|
||||
|
||||
selected(name, null)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// name はスキントーン適用済みであること
|
||||
|
|
|
@ -55,13 +55,16 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
private val queue = LinkedList<Request>()
|
||||
|
||||
private val handler : Handler
|
||||
private val worker : Worker
|
||||
private val workers = ArrayList<Worker>()
|
||||
|
||||
init {
|
||||
handler = Handler(context.mainLooper)
|
||||
cache = ConcurrentHashMap()
|
||||
worker = Worker()
|
||||
worker.start()
|
||||
for(i in 0 until 4) {
|
||||
val worker = Worker(workers)
|
||||
worker.start()
|
||||
workers.add(worker)
|
||||
}
|
||||
}
|
||||
|
||||
// カラムのリロードボタンを押したタイミングでエラーキャッシュをクリアする
|
||||
|
@ -123,7 +126,7 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
synchronized(queue) {
|
||||
queue.addLast(Request(refDrawTarget, url, onLoadComplete))
|
||||
}
|
||||
worker.notifyEx()
|
||||
workers.first().notifyEx()
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
// たまにcache変数がなぜかnullになる端末があるらしい
|
||||
|
@ -131,45 +134,55 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
return null
|
||||
}
|
||||
|
||||
private inner class Worker : WorkerBase() {
|
||||
private inner class Worker(waiter : Any?) : WorkerBase(waiter) {
|
||||
|
||||
override fun cancel() {
|
||||
// このスレッドはプロセスが生きてる限りキャンセルされない
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
var ts : Long
|
||||
var te : Long
|
||||
while(true) {
|
||||
try {
|
||||
var queue_size : Int
|
||||
val request = synchronized(queue) {
|
||||
val x = if(queue.isNotEmpty()) queue.removeFirst() else null
|
||||
queue_size = queue.size
|
||||
return@synchronized x
|
||||
x
|
||||
}
|
||||
|
||||
if(request == null) {
|
||||
if(DEBUG) log.d("wait")
|
||||
ts = elapsedTime
|
||||
waitEx(86400000L)
|
||||
te = elapsedTime
|
||||
if(te - ts >= 200L) log.d("sleep ${te - ts}ms")
|
||||
continue
|
||||
}
|
||||
|
||||
// 描画先がGCされたなら何もしない
|
||||
request.refTarget.get() ?: continue
|
||||
|
||||
ts = elapsedTime
|
||||
var cache_size : Int = - 1
|
||||
if(synchronized(cache) {
|
||||
val now = elapsedTime
|
||||
val item = getCached(now, request.url)
|
||||
if(item != null) {
|
||||
if(item.frames != null) {
|
||||
fireCallback(request)
|
||||
}
|
||||
return@synchronized true
|
||||
val chache_used = synchronized(cache) {
|
||||
val now = elapsedTime
|
||||
val item = getCached(now, request.url)
|
||||
if(item != null) {
|
||||
if(item.frames != null) {
|
||||
fireCallback(request)
|
||||
}
|
||||
sweep_cache(now)
|
||||
cache_size = cache.size
|
||||
return@synchronized false
|
||||
}) continue
|
||||
return@synchronized true
|
||||
}
|
||||
sweep_cache(now)
|
||||
cache_size = cache.size
|
||||
return@synchronized false
|
||||
}
|
||||
te = elapsedTime
|
||||
if(te - ts >= 200L) log.d("cache_used? ${te - ts}ms")
|
||||
|
||||
if(chache_used) continue
|
||||
|
||||
if(DEBUG)
|
||||
log.d(
|
||||
|
@ -181,16 +194,27 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
|
||||
var frames : ApngFrames? = null
|
||||
try {
|
||||
|
||||
ts = elapsedTime
|
||||
val data = App1.getHttpCached(request.url)
|
||||
te = elapsedTime
|
||||
if(te - ts >= 200L) log.d("image get? ${te - ts}ms")
|
||||
|
||||
if(data == null) {
|
||||
log.e("get failed. url=%s", request.url)
|
||||
} else {
|
||||
|
||||
ts = elapsedTime
|
||||
frames = decodeAPNG(data, request.url)
|
||||
te = elapsedTime
|
||||
if(te - ts >= 200L) log.d("iamge decode? ${te - ts}ms")
|
||||
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
ts = elapsedTime
|
||||
synchronized(cache) {
|
||||
if(frames == null) {
|
||||
cache_error.put(request.url, elapsedTime)
|
||||
|
@ -207,6 +231,8 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
fireCallback(request)
|
||||
}
|
||||
}
|
||||
te = elapsedTime
|
||||
if(te - ts >= 200L) log.d("update_cache? ${te - ts}ms")
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
|
||||
|
@ -251,11 +277,10 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private fun decodeAPNG(data : ByteArray, url : String) : ApngFrames? {
|
||||
try {
|
||||
// APNGをデコード
|
||||
val x = ApngFrames.parse(64){ ByteArrayInputStream(data) }
|
||||
val x = ApngFrames.parse(64) { ByteArrayInputStream(data) }
|
||||
if(x != null) return x
|
||||
// fall thru
|
||||
} catch(ex : Throwable) {
|
||||
|
@ -280,7 +305,7 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
|
||||
// SVGのロードを試みる
|
||||
try {
|
||||
val b = decodeSVG(url,data, 128.toFloat())
|
||||
val b = decodeSVG(url, data, 128.toFloat())
|
||||
if(b != null) {
|
||||
if(DEBUG) log.d("SVG decoded.")
|
||||
return ApngFrames(b)
|
||||
|
@ -296,7 +321,10 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
|
||||
private val options = BitmapFactory.Options()
|
||||
|
||||
private fun decodeBitmap(data : ByteArray, pixel_max : Int) : Bitmap? {
|
||||
private fun decodeBitmap(
|
||||
data : ByteArray,
|
||||
@Suppress("SameParameterValue") pixel_max : Int
|
||||
) : Bitmap? {
|
||||
options.inJustDecodeBounds = true
|
||||
options.inScaled = false
|
||||
options.outWidth = 0
|
||||
|
@ -319,16 +347,22 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
return BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
||||
}
|
||||
|
||||
private fun decodeSVG(url:String, data : ByteArray, pixelMax : Float) : Bitmap? {
|
||||
private fun decodeSVG(
|
||||
url : String,
|
||||
data : ByteArray,
|
||||
@Suppress("SameParameterValue") pixelMax : Float
|
||||
) : Bitmap? {
|
||||
try {
|
||||
val svg = SVG.getFromInputStream(ByteArrayInputStream(data))
|
||||
|
||||
val src_w = svg.documentWidth // the width in pixels, or -1 if there is no width available.
|
||||
val src_h = svg.documentHeight // the height in pixels, or -1 if there is no height available.
|
||||
val aspect = if( src_w <= 0f || src_h <=0f){
|
||||
val src_w =
|
||||
svg.documentWidth // the width in pixels, or -1 if there is no width available.
|
||||
val src_h =
|
||||
svg.documentHeight // the height in pixels, or -1 if there is no height available.
|
||||
val aspect = if(src_w <= 0f || src_h <= 0f) {
|
||||
// widthやheightの情報がない
|
||||
1f
|
||||
}else{
|
||||
} else {
|
||||
src_w / src_h
|
||||
}
|
||||
|
||||
|
@ -337,7 +371,7 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
if(aspect >= 1f) {
|
||||
dst_w = pixelMax
|
||||
dst_h = pixelMax / aspect
|
||||
}else {
|
||||
} else {
|
||||
dst_h = pixelMax
|
||||
dst_w = pixelMax * aspect
|
||||
}
|
||||
|
@ -352,9 +386,9 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
svg.renderToCanvas(
|
||||
canvas,
|
||||
if(aspect >= 1f) {
|
||||
RectF(0f, h_ceil-dst_h, dst_w, dst_h) // 後半はw,hを指定する
|
||||
RectF(0f, h_ceil - dst_h, dst_w, dst_h) // 後半はw,hを指定する
|
||||
} else {
|
||||
RectF(w_ceil-dst_w, 0f, dst_w , dst_h) // 後半はw,hを指定する
|
||||
RectF(w_ceil - dst_w, 0f, dst_w, dst_h) // 後半はw,hを指定する
|
||||
}
|
||||
)
|
||||
return b
|
||||
|
|
|
@ -4,13 +4,11 @@ import android.content.Context
|
|||
import android.os.Handler
|
||||
import android.os.SystemClock
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
import jp.juggler.subwaytooter.api.entity.CustomEmoji
|
||||
import jp.juggler.subwaytooter.api.entity.parseList
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.toJsonArray
|
||||
import jp.juggler.util.toRequestBody
|
||||
import okhttp3.RequestBody
|
||||
import org.json.JSONObject
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
@ -33,20 +31,12 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
internal class CacheItem(
|
||||
val instance : String,
|
||||
var list : ArrayList<CustomEmoji>? = null,
|
||||
var listWithAliases : ArrayList<CustomEmoji>? = null
|
||||
) {
|
||||
|
||||
// 参照された時刻
|
||||
var time_used : Long = 0
|
||||
|
||||
var listWithAliases : ArrayList<CustomEmoji>? = null,
|
||||
// ロードした時刻
|
||||
var time_update : Long = 0
|
||||
|
||||
init {
|
||||
time_update = elapsedTime
|
||||
time_used = time_update
|
||||
}
|
||||
}
|
||||
var time_update : Long = elapsedTime,
|
||||
// 参照された時刻
|
||||
var time_used : Long = time_update
|
||||
)
|
||||
|
||||
internal class Request(
|
||||
val instance : String,
|
||||
|
@ -101,7 +91,7 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
) : ArrayList<CustomEmoji>? {
|
||||
try {
|
||||
if(_instance.isEmpty()) return null
|
||||
val instance = _instance.toLowerCase()
|
||||
val instance = _instance.toLowerCase(Locale.JAPAN)
|
||||
|
||||
synchronized(cache) {
|
||||
val item = getCached(elapsedTime, instance)
|
||||
|
@ -123,7 +113,7 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
) : ArrayList<CustomEmoji>? {
|
||||
try {
|
||||
if(_instance.isEmpty()) return null
|
||||
val instance = _instance.toLowerCase()
|
||||
val instance = _instance.toLowerCase(Locale.JAPAN)
|
||||
|
||||
synchronized(cache) {
|
||||
val item = getCached(elapsedTime, instance)
|
||||
|
@ -196,7 +186,7 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
try {
|
||||
val data = if(request.isMisskey) {
|
||||
App1.getHttpCachedString("https://" + request.instance + "/api/meta") { builder ->
|
||||
builder.post( JSONObject().toRequestBody() )
|
||||
builder.post(JSONObject().toRequestBody())
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -237,7 +227,6 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private fun fireCallback(
|
||||
request : Request,
|
||||
list : ArrayList<CustomEmoji>,
|
||||
|
@ -297,15 +286,14 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private fun makeListWithAlias(list : ArrayList<CustomEmoji>?) : ArrayList<CustomEmoji> {
|
||||
val dst = ArrayList<CustomEmoji>()
|
||||
if( list != null) {
|
||||
if(list != null) {
|
||||
dst.addAll(list)
|
||||
for(item in list) {
|
||||
val aliases = item.aliases ?: continue
|
||||
for(alias in aliases) {
|
||||
if( alias.equals(item.shortcode,ignoreCase = true)) continue
|
||||
if(alias.equals(item.shortcode, ignoreCase = true)) continue
|
||||
dst.add(item.makeAlias(alias))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
package jp.juggler.subwaytooter.util
|
||||
|
||||
abstract class WorkerBase : Thread() {
|
||||
abstract class WorkerBase(private val waiter:Any?=null) : Thread() {
|
||||
|
||||
abstract fun cancel()
|
||||
abstract override fun run()
|
||||
|
||||
fun waitEx(ms : Long) {
|
||||
WaitNotifyHelper.waitEx(this,ms)
|
||||
WaitNotifyHelper.waitEx(waiter ?: this,ms)
|
||||
}
|
||||
|
||||
fun notifyEx() {
|
||||
WaitNotifyHelper.notifyEx(this)
|
||||
WaitNotifyHelper.notifyEx(waiter ?:this)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ class NetworkEmojiView : View {
|
|||
fun setEmoji(url : String?) {
|
||||
this.url = url
|
||||
mPaint.isFilterBitmap = true
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun onDraw(canvas : Canvas) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<GridView
|
||||
<jp.juggler.subwaytooter.view.HeaderGridView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/gridView"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -935,5 +935,6 @@
|
|||
<string name="profile_directory_not_supported_on_misskey">ディレクトリはMisskeyでは利用できません</string>
|
||||
<string name="show_in_directory">ディレクトリに表示</string>
|
||||
<string name="featured_hashtags">注目のハッシュタグ</string>
|
||||
<string name="custom_emoji">カスタム絵文字</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -928,5 +928,6 @@
|
|||
<string name="profile_directory_not_supported_on_misskey">Profile directory is not supported on Misskey</string>
|
||||
<string name="show_in_directory">Show in directory</string>
|
||||
<string name="featured_hashtags">Featured hashtags</string>
|
||||
<string name="custom_emoji">Custom emoji</string>
|
||||
|
||||
</resources>
|
Loading…
Reference in New Issue