(Misskey 13)絵文字ピッカーと投稿中のローカルなカスタム絵文字の表示
This commit is contained in:
parent
aa17a0a86e
commit
07daa2f7ef
|
@ -7,6 +7,7 @@
|
||||||
<module name="SubwayTooter.app" target="11" />
|
<module name="SubwayTooter.app" target="11" />
|
||||||
<module name="SubwayTooter.colorpicker" target="11" />
|
<module name="SubwayTooter.colorpicker" target="11" />
|
||||||
<module name="SubwayTooter.emoji" target="11" />
|
<module name="SubwayTooter.emoji" target="11" />
|
||||||
|
<module name="SubwayTooter.icon_material_symbols" target="11" />
|
||||||
<module name="SubwayTooter.sample_apng" target="11" />
|
<module name="SubwayTooter.sample_apng" target="11" />
|
||||||
</bytecodeTargetLevel>
|
</bytecodeTargetLevel>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
<option value="$PROJECT_DIR$/app" />
|
<option value="$PROJECT_DIR$/app" />
|
||||||
<option value="$PROJECT_DIR$/colorpicker" />
|
<option value="$PROJECT_DIR$/colorpicker" />
|
||||||
<option value="$PROJECT_DIR$/emoji" />
|
<option value="$PROJECT_DIR$/emoji" />
|
||||||
|
<option value="$PROJECT_DIR$/icon_material_symbols" />
|
||||||
<option value="$PROJECT_DIR$/sample_apng" />
|
<option value="$PROJECT_DIR$/sample_apng" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.androidTest.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.androidTest.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.main.iml" filepath="$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.main.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.main.iml" filepath="$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.main.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.unitTest.iml" filepath="$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.unitTest.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.unitTest.iml" filepath="$PROJECT_DIR$/.idea/modules/emoji/SubwayTooter.emoji.unitTest.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/icon_material_symbols/SubwayTooter.icon_material_symbols.iml" filepath="$PROJECT_DIR$/.idea/modules/icon_material_symbols/SubwayTooter.icon_material_symbols.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/icon_material_symbols/SubwayTooter.icon_material_symbols.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/icon_material_symbols/SubwayTooter.icon_material_symbols.androidTest.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/icon_material_symbols/SubwayTooter.icon_material_symbols.main.iml" filepath="$PROJECT_DIR$/.idea/modules/icon_material_symbols/SubwayTooter.icon_material_symbols.main.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/icon_material_symbols/SubwayTooter.icon_material_symbols.unitTest.iml" filepath="$PROJECT_DIR$/.idea/modules/icon_material_symbols/SubwayTooter.icon_material_symbols.unitTest.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.iml" filepath="$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.iml" filepath="$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.androidTest.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.androidTest.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.main.iml" filepath="$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.main.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.main.iml" filepath="$PROJECT_DIR$/.idea/modules/sample_apng/SubwayTooter.sample_apng.main.iml" />
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package jp.juggler.subwaytooter
|
package jp.juggler.subwaytooter
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.DownloadManager
|
import android.app.DownloadManager
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
|
@ -296,6 +295,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
||||||
App1.initEdgeToEdge(this)
|
App1.initEdgeToEdge(this)
|
||||||
|
|
||||||
views.pbvImage.background = MediaBackgroundDrawable(
|
views.pbvImage.background = MediaBackgroundDrawable(
|
||||||
|
context = views.root.context,
|
||||||
tileStep = tileStep,
|
tileStep = tileStep,
|
||||||
kind = MediaBackgroundDrawable.Kind.fromIndex(PrefI.ipMediaBackground(this))
|
kind = MediaBackgroundDrawable.Kind.fromIndex(PrefI.ipMediaBackground(this))
|
||||||
)
|
)
|
||||||
|
@ -811,11 +811,12 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
||||||
private fun mediaBackgroundDialog() {
|
private fun mediaBackgroundDialog() {
|
||||||
val ad = ActionsDialog()
|
val ad = ActionsDialog()
|
||||||
for (k in MediaBackgroundDrawable.Kind.values()) {
|
for (k in MediaBackgroundDrawable.Kind.values()) {
|
||||||
|
if (!k.isMediaBackground) continue
|
||||||
ad.addAction(k.name) {
|
ad.addAction(k.name) {
|
||||||
val idx = k.toIndex()
|
val idx = k.toIndex()
|
||||||
appPref.edit().put(PrefI.ipMediaBackground, idx).apply()
|
appPref.edit().put(PrefI.ipMediaBackground, idx).apply()
|
||||||
|
|
||||||
views.pbvImage.background = MediaBackgroundDrawable(
|
views.pbvImage.background = MediaBackgroundDrawable(
|
||||||
|
context = views.root.context,
|
||||||
tileStep = tileStep,
|
tileStep = tileStep,
|
||||||
kind = k
|
kind = k
|
||||||
)
|
)
|
||||||
|
|
|
@ -465,22 +465,29 @@ class App1 : Application() {
|
||||||
suspend fun getHttpCachedString(
|
suspend fun getHttpCachedString(
|
||||||
url: String,
|
url: String,
|
||||||
accessInfo: SavedAccount? = null,
|
accessInfo: SavedAccount? = null,
|
||||||
|
misskeyPost: Boolean = false,
|
||||||
builderBlock: (Request.Builder) -> Unit = {},
|
builderBlock: (Request.Builder) -> Unit = {},
|
||||||
): String? {
|
): String? {
|
||||||
val response: Response
|
val response: Response
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val request_builder = Request.Builder()
|
val request_builder = when {
|
||||||
|
misskeyPost && accessInfo?.isMisskey == true ->
|
||||||
|
accessInfo.putMisskeyApiToken().toPostRequestBuilder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.cacheControl(CACHE_CONTROL)
|
.cacheControl(CACHE_CONTROL)
|
||||||
|
else ->
|
||||||
|
Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.cacheControl(CACHE_CONTROL)
|
||||||
|
.also {
|
||||||
val access_token = accessInfo?.getAccessToken()
|
val access_token = accessInfo?.getAccessToken()
|
||||||
if (access_token?.isNotEmpty() == true) {
|
if (access_token?.isNotEmpty() == true) {
|
||||||
request_builder.header("Authorization", "Bearer $access_token")
|
it.header("Authorization", "Bearer $access_token")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builderBlock(request_builder)
|
builderBlock(request_builder)
|
||||||
|
|
||||||
val call = ok_http_client2.newCall(request_builder.build())
|
val call = ok_http_client2.newCall(request_builder.build())
|
||||||
response = call.await()
|
response = call.await()
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
|
|
|
@ -498,7 +498,6 @@ class TootApiClient(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!sendRequest(result) {
|
if (!sendRequest(result) {
|
||||||
|
|
||||||
val url = "https://${apiHost?.ascii}$path"
|
val url = "https://${apiHost?.ascii}$path"
|
||||||
|
|
||||||
requestBuilder.url(url)
|
requestBuilder.url(url)
|
||||||
|
@ -508,7 +507,8 @@ class TootApiClient(
|
||||||
|
|
||||||
requestBuilder.build()
|
requestBuilder.build()
|
||||||
.also { log.d("request: ${it.method} $url") }
|
.also { log.d("request: ${it.method} $url") }
|
||||||
}) return result
|
}
|
||||||
|
) return result
|
||||||
|
|
||||||
return parseJson(result)
|
return parseJson(result)
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import jp.juggler.util.JsonArray
|
||||||
import jp.juggler.util.JsonException
|
import jp.juggler.util.JsonException
|
||||||
import jp.juggler.util.JsonObject
|
import jp.juggler.util.JsonObject
|
||||||
import jp.juggler.util.LogCategory
|
import jp.juggler.util.LogCategory
|
||||||
import java.util.HashMap
|
|
||||||
|
|
||||||
object EntityUtil {
|
object EntityUtil {
|
||||||
val log = LogCategory("EntityUtil")
|
val log = LogCategory("EntityUtil")
|
||||||
|
@ -122,8 +121,9 @@ inline fun <reified K, reified V> parseMapOrNull(
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified K, reified V> parseMapOrNull(
|
inline fun <reified K, reified V> parseMapOrNull(
|
||||||
factory: (host: Host, src: JsonObject) -> V,
|
factory: (apDomain: Host, apiHost: Host, src: JsonObject) -> V,
|
||||||
host: Host,
|
apDomain: Host,
|
||||||
|
apiHost: Host,
|
||||||
src: JsonArray?,
|
src: JsonArray?,
|
||||||
log: LogCategory = EntityUtil.log,
|
log: LogCategory = EntityUtil.log,
|
||||||
): HashMap<K, V>? where V : Mappable<K> {
|
): HashMap<K, V>? where V : Mappable<K> {
|
||||||
|
@ -132,7 +132,7 @@ inline fun <reified K, reified V> parseMapOrNull(
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
val dst = HashMap<K, V>()
|
val dst = HashMap<K, V>()
|
||||||
for (i in 0 until size) {
|
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 (item != null) dst[item.mapKey] = item
|
||||||
}
|
}
|
||||||
if (dst.isNotEmpty()) return dst
|
if (dst.isNotEmpty()) return dst
|
||||||
|
@ -183,6 +183,22 @@ inline fun <P, reified T> parseItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <P1, P2, reified T> 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 <reified T> parseItem(
|
inline fun <reified T> parseItem(
|
||||||
factory: (serviceType: ServiceType, src: JsonObject) -> T,
|
factory: (serviceType: ServiceType, src: JsonObject) -> T,
|
||||||
serviceType: ServiceType,
|
serviceType: ServiceType,
|
||||||
|
@ -198,9 +214,9 @@ inline fun <reified T> parseItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T> parseList(
|
inline fun <P1, reified T> parseListP1(
|
||||||
factory: (parser: TootParser, src: JsonObject) -> T,
|
factory: (p1: P1, src: JsonObject) -> T,
|
||||||
parser: TootParser,
|
p1: P1,
|
||||||
src: JsonArray?,
|
src: JsonArray?,
|
||||||
log: LogCategory = EntityUtil.log,
|
log: LogCategory = EntityUtil.log,
|
||||||
): ArrayList<T> {
|
): ArrayList<T> {
|
||||||
|
@ -210,7 +226,28 @@ inline fun <reified T> parseList(
|
||||||
if (src_length > 0) {
|
if (src_length > 0) {
|
||||||
dst.ensureCapacity(src_length)
|
dst.ensureCapacity(src_length)
|
||||||
for (i in src.indices) {
|
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 <P1, P2, reified T> parseListP2(
|
||||||
|
factory: (p1: P1, p2: P2, src: JsonObject) -> T,
|
||||||
|
p1: P1,
|
||||||
|
p2: P2,
|
||||||
|
src: JsonArray?,
|
||||||
|
log: LogCategory = EntityUtil.log,
|
||||||
|
): ArrayList<T> {
|
||||||
|
val dst = ArrayList<T>()
|
||||||
|
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)
|
if (item != null) dst.add(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||||
import jp.juggler.util.JsonObject
|
import jp.juggler.util.JsonObject
|
||||||
import jp.juggler.util.LogCategory
|
import jp.juggler.util.LogCategory
|
||||||
|
|
||||||
class MisskeyNoteUpdate(apDomain: Host, src: JsonObject) {
|
class MisskeyNoteUpdate(apDomain: Host, apiHost: Host, src: JsonObject) {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LogCategory("MisskeyNoteUpdate")
|
private val log = LogCategory("MisskeyNoteUpdate")
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ class MisskeyNoteUpdate(apDomain: Host, src: JsonObject) {
|
||||||
userId = EntityId.mayDefault(body2.string("userId"))
|
userId = EntityId.mayDefault(body2.string("userId"))
|
||||||
emoji = body2.jsonObject("emoji")?.let {
|
emoji = body2.jsonObject("emoji")?.let {
|
||||||
try {
|
try {
|
||||||
CustomEmoji.decodeMisskey(apDomain, it)
|
CustomEmoji.decodeMisskey(apDomain, apiHost, it)
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
log.e(ex, "can't parse custom emoji.")
|
log.e(ex, "can't parse custom emoji.")
|
||||||
null
|
null
|
||||||
|
|
|
@ -138,6 +138,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||||
parseMapOrNull(
|
parseMapOrNull(
|
||||||
CustomEmoji.decodeMisskey,
|
CustomEmoji.decodeMisskey,
|
||||||
parser.apDomain,
|
parser.apDomain,
|
||||||
|
parser.apiHost,
|
||||||
src.jsonArray("emojis")
|
src.jsonArray("emojis")
|
||||||
)
|
)
|
||||||
this.profile_emojis = null
|
this.profile_emojis = null
|
||||||
|
@ -276,8 +277,12 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||||
else -> {
|
else -> {
|
||||||
|
|
||||||
// 絵文字データは先に読んでおく
|
// 絵文字データは先に読んでおく
|
||||||
this.custom_emojis =
|
this.custom_emojis = parseMapOrNull(
|
||||||
parseMapOrNull(CustomEmoji.decode, parser.apDomain, src.jsonArray("emojis"))
|
CustomEmoji.decode,
|
||||||
|
parser.apDomain,
|
||||||
|
parser.apiHost,
|
||||||
|
src.jsonArray("emojis")
|
||||||
|
)
|
||||||
|
|
||||||
this.profile_emojis = when (val o = src["profile_emojis"]) {
|
this.profile_emojis = when (val o = src["profile_emojis"]) {
|
||||||
is JsonArray -> parseMapOrNull(::NicoProfileEmoji, o, TootStatus.log)
|
is JsonArray -> parseMapOrNull(::NicoProfileEmoji, o, TootStatus.log)
|
||||||
|
|
|
@ -7,7 +7,6 @@ import jp.juggler.subwaytooter.util.DecodeOptions
|
||||||
import jp.juggler.util.JsonObject
|
import jp.juggler.util.JsonObject
|
||||||
import jp.juggler.util.LogCategory
|
import jp.juggler.util.LogCategory
|
||||||
import jp.juggler.util.notEmpty
|
import jp.juggler.util.notEmpty
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class TootAnnouncement(parser: TootParser, src: JsonObject) {
|
class TootAnnouncement(parser: TootParser, src: JsonObject) {
|
||||||
|
|
||||||
|
@ -44,8 +43,13 @@ class TootAnnouncement(parser: TootParser, src: JsonObject) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
||||||
this.custom_emojis =
|
this.custom_emojis = parseMapOrNull(
|
||||||
parseMapOrNull(CustomEmoji.decode, parser.apDomain, src.jsonArray("emojis"), log)
|
CustomEmoji.decode,
|
||||||
|
parser.apDomain,
|
||||||
|
parser.apiHost,
|
||||||
|
src.jsonArray("emojis"),
|
||||||
|
log
|
||||||
|
)
|
||||||
|
|
||||||
this.tags = TootTag.parseListOrNull(parser, src.jsonArray("tags"))
|
this.tags = TootTag.parseListOrNull(parser, src.jsonArray("tags"))
|
||||||
|
|
||||||
|
|
|
@ -239,11 +239,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
|
||||||
val canUseReference: Boolean?
|
val canUseReference: Boolean?
|
||||||
get() = fedibird_capabilities?.contains("status_reference")
|
get() = fedibird_capabilities?.contains("status_reference")
|
||||||
|
|
||||||
fun versionGE(check: VersionString): Boolean {
|
fun versionGE(check: VersionString) = decoded_version.ge(check)
|
||||||
if (decoded_version.isEmpty || check.isEmpty) return false
|
|
||||||
val i = VersionString.compare(decoded_version, check)
|
|
||||||
return i >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LogCategory("TootInstance")
|
private val log = LogCategory("TootInstance")
|
||||||
|
|
|
@ -238,10 +238,10 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||||
// お気に入りカラムなどではパース直後に変更することがある
|
// お気に入りカラムなどではパース直後に変更することがある
|
||||||
|
|
||||||
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
||||||
this.custom_emojis =
|
this.custom_emojis = parseMapOrNull(
|
||||||
parseMapOrNull(
|
|
||||||
CustomEmoji.decodeMisskey,
|
CustomEmoji.decodeMisskey,
|
||||||
parser.apDomain,
|
parser.apDomain,
|
||||||
|
parser.apiHost,
|
||||||
src.jsonArray("emojis"),
|
src.jsonArray("emojis"),
|
||||||
log
|
log
|
||||||
)
|
)
|
||||||
|
@ -581,10 +581,10 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||||
this.created_at = src.string("created_at")
|
this.created_at = src.string("created_at")
|
||||||
|
|
||||||
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
||||||
this.custom_emojis =
|
this.custom_emojis = parseMapOrNull(
|
||||||
parseMapOrNull(
|
|
||||||
CustomEmoji.decode,
|
CustomEmoji.decode,
|
||||||
parser.apDomain,
|
parser.apDomain,
|
||||||
|
parser.apiHost,
|
||||||
src.jsonArray("emojis"),
|
src.jsonArray("emojis"),
|
||||||
log
|
log
|
||||||
)
|
)
|
||||||
|
@ -809,9 +809,9 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mergeMentions(
|
private fun mergeMentions(
|
||||||
mentions1: java.util.ArrayList<TootMention>?,
|
mentions1: List<TootMention>?,
|
||||||
mentions2: java.util.ArrayList<TootMention>?,
|
mentions2: List<TootMention>?,
|
||||||
): java.util.ArrayList<TootMention>? {
|
): ArrayList<TootMention>? {
|
||||||
val size = (mentions1?.size ?: 0) + (mentions2?.size ?: 0)
|
val size = (mentions1?.size ?: 0) + (mentions2?.size ?: 0)
|
||||||
if (size == 0) return null
|
if (size == 0) return null
|
||||||
val dst = ArrayList<TootMention>(size)
|
val dst = ArrayList<TootMention>(size)
|
||||||
|
|
|
@ -466,7 +466,9 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
||||||
sw(PrefB.bpUseInternalMediaViewer, R.string.use_internal_media_viewer)
|
sw(PrefB.bpUseInternalMediaViewer, R.string.use_internal_media_viewer)
|
||||||
|
|
||||||
spinner(PrefI.ipMediaBackground, R.string.background_pattern) {
|
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)
|
sw(PrefB.bpPriorLocalURL, R.string.prior_local_url_when_open_attachment)
|
||||||
|
|
|
@ -23,6 +23,7 @@ import com.google.android.flexbox.FlexboxLayout
|
||||||
import jp.juggler.subwaytooter.App1
|
import jp.juggler.subwaytooter.App1
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.databinding.EmojiPickerDialogBinding
|
import jp.juggler.subwaytooter.databinding.EmojiPickerDialogBinding
|
||||||
|
import jp.juggler.subwaytooter.drawable.MediaBackgroundDrawable
|
||||||
import jp.juggler.subwaytooter.emoji.*
|
import jp.juggler.subwaytooter.emoji.*
|
||||||
import jp.juggler.subwaytooter.global.appPref
|
import jp.juggler.subwaytooter.global.appPref
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
|
@ -574,7 +575,7 @@ private class EmojiPicker(
|
||||||
category.createFiltered(keywordLower)
|
category.createFiltered(keywordLower)
|
||||||
.takeIf { it.items.isNotEmpty() }
|
.takeIf { it.items.isNotEmpty() }
|
||||||
}.forEach {
|
}.forEach {
|
||||||
if (selectedCategory == null) add(it)
|
if( it.category == EmojiCategory.Custom || selectedCategory == null) add(it)
|
||||||
addAll(it.items)
|
addAll(it.items)
|
||||||
val mod = it.items.size % gridCols
|
val mod = it.items.size % gridCols
|
||||||
if (mod > 0) {
|
if (mod > 0) {
|
||||||
|
@ -743,8 +744,12 @@ private class EmojiPicker(
|
||||||
|
|
||||||
views.etFilter.addTextChangedListener(textWatcher)
|
views.etFilter.addTextChangedListener(textWatcher)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
showFiltered(null, null)
|
showFiltered(null, null)
|
||||||
|
|
||||||
|
val density = activity.resources.displayMetrics.density
|
||||||
|
|
||||||
views.giGrid.intercept = { handleTouch(it, wasIntercept = false) }
|
views.giGrid.intercept = { handleTouch(it, wasIntercept = false) }
|
||||||
views.giGrid.touch = { handleTouch(it, wasIntercept = true) }
|
views.giGrid.touch = { handleTouch(it, wasIntercept = true) }
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,35 @@
|
||||||
package jp.juggler.subwaytooter.drawable
|
package jp.juggler.subwaytooter.drawable
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.graphics.*
|
import android.graphics.*
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import androidx.annotation.ColorInt
|
import jp.juggler.subwaytooter.R
|
||||||
|
import jp.juggler.util.attrColor
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class MediaBackgroundDrawable(
|
class MediaBackgroundDrawable(
|
||||||
|
private val context: Context,
|
||||||
private val tileStep: Int,
|
private val tileStep: Int,
|
||||||
private val kind: Kind
|
private val kind: Kind,
|
||||||
) : Drawable() {
|
) : Drawable() {
|
||||||
|
|
||||||
enum class Kind(@ColorInt val c1: Int, @ColorInt val c2: Int = 0) {
|
enum class Kind(
|
||||||
Black(Color.BLACK),
|
val c1: Context.() -> Int,
|
||||||
BlackTile(Color.BLACK, Color.BLACK or 0x202020),
|
val c2: Context.() -> Int,
|
||||||
Grey(Color.BLACK or 0x787878),
|
val isMediaBackground: Boolean = true,
|
||||||
GreyTile(Color.BLACK or 0x707070, Color.BLACK or 0x808080),
|
) {
|
||||||
White(Color.WHITE),
|
Black({ Color.BLACK }, { 0 }),
|
||||||
WhiteTile(Color.WHITE, Color.BLACK or 0xe0e0e0),
|
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) {
|
override fun draw(canvas: Canvas) {
|
||||||
val bounds = this.bounds
|
val bounds = this.bounds
|
||||||
val c1 = kind.c1
|
val c1 = kind.c1.invoke(context)
|
||||||
val c2 = kind.c2
|
val c2 = kind.c2.invoke(context)
|
||||||
if (c2 == 0) {
|
if (c2 == 0) {
|
||||||
paint.color = c1
|
paint.color = c1
|
||||||
canvas.drawRect(bounds, paint)
|
canvas.drawRect(bounds, paint)
|
||||||
|
|
|
@ -7,7 +7,6 @@ import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.util.JsonArray
|
import jp.juggler.util.JsonArray
|
||||||
import jp.juggler.util.JsonObject
|
import jp.juggler.util.JsonObject
|
||||||
import jp.juggler.util.notEmpty
|
import jp.juggler.util.notEmpty
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
sealed interface EmojiBase
|
sealed interface EmojiBase
|
||||||
|
|
||||||
|
@ -81,7 +80,7 @@ class CustomEmoji(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val decode: (Host, JsonObject) -> CustomEmoji = { apDomain, src ->
|
val decode: (Host, Host, JsonObject) -> CustomEmoji = { apDomain, _, src ->
|
||||||
CustomEmoji(
|
CustomEmoji(
|
||||||
apDomain = apDomain,
|
apDomain = apDomain,
|
||||||
shortcode = src.stringOrThrow("shortcode"),
|
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")
|
val url = src.string("url") ?: error("missing url")
|
||||||
|
|
||||||
CustomEmoji(
|
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<String>? {
|
private fun parseAliases(src: JsonArray?): ArrayList<String>? {
|
||||||
var dst = null as ArrayList<String>?
|
var dst = null as ArrayList<String>?
|
||||||
if (src != null) {
|
if (src != null) {
|
||||||
|
|
|
@ -197,7 +197,11 @@ class StreamConnection(
|
||||||
log.e("$name handleMisskeyMessage: noteUpdated body is null")
|
log.e("$name handleMisskeyMessage: noteUpdated body is null")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fireNoteUpdated(MisskeyNoteUpdate(acctGroup.account.apDomain, body), channelId)
|
fireNoteUpdated(MisskeyNoteUpdate(
|
||||||
|
acctGroup.account.apDomain,
|
||||||
|
acctGroup.account.apiHost,
|
||||||
|
body
|
||||||
|
), channelId)
|
||||||
}
|
}
|
||||||
|
|
||||||
"notification" -> {
|
"notification" -> {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import jp.juggler.subwaytooter.App1
|
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.emoji.CustomEmoji
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
|
@ -35,6 +35,7 @@ class CustomEmojiLister(
|
||||||
val key: String,
|
val key: String,
|
||||||
var list: List<CustomEmoji>,
|
var list: List<CustomEmoji>,
|
||||||
var listWithAliases: List<CustomEmoji>,
|
var listWithAliases: List<CustomEmoji>,
|
||||||
|
var mapShortCode: Map<String, CustomEmoji>,
|
||||||
// ロードした時刻
|
// ロードした時刻
|
||||||
var timeUpdate: Long = elapsedTime,
|
var timeUpdate: Long = elapsedTime,
|
||||||
// 参照された時刻
|
// 参照された時刻
|
||||||
|
@ -63,7 +64,12 @@ class CustomEmojiLister(
|
||||||
// エラーキャッシュ
|
// エラーキャッシュ
|
||||||
internal val cacheError = ConcurrentHashMap<String, Long>()
|
internal val cacheError = ConcurrentHashMap<String, Long>()
|
||||||
|
|
||||||
private val cacheErrorItem = CacheItem("error", emptyList(), emptyList())
|
private val cacheErrorItem = CacheItem(
|
||||||
|
key = "error",
|
||||||
|
list = emptyList(),
|
||||||
|
listWithAliases = emptyList(),
|
||||||
|
mapShortCode = emptyMap(),
|
||||||
|
)
|
||||||
|
|
||||||
// ロード要求
|
// ロード要求
|
||||||
internal val queue = ConcurrentLinkedQueue<Request>()
|
internal val queue = ConcurrentLinkedQueue<Request>()
|
||||||
|
@ -75,18 +81,21 @@ class CustomEmojiLister(
|
||||||
cacheError.clear()
|
cacheError.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCached(now: Long, accessInfo: SavedAccount): CacheItem? {
|
private fun getCached(now: Long, accessInfo: SavedAccount) =
|
||||||
val host = accessInfo.apiHost.ascii
|
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) {
|
if (item != null && now - item.timeUpdate <= ERROR_EXPIRE) {
|
||||||
item.timeUsed = now
|
item.timeUsed = now
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
// エラーキャッシュ
|
// エラーキャッシュ
|
||||||
val timeError = cacheError[host]
|
val timeError = cacheError[apiHostAscii]
|
||||||
if (timeError != null && now < timeError + ERROR_EXPIRE) {
|
if (timeError != null && now < timeError + ERROR_EXPIRE) {
|
||||||
return cacheErrorItem
|
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() {
|
private inner class Worker : WorkerBase() {
|
||||||
|
|
||||||
override fun cancel() {
|
override fun cancel() {
|
||||||
|
@ -186,39 +198,92 @@ class CustomEmojiLister(
|
||||||
|
|
||||||
val accessInfo = request.accessInfo
|
val accessInfo = request.accessInfo
|
||||||
val cacheKey = accessInfo.apiHost.ascii
|
val cacheKey = accessInfo.apiHost.ascii
|
||||||
val data = if (accessInfo.isMisskey) {
|
|
||||||
|
// v12のmetaからemojisをパース
|
||||||
|
suspend fun misskeyEmojis12(): List<CustomEmoji>? =
|
||||||
App1.getHttpCachedString(
|
App1.getHttpCachedString(
|
||||||
"https://$cacheKey/api/meta",
|
"https://$cacheKey/api/meta",
|
||||||
accessInfo = accessInfo
|
accessInfo = accessInfo
|
||||||
) { builder ->
|
) { builder ->
|
||||||
builder.post(JsonObject().toRequestBody())
|
builder.post(JsonObject().toRequestBody())
|
||||||
|
}?.decodeJsonObject()
|
||||||
|
?.jsonArray("emojis")
|
||||||
|
?.let { emojis12 ->
|
||||||
|
parseListP2(
|
||||||
|
CustomEmoji.decodeMisskey,
|
||||||
|
accessInfo.apDomain,
|
||||||
|
accessInfo.apiHost,
|
||||||
|
emojis12,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
// v13のemojisを読む
|
||||||
|
suspend fun misskeyEmojis13(): List<CustomEmoji>? =
|
||||||
|
App1.getHttpCachedString(
|
||||||
|
"https://$cacheKey/api/emojis",
|
||||||
|
accessInfo = accessInfo,
|
||||||
|
misskeyPost = true,
|
||||||
|
) { builder ->
|
||||||
|
builder.post(JsonObject().toRequestBody())
|
||||||
|
}
|
||||||
|
?.decodeJsonObject()
|
||||||
|
?.jsonArray("emojis")
|
||||||
|
?.let { emojis13 ->
|
||||||
|
parseListP2(
|
||||||
|
CustomEmoji.decodeMisskey13,
|
||||||
|
accessInfo.apDomain,
|
||||||
|
accessInfo.apiHost,
|
||||||
|
emojis13,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// マストドンのカスタム絵文字一覧を読む
|
||||||
|
suspend fun mastodonEmojis() =
|
||||||
App1.getHttpCachedString(
|
App1.getHttpCachedString(
|
||||||
"https://$cacheKey/api/v1/custom_emojis",
|
"https://$cacheKey/api/v1/custom_emojis",
|
||||||
accessInfo = accessInfo
|
accessInfo = accessInfo
|
||||||
|
)?.let { data ->
|
||||||
|
parseListP2(
|
||||||
|
CustomEmoji.decode,
|
||||||
|
accessInfo.apDomain,
|
||||||
|
accessInfo.apiHost,
|
||||||
|
data.decodeJsonArray()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
var list: List<CustomEmoji>? = null
|
|
||||||
var listWithAlias: List<CustomEmoji>? = null
|
val list = when {
|
||||||
if (data != null) {
|
accessInfo.isMastodon -> mastodonEmojis()
|
||||||
val a = decodeEmojiList(data, accessInfo)
|
else -> misskeyEmojis12() ?: misskeyEmojis13()
|
||||||
list = a
|
}?.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.shortcode })
|
||||||
listWithAlias = makeListWithAlias(a)
|
|
||||||
|
val listWithAlias = list?.let { srcList ->
|
||||||
|
ArrayList<CustomEmoji>(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) {
|
return synchronized(cache) {
|
||||||
val now = elapsedTime
|
val now = elapsedTime
|
||||||
if (list == null || listWithAlias == null) {
|
if (list == null || listWithAlias == null) {
|
||||||
cacheError[cacheKey] = now
|
cacheError[cacheKey] = now
|
||||||
error("can't load custom emoji for ${accessInfo.apiHost}")
|
error("can't load custom emoji for ${accessInfo.apiHost}")
|
||||||
} else {
|
} else {
|
||||||
|
val mapShortCode = buildMap {
|
||||||
|
list.forEach { put(it.alias ?: it.shortcode, it) }
|
||||||
|
listWithAlias.forEach { put(it.alias ?: it.shortcode, it) }
|
||||||
|
}
|
||||||
var item = cache[cacheKey]
|
var item = cache[cacheKey]
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = CacheItem(cacheKey, list, listWithAlias)
|
item = CacheItem(cacheKey, list, listWithAlias, mapShortCode)
|
||||||
cache[cacheKey] = item
|
cache[cacheKey] = item
|
||||||
} else {
|
} else {
|
||||||
item.list = list
|
item.list = list
|
||||||
item.listWithAliases = listWithAlias
|
item.listWithAliases = listWithAlias
|
||||||
|
item.mapShortCode = mapShortCode
|
||||||
item.timeUpdate = now
|
item.timeUpdate = now
|
||||||
}
|
}
|
||||||
item
|
item
|
||||||
|
@ -249,39 +314,5 @@ class CustomEmojiLister(
|
||||||
if (++removed >= over) break
|
if (++removed >= over) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeEmojiList(
|
|
||||||
data: String,
|
|
||||||
accessInfo: SavedAccount,
|
|
||||||
): List<CustomEmoji> =
|
|
||||||
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<CustomEmoji>,
|
|
||||||
) = ArrayList<CustomEmoji>().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 })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package jp.juggler.subwaytooter.util
|
package jp.juggler.subwaytooter.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.SystemClock
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
import android.util.SparseBooleanArray
|
import android.util.SparseBooleanArray
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
import jp.juggler.subwaytooter.App1
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||||
import jp.juggler.subwaytooter.emoji.EmojiMap
|
import jp.juggler.subwaytooter.emoji.EmojiMap
|
||||||
|
@ -382,6 +384,11 @@ object EmojiDecoder {
|
||||||
|
|
||||||
// カスタム絵文字
|
// カスタム絵文字
|
||||||
val emojiCustom = emojiMapCustom?.get(name)
|
val emojiCustom = emojiMapCustom?.get(name)
|
||||||
|
?: App1.custom_emoji_lister.getCachedEmoji(
|
||||||
|
options.linkHelper?.apiHost?.ascii,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
|
||||||
if (emojiCustom != null) {
|
if (emojiCustom != null) {
|
||||||
val url = when {
|
val url = when {
|
||||||
PrefB.bpDisableEmojiAnimation() && emojiCustom.staticUrl?.isNotEmpty() == true -> emojiCustom.staticUrl
|
PrefB.bpDisableEmojiAnimation() && emojiCustom.staticUrl?.isNotEmpty() == true -> emojiCustom.staticUrl
|
||||||
|
|
|
@ -161,4 +161,11 @@ class VersionString(src: String?) : Comparable<VersionString> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
/build
|
|
@ -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'
|
||||||
|
}
|
|
@ -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
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
</manifest>
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
include ':app', ':sample_apng', ':apng_android', ':apng', ':colorpicker', ':emoji'
|
include ':app', ':sample_apng', ':apng_android', ':apng', ':colorpicker', ':emoji'
|
||||||
|
include ':icon_material_symbols'
|
||||||
|
|
Loading…
Reference in New Issue