アプリ設定に絵文字セクションと設定項目をいくつか追加。絵文字のアスペクト比をDBに保存する。
This commit is contained in:
parent
d83efef4d9
commit
aefcc7c798
|
@ -236,6 +236,9 @@ class ApngFrames private constructor(
|
|||
}
|
||||
}
|
||||
|
||||
val aspect:Float?
|
||||
get() = if( width<=0 || height<=0) null else width.toFloat().div(height)
|
||||
|
||||
constructor(bitmap: Bitmap) : this() {
|
||||
defaultImage = bitmap
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import jp.juggler.subwaytooter.span.MyClickableSpan
|
|||
import jp.juggler.subwaytooter.span.MyClickableSpanHandler
|
||||
import jp.juggler.subwaytooter.table.daoSavedAccount
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions.Companion.reloadEmojiScale
|
||||
import jp.juggler.subwaytooter.view.MyDrawerLayout
|
||||
import jp.juggler.subwaytooter.view.MyEditText
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
|
@ -370,6 +371,7 @@ class ActMain : AppCompatActivity(),
|
|||
|
||||
acctPadLr = (0.5f + 4f * density).toInt()
|
||||
reloadTextSize()
|
||||
reloadEmojiScale()
|
||||
|
||||
initUI()
|
||||
|
||||
|
@ -455,6 +457,7 @@ class ActMain : AppCompatActivity(),
|
|||
super.onStart()
|
||||
galaxyBackgroundWorkaround()
|
||||
benchmark("onStart total") {
|
||||
reloadEmojiScale()
|
||||
benchmark("reload color") { reloadColors() }
|
||||
benchmark("reload timezone") { reloadTimeZone() }
|
||||
|
||||
|
|
|
@ -120,8 +120,8 @@ fun ActMain.reactionAdd(
|
|||
activity,
|
||||
accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
enlargeCustomEmoji = 1.5f,
|
||||
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
||||
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
val emojiSpan = TootReaction.toSpannableStringBuilder(options, code, urlArg)
|
||||
|
@ -219,8 +219,8 @@ fun ActMain.reactionRemove(
|
|||
activity,
|
||||
accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
enlargeCustomEmoji = 1.5f,
|
||||
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
||||
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
val emojiSpan = reaction.toSpannableStringBuilder(options, status)
|
||||
|
@ -330,8 +330,8 @@ private fun ActMain.reactionWithoutUi(
|
|||
activity,
|
||||
accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
enlargeCustomEmoji = 1.5f,
|
||||
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
||||
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
val emojiSpan = TootReaction.toSpannableStringBuilder(options, reactionCode, reactionImage)
|
||||
|
|
|
@ -14,6 +14,7 @@ import jp.juggler.subwaytooter.util.emojiSizeMode
|
|||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.daoUserRelation
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions.Companion.emojiScaleUserName
|
||||
import jp.juggler.subwaytooter.util.LinkHelper
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||
import jp.juggler.subwaytooter.util.matchHost
|
||||
|
@ -168,6 +169,8 @@ open class TootAccount(
|
|||
emojiMapProfile = profile_emojis,
|
||||
emojiMapCustom = custom_emojis,
|
||||
authorDomain = this,
|
||||
enlargeCustomEmoji = emojiScaleUserName,
|
||||
enlargeEmoji = emojiScaleUserName,
|
||||
).decodeEmoji(sv)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import android.text.Spannable
|
|||
import jp.juggler.subwaytooter.api.TootAccountMap
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions.Companion.emojiScaleMastodon
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions.Companion.emojiScaleMisskey
|
||||
|
||||
class TootAccountRef private constructor(
|
||||
val mapId: Int,
|
||||
|
@ -50,6 +52,8 @@ class TootAccountRef private constructor(
|
|||
unwrapEmojiImageTag = true,
|
||||
authorDomain = account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
enlargeEmoji = if(parser.linkHelper.isMisskey) emojiScaleMisskey else emojiScaleMastodon,
|
||||
enlargeCustomEmoji = if(parser.linkHelper.isMisskey) emojiScaleMisskey else emojiScaleMastodon,
|
||||
).decodeHTML(account.note),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -343,7 +343,6 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
|
||||
sw(PrefB.bpDontRemoveDeletedToot, R.string.dont_remove_deleted_toot_from_timeline)
|
||||
sw(PrefB.bpCustomEmojiSeparatorZwsp, R.string.custom_emoji_separator_zwsp)
|
||||
sw(PrefB.bpShowTranslateButton, R.string.show_translate_button)
|
||||
|
||||
item(
|
||||
|
@ -464,10 +463,6 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
R.string.warn_hashtag_ascii_and_non_ascii
|
||||
)
|
||||
|
||||
sw(
|
||||
PrefB.bpEmojiPickerCloseOnSelected,
|
||||
R.string.close_emoji_picker_when_selected
|
||||
)
|
||||
|
||||
sw(PrefB.bpIgnoreTextInSharedMedia, R.string.ignore_text_in_shared_media)
|
||||
}
|
||||
|
@ -525,6 +520,58 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
sw(PrefB.bpDisableEmojiAnimation, R.string.disable_custom_emoji_animation)
|
||||
}
|
||||
|
||||
section(R.string.emoji){
|
||||
|
||||
sw(PrefB.bpUseTwemoji, R.string.use_twemoji_emoji)
|
||||
|
||||
sw(PrefB.bpEmojioneShortcode, R.string.emojione_shortcode_support) {
|
||||
desc = R.string.emojione_shortcode_support_desc
|
||||
}
|
||||
|
||||
sw(PrefB.bpEmojiPickerCategoryOther, R.string.show_emoji_picker_other_category)
|
||||
|
||||
sw(
|
||||
PrefB.bpEmojiPickerCloseOnSelected,
|
||||
R.string.close_emoji_picker_when_selected
|
||||
)
|
||||
|
||||
sw(PrefB.bpCustomEmojiSeparatorZwsp, R.string.custom_emoji_separator_zwsp)
|
||||
|
||||
text(
|
||||
PrefS.spEmojiSizeMastodon,
|
||||
R.string.emoji_size_mastodon,
|
||||
InputTypeEx.number
|
||||
)
|
||||
text(
|
||||
PrefS.spEmojiSizeMisskey,
|
||||
R.string.emoji_size_misskey,
|
||||
InputTypeEx.number
|
||||
)
|
||||
text(
|
||||
PrefS.spEmojiSizeReaction,
|
||||
R.string.emoji_size_reaction,
|
||||
InputTypeEx.number
|
||||
)
|
||||
text(
|
||||
PrefS.spEmojiSizeUserName,
|
||||
R.string.emoji_size_user_name,
|
||||
InputTypeEx.number
|
||||
)
|
||||
|
||||
spinnerSimple(
|
||||
PrefI.ipEmojiWideMode,
|
||||
R.string.emoji_wide_mode,
|
||||
R.string.auto,
|
||||
R.string.enabled,
|
||||
R.string.disabled,
|
||||
)
|
||||
text(
|
||||
PrefS.spEmojiPixels,
|
||||
R.string.emoji_texture_pixels,
|
||||
InputTypeEx.number
|
||||
)
|
||||
}
|
||||
|
||||
section(R.string.appearance) {
|
||||
sw(PrefB.bpSimpleList, R.string.simple_list)
|
||||
sw(PrefB.bpShowFollowButtonInButtonBar, R.string.show_follow_button_in_button_bar)
|
||||
|
@ -789,10 +836,6 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
)
|
||||
sw(PrefB.bpShowBookmarkButton, R.string.show_bookmark_button)
|
||||
sw(PrefB.bpHideFollowCount, R.string.hide_followers_count)
|
||||
sw(PrefB.bpEmojioneShortcode, R.string.emojione_shortcode_support) {
|
||||
desc = R.string.emojione_shortcode_support_desc
|
||||
}
|
||||
sw(PrefB.bpUseTwemoji, R.string.use_twemoji_emoji)
|
||||
|
||||
sw(PrefB.bpKeepReactionSpace, R.string.keep_reaction_space)
|
||||
|
||||
|
@ -1032,7 +1075,6 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
|
||||
sw(PrefB.bpEnableDeprecatedSomething,R.string.enable_deprecated_something)
|
||||
|
||||
sw(PrefB.bpEmojiPickerCategoryOther, R.string.show_emoji_picker_other_category)
|
||||
action(R.string.drawable_list) {
|
||||
action = { startActivity(Intent(this, ActDrawableList::class.java)) }
|
||||
}
|
||||
|
|
|
@ -61,8 +61,8 @@ fun ColumnViewHolder.updateReactionQueryView() {
|
|||
act,
|
||||
column.accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
enlargeCustomEmoji = 1.5f,
|
||||
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
||||
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
||||
emojiSizeMode = column.accessInfo.emojiSizeMode(),
|
||||
)
|
||||
|
||||
|
|
|
@ -624,6 +624,8 @@ internal class ViewHolderHeaderProfile(
|
|||
emojiMapProfile = who.profile_emojis,
|
||||
authorDomain = who,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
enlargeCustomEmoji = DecodeOptions.emojiScaleUserName,
|
||||
enlargeEmoji = DecodeOptions.emojiScaleUserName,
|
||||
)
|
||||
|
||||
val nameTypeface = ActMain.timelineFontBold
|
||||
|
|
|
@ -290,7 +290,6 @@ private class EmojiPicker(
|
|||
item.customEmoji.url
|
||||
},
|
||||
initialAspect = item.customEmoji.aspect,
|
||||
defaultWidth = gridSize,
|
||||
defaultHeight = gridSize,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.google.android.flexbox.FlexWrap
|
|||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.flexbox.JustifyContent
|
||||
import jp.juggler.subwaytooter.ActMain
|
||||
import jp.juggler.subwaytooter.ActMain.Companion.boostButtonSize
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.action.reactionAdd
|
||||
import jp.juggler.subwaytooter.action.reactionFromAnotherAccount
|
||||
|
@ -18,6 +19,7 @@ import jp.juggler.subwaytooter.api.entity.TootReaction
|
|||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.pref.PrefI
|
||||
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
|
||||
import jp.juggler.subwaytooter.util.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||
|
@ -29,6 +31,7 @@ import jp.juggler.util.ui.attrColor
|
|||
import jp.juggler.util.ui.getAdaptiveRippleDrawableRound
|
||||
import org.jetbrains.anko.allCaps
|
||||
import org.jetbrains.anko.dip
|
||||
import org.jetbrains.anko.wrapContent
|
||||
|
||||
private val log = LogCategory("ItemViewHolderReaction")
|
||||
|
||||
|
@ -42,11 +45,10 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
|
|||
|
||||
fun Float.round() = (this + 0.5f).toInt()
|
||||
|
||||
val imageScale = 1.5f
|
||||
val buttonHeight = ActMain.boostButtonSize // px
|
||||
val marginBetween = (buttonHeight * 0.05f).round()
|
||||
val paddingH = (buttonHeight * 0.1f).round()
|
||||
val textHeight = (buttonHeight * 0.7f) / imageScale
|
||||
val imageScale = DecodeOptions.emojiScaleReaction
|
||||
val textHeight = (boostButtonSize.toFloat()/2)
|
||||
val marginBetween = (boostButtonSize * 0.05f).round()
|
||||
val paddingH = (boostButtonSize * 0.1f).round()
|
||||
|
||||
val act = this@makeReactionsView.activity // not Button(View).getActivity()
|
||||
|
||||
|
@ -63,7 +65,7 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
|
|||
|
||||
if (reactionSet?.isEmpty() != false) {
|
||||
val v = View(act).apply {
|
||||
layoutParams = FlexboxLayout.LayoutParams(0, buttonHeight)
|
||||
layoutParams = FlexboxLayout.LayoutParams(0, wrapContent)
|
||||
setPadding(paddingH, 0, paddingH, 0)
|
||||
}
|
||||
box.addView(v)
|
||||
|
@ -85,13 +87,13 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
|
|||
val b = AppCompatButton(act).apply {
|
||||
layoutParams = FlexboxLayout.LayoutParams(
|
||||
FlexboxLayout.LayoutParams.WRAP_CONTENT,
|
||||
buttonHeight,
|
||||
wrapContent,
|
||||
).apply {
|
||||
if (index > 0) startMargin = marginBetween
|
||||
bottomMargin = dip(3)
|
||||
}
|
||||
gravity = Gravity.CENTER
|
||||
minWidthCompat = buttonHeight
|
||||
minWidthCompat = textHeight.round()
|
||||
|
||||
background = if (reactionSet.isMyReaction(reaction)) {
|
||||
// 自分がリアクションしたやつは背景を変える
|
||||
|
|
|
@ -354,8 +354,8 @@ fun ItemViewHolder.showBoost(
|
|||
activity,
|
||||
accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
enlargeCustomEmoji = 1.5f,
|
||||
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
||||
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
val ssb = reaction.toSpannableStringBuilder(options, boostStatus)
|
||||
|
|
|
@ -61,7 +61,7 @@ object MisskeyMarkdownDecoder {
|
|||
fun decodeMarkdown(options: DecodeOptions, src: String?) =
|
||||
SpannableStringBuilderEx().apply {
|
||||
val save = options.enlargeCustomEmoji
|
||||
options.enlargeCustomEmoji = 2.5f
|
||||
options.enlargeCustomEmoji = DecodeOptions.emojiScaleMisskey
|
||||
try {
|
||||
val env = SpanOutputEnv(options, this)
|
||||
|
||||
|
|
|
@ -124,4 +124,9 @@ object PrefI {
|
|||
val ipMediaBackground = IntPref("MediaBackground", 1)
|
||||
|
||||
val ipLogSaveLevel = IntPref("LogSaveLevel", Log.WARN)
|
||||
|
||||
const val EMOJI_WIDE_AUTO = 0
|
||||
const val EMOJI_WIDE_ENABLE = 1
|
||||
const val EMOJI_WIDE_DISABLE = 2
|
||||
val ipEmojiWideMode = IntPref("EmojiWideMode", EMOJI_WIDE_AUTO)
|
||||
}
|
||||
|
|
|
@ -53,4 +53,10 @@ object PrefS {
|
|||
val spTimelineSpacing = StringPref("TimelineSpacing", "")
|
||||
|
||||
val spEventTextAlpha = StringPref("EventTextAlpha", "")
|
||||
}
|
||||
|
||||
val spEmojiSizeMastodon = StringPref("EmojiSizeMastodon", "100")
|
||||
val spEmojiSizeMisskey = StringPref("EmojiSizeMisskey", "250")
|
||||
val spEmojiSizeReaction = StringPref("EmojiSizeReaction", "150")
|
||||
val spEmojiSizeUserName = StringPref("EmojiSizeUserName", "100")
|
||||
val spEmojiPixels = StringPref("spEmojiPixels", "128")
|
||||
}
|
||||
|
|
|
@ -19,4 +19,5 @@ class StringPref(
|
|||
defVal != pref.getString(key, defVal)
|
||||
|
||||
fun toInt() = value.toIntOrNull() ?: defVal.toInt()
|
||||
fun toFloat() = value.toFloatOrNull() ?: defVal.toFloat()
|
||||
}
|
||||
|
|
|
@ -21,13 +21,13 @@ class NetworkEmojiSpan constructor(
|
|||
private val url: String,
|
||||
sizeMode: EmojiSizeMode,
|
||||
scale: Float = 1f,
|
||||
private val initialAspect:Float? = null,
|
||||
private val initialAspect: Float? = null,
|
||||
private val errorDrawableId: Int = R.drawable.outline_broken_image_24,
|
||||
) : ReplacementSpan(), AnimatableSpan {
|
||||
|
||||
companion object {
|
||||
internal val log = LogCategory("NetworkEmojiSpan")
|
||||
private const val scaleRatio = 1.14f
|
||||
const val scaleRatio = 1.14f
|
||||
private const val descentRatio = 0.211f
|
||||
|
||||
// 最大幅
|
||||
|
@ -47,13 +47,23 @@ class NetworkEmojiSpan constructor(
|
|||
|
||||
private val rectSrc = Rect()
|
||||
|
||||
private var lastMeasuredWidth = 0f
|
||||
|
||||
private val emojiImageRect = EmojiImageRect(
|
||||
sizeMode = sizeMode,
|
||||
scale = scale,
|
||||
scaleRatio = scaleRatio,
|
||||
descentRatio = descentRatio,
|
||||
maxEmojiWidth = maxEmojiWidth,
|
||||
layout = { _,_-> invalidateCallback?.requestLayout() },
|
||||
// layout = { _, _ ->
|
||||
// when (val cb = invalidateCallback) {
|
||||
// null -> log.w("layoutCb is null")
|
||||
// else -> {
|
||||
// log.i("layoutCb requestLayout. url=$url")
|
||||
// cb.requestLayout()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
)
|
||||
|
||||
override fun setInvalidateCallback(
|
||||
|
@ -72,8 +82,12 @@ class NetworkEmojiSpan constructor(
|
|||
fm: Paint.FontMetricsInt?,
|
||||
): Int {
|
||||
emojiImageRect.updateRect(
|
||||
url = url, aspectArg = initialAspect, paint.textSize, baseline = 0f
|
||||
url = url,
|
||||
aspectArg = initialAspect,
|
||||
paint.textSize,
|
||||
baseline = 0f
|
||||
)
|
||||
|
||||
val height = (emojiImageRect.emojiHeight + 0.5f).toInt()
|
||||
if (fm != null) {
|
||||
val cDescent = (0.5f + height * descentRatio).toInt()
|
||||
|
@ -83,7 +97,10 @@ class NetworkEmojiSpan constructor(
|
|||
if (fm.descent < cDescent) fm.descent = cDescent
|
||||
if (fm.bottom < cDescent) fm.bottom = cDescent
|
||||
}
|
||||
return (emojiImageRect.emojiWidth + 0.5f).toInt()
|
||||
|
||||
val width = emojiImageRect.emojiWidth
|
||||
lastMeasuredWidth = width
|
||||
return (width + 0.5f).toInt()
|
||||
}
|
||||
|
||||
override fun draw(
|
||||
|
@ -115,6 +132,7 @@ class NetworkEmojiSpan constructor(
|
|||
|
||||
// APNGデータの取得
|
||||
val frames = App1.custom_emoji_cache.getFrames(refDrawTarget, url) {
|
||||
handleFrameLoaded(it)
|
||||
invalidateCallback.delayInvalidate(0L)
|
||||
} ?: return false
|
||||
|
||||
|
@ -144,6 +162,17 @@ class NetworkEmojiSpan constructor(
|
|||
textPaint.textSize,
|
||||
baseline.toFloat()
|
||||
)
|
||||
val clipBounds = canvas.clipBounds
|
||||
val clipWidth = clipBounds.width()
|
||||
// 最後にgetSizeで返した幅と異なるか、現在のTextViewのClip幅より大きいなら
|
||||
// 再レイアウトを要求する
|
||||
if (emojiImageRect.emojiWidth != lastMeasuredWidth ){
|
||||
log.i("requestLayout by width changed")
|
||||
invalidateCallback.requestLayout()
|
||||
}else if(emojiImageRect.emojiWidth > clipWidth) {
|
||||
log.i("requestLayout by clipWidth ${emojiImageRect.emojiWidth}/${clipWidth}")
|
||||
invalidateCallback.requestLayout()
|
||||
}
|
||||
|
||||
canvas.save()
|
||||
try {
|
||||
|
@ -172,6 +201,12 @@ class NetworkEmojiSpan constructor(
|
|||
return true
|
||||
}
|
||||
|
||||
private fun handleFrameLoaded(frames: ApngFrames?) {
|
||||
frames?.aspect?.let {
|
||||
invalidateCallback?.requestLayout()
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawError(
|
||||
canvas: Canvas,
|
||||
x: Float,
|
||||
|
@ -190,7 +225,7 @@ class NetworkEmojiSpan constructor(
|
|||
url = "",
|
||||
aspectArg = srcWidth / srcHeight,
|
||||
textSize = textPaint.textSize,
|
||||
baseline = baseline.toFloat()
|
||||
baseline = baseline.toFloat(),
|
||||
)
|
||||
|
||||
canvas.save()
|
||||
|
|
|
@ -70,7 +70,9 @@ import java.util.concurrent.atomic.AtomicReference
|
|||
// 2022/3/15 63=>64 SavedAccountテーブルに項目追加
|
||||
// 2023/2/2 64 => 65 PushMessage, AccountNotificationStatus,NotificationShown テーブルの追加。
|
||||
// 2023/2/11 65=>66 LogDataがなければ作る
|
||||
const val DB_VERSION = 66
|
||||
// 2023/2/26 66=>67 ImageAspect テーブルの追加
|
||||
|
||||
const val DB_VERSION = 67
|
||||
const val DB_NAME = "app_db"
|
||||
|
||||
// テーブルのリスト
|
||||
|
@ -96,6 +98,7 @@ val TABLE_LIST = arrayOf(
|
|||
PushMessage.Companion, // v65
|
||||
AccountNotificationStatus.Companion, // v65,
|
||||
NotificationShown.Companion, // v65
|
||||
ImageAspect.Companion, // v67
|
||||
)
|
||||
|
||||
private val log = LogCategory("AppDatabaseHolder")
|
||||
|
@ -201,3 +204,6 @@ val daoTagHistory get() = TagHistory.Access(appDatabase)
|
|||
val daoUserRelation get() = UserRelation.Access(appDatabase)
|
||||
val daoPushMessage get() = PushMessage.Access(appDatabase)
|
||||
val daoLogData get() = LogData.Access(appDatabase)
|
||||
val daoImageAspect by lazy{
|
||||
ImageAspect.Access(appDatabase)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package jp.juggler.subwaytooter.table
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import jp.juggler.util.data.*
|
||||
import jp.juggler.util.log.LogCategory
|
||||
import kotlin.math.abs
|
||||
|
||||
// リスト要素のデータ
|
||||
class ImageAspect(
|
||||
var id: Long = 0L,
|
||||
var url: String = "",
|
||||
var aspect: Float = 1f,
|
||||
) {
|
||||
companion object : TableCompanion {
|
||||
private val log = LogCategory("ImageAspect")
|
||||
override val table = "image_aspect"
|
||||
private const val COL_ID = "_id"
|
||||
private const val COL_URL = "u"
|
||||
private const val COL_ASPECT = "aspect"
|
||||
|
||||
val columnList = MetaColumns(table, 67).apply {
|
||||
column(0, COL_ID, "INTEGER PRIMARY KEY")
|
||||
column(0, COL_URL, "text not null")
|
||||
column(0, COL_ASPECT, "real not null")
|
||||
createExtra={
|
||||
arrayOf(
|
||||
"create unique index if not exists ${table}_u on $table($COL_URL)",
|
||||
)
|
||||
}
|
||||
}
|
||||
override fun onDBCreate(db: SQLiteDatabase) {
|
||||
log.d("onDBCreate!")
|
||||
columnList.onDBCreate(db)
|
||||
}
|
||||
|
||||
override fun onDBUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
columnList.onDBUpgrade(db,oldVersion,newVersion)
|
||||
}
|
||||
}
|
||||
|
||||
class Access(val db: SQLiteDatabase) {
|
||||
fun load(url:String) :Float?=
|
||||
db.rawQuery(
|
||||
"select $COL_ASPECT from $table where $COL_URL=?",
|
||||
arrayOf(url),
|
||||
).use { cursor->
|
||||
when {
|
||||
cursor.moveToNext() -> cursor.getFloat(0)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun save(url:String,aspect:Float) {
|
||||
val oldAspect = load(url)
|
||||
if( oldAspect != null && abs(oldAspect-aspect) <= 1.4E-40F) return
|
||||
ContentValues().apply {
|
||||
put(COL_URL, url)
|
||||
put(COL_ASPECT, aspect)
|
||||
}.replaceTo(db, table)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,10 +10,14 @@ import android.os.SystemClock
|
|||
import com.caverock.androidsvg.SVG
|
||||
import jp.juggler.apng.ApngFrames
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.pref.PrefS
|
||||
import jp.juggler.subwaytooter.table.EmojiCacheDbOpenHelper
|
||||
import jp.juggler.subwaytooter.table.daoImageAspect
|
||||
import jp.juggler.util.coroutine.EmptyScope
|
||||
import jp.juggler.util.data.*
|
||||
import jp.juggler.util.log.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
@ -45,7 +49,7 @@ class CustomEmojiCache(
|
|||
private class Request(
|
||||
val refTarget: WeakReference<Any>,
|
||||
val url: String,
|
||||
val onLoadComplete: () -> Unit,
|
||||
val onLoadComplete: (ApngFrames?) -> Unit,
|
||||
)
|
||||
|
||||
// APNGデコード済のキャッシュデータ
|
||||
|
@ -115,7 +119,7 @@ class CustomEmojiCache(
|
|||
fun getFrames(
|
||||
refDrawTarget: WeakReference<Any>?,
|
||||
url: String,
|
||||
onLoadComplete: () -> Unit,
|
||||
onLoadComplete: (ApngFrames?) -> Unit,
|
||||
): ApngFrames? {
|
||||
try {
|
||||
if (refDrawTarget?.get() == null) {
|
||||
|
@ -200,7 +204,7 @@ class CustomEmojiCache(
|
|||
val item = getCached(now, request.url)
|
||||
if (item != null) {
|
||||
if (item.frames != null) {
|
||||
fireCallback(request)
|
||||
fireCallback(request, item.frames)
|
||||
}
|
||||
return@synchronized true
|
||||
}
|
||||
|
@ -231,7 +235,7 @@ class CustomEmojiCache(
|
|||
data = try {
|
||||
App1.getHttpCached(request.url)
|
||||
} catch (ex: Throwable) {
|
||||
log.w( "get failed. url=${request.url}")
|
||||
log.w("get failed. url=${request.url}")
|
||||
null
|
||||
}
|
||||
te = elapsedTime
|
||||
|
@ -267,7 +271,7 @@ class CustomEmojiCache(
|
|||
item.frames?.dispose()
|
||||
item.frames = frames
|
||||
}
|
||||
fireCallback(request)
|
||||
fireCallback(request, item.frames)
|
||||
}
|
||||
}
|
||||
te = elapsedTime
|
||||
|
@ -285,8 +289,15 @@ class CustomEmojiCache(
|
|||
}
|
||||
}
|
||||
|
||||
private fun fireCallback(request: Request) {
|
||||
handler.post { request.onLoadComplete() }
|
||||
private fun fireCallback(request: Request, frames: ApngFrames?) {
|
||||
handler.post { request.onLoadComplete(frames) }
|
||||
EmptyScope.launch {
|
||||
try {
|
||||
frames?.aspect?.let { daoImageAspect.save(request.url, it) }
|
||||
} catch (ex: Throwable) {
|
||||
log.e(ex, "aspect save failed.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sweepCache(now: Long) {
|
||||
|
@ -295,7 +306,7 @@ class CustomEmojiCache(
|
|||
val over = cache.size - CACHE_MAX
|
||||
|
||||
// 超過した数がある程度大きくなるまで掃除しない
|
||||
if (over <= 64) return
|
||||
if (over <= CACHE_MAX / 2) return
|
||||
|
||||
// 掃除する候補
|
||||
val list = ArrayList<CacheItem>()
|
||||
|
@ -319,7 +330,7 @@ class CustomEmojiCache(
|
|||
private fun decodeAPNG(data: ByteArray, url: String): ApngFrames? {
|
||||
val errors = ArrayList<Throwable>()
|
||||
|
||||
val maxSize = 256
|
||||
val maxSize = PrefS.spEmojiPixels.toInt().clip(16, 1024)
|
||||
|
||||
try {
|
||||
// APNGをデコード AWebPも
|
||||
|
|
|
@ -9,9 +9,10 @@ import jp.juggler.subwaytooter.api.entity.NicoProfileEmoji
|
|||
import jp.juggler.subwaytooter.api.entity.TootAttachmentLike
|
||||
import jp.juggler.subwaytooter.api.entity.TootMention
|
||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||
import jp.juggler.subwaytooter.util.EmojiSizeMode
|
||||
import jp.juggler.subwaytooter.pref.PrefS
|
||||
import jp.juggler.subwaytooter.table.HighlightWord
|
||||
import jp.juggler.util.data.WordTrieTree
|
||||
import jp.juggler.util.data.clip
|
||||
import org.jetbrains.anko.collections.forEachReversedByIndex
|
||||
|
||||
class DecodeOptions(
|
||||
|
@ -25,8 +26,8 @@ class DecodeOptions(
|
|||
var emojiMapProfile: Map<String, NicoProfileEmoji>? = null,
|
||||
var highlightTrie: WordTrieTree? = null,
|
||||
var unwrapEmojiImageTag: Boolean = false,
|
||||
var enlargeCustomEmoji: Float = 1f,
|
||||
var enlargeEmoji: Float = 1f,
|
||||
var enlargeCustomEmoji: Float = emojiScaleMastodon,
|
||||
var enlargeEmoji: Float = emojiScaleMastodon,
|
||||
// force use HTML instead of Misskey Markdown
|
||||
var forceHtml: Boolean = false,
|
||||
var mentionFullAcct: Boolean = false,
|
||||
|
@ -36,6 +37,18 @@ class DecodeOptions(
|
|||
var authorDomain: HostAndDomain? = null,
|
||||
var emojiSizeMode: EmojiSizeMode = EmojiSizeMode.Square,
|
||||
) {
|
||||
companion object {
|
||||
var emojiScaleMastodon = 1f
|
||||
var emojiScaleMisskey = 1f
|
||||
var emojiScaleReaction = 1f
|
||||
var emojiScaleUserName = 1f
|
||||
fun reloadEmojiScale() {
|
||||
emojiScaleMastodon = PrefS.spEmojiSizeMastodon.toFloat().div(100f).clip(0.5f, 5f)
|
||||
emojiScaleMisskey = PrefS.spEmojiSizeMisskey.toFloat().div(100f).clip(0.5f, 5f)
|
||||
emojiScaleReaction = PrefS.spEmojiSizeReaction.toFloat().div(100f).clip(0.5f, 5f)
|
||||
emojiScaleUserName = PrefS.spEmojiSizeUserName.toFloat().div(100f).clip(0.5f, 5f)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun isMediaAttachment(url: String?): Boolean =
|
||||
url?.let { u -> attachmentList?.any { it.hasUrl(u) } } ?: false
|
||||
|
|
|
@ -380,11 +380,11 @@ object EmojiDecoder {
|
|||
val userHost = cols.elementAtOrNull(1)
|
||||
?: options.authorDomain?.apiHost?.ascii
|
||||
?: apiHostAscii
|
||||
log.i(
|
||||
"decodeEmoji Misskey13 c0=${cols.elementAtOrNull(0)} c1=${
|
||||
cols.elementAtOrNull(1)
|
||||
} apiHostAscii=$apiHostAscii, userHost=$userHost"
|
||||
)
|
||||
// log.i(
|
||||
// "decodeEmoji Misskey13 c0=${cols.elementAtOrNull(0)} c1=${
|
||||
// cols.elementAtOrNull(1)
|
||||
// } apiHostAscii=$apiHostAscii, userHost=$userHost"
|
||||
// )
|
||||
|
||||
when {
|
||||
// 絵文字プロクシを利用できない
|
||||
|
|
|
@ -2,7 +2,9 @@ package jp.juggler.subwaytooter.util
|
|||
|
||||
import android.graphics.RectF
|
||||
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||
import jp.juggler.subwaytooter.pref.PrefI
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.daoImageAspect
|
||||
import jp.juggler.util.log.LogCategory
|
||||
import kotlin.math.min
|
||||
|
||||
|
@ -11,14 +13,19 @@ enum class EmojiSizeMode {
|
|||
Wide,
|
||||
}
|
||||
|
||||
fun SavedAccount?.emojiSizeMode(): EmojiSizeMode {
|
||||
val ti = this?.let { TootInstance.getCached(it) }
|
||||
return when {
|
||||
ti == null -> EmojiSizeMode.Square
|
||||
ti.isMisskey || !ti.fedibirdCapabilities.isNullOrEmpty() -> EmojiSizeMode.Wide
|
||||
else -> EmojiSizeMode.Square
|
||||
fun SavedAccount?.emojiSizeMode(): EmojiSizeMode =
|
||||
when (PrefI.ipEmojiWideMode.value) {
|
||||
PrefI.EMOJI_WIDE_ENABLE -> EmojiSizeMode.Wide
|
||||
PrefI.EMOJI_WIDE_DISABLE -> EmojiSizeMode.Square
|
||||
else /* PrefI.EMOJI_WIDE_AUTO */ -> {
|
||||
val ti = this?.let { TootInstance.getCached(it) }
|
||||
when {
|
||||
ti == null -> EmojiSizeMode.Square
|
||||
ti.isMisskey || !ti.fedibirdCapabilities.isNullOrEmpty() -> EmojiSizeMode.Wide
|
||||
else -> EmojiSizeMode.Square
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* カスタム絵文字のSpanやビューの描画領域やサイズを計算する
|
||||
|
@ -29,7 +36,7 @@ class EmojiImageRect(
|
|||
val scaleRatio: Float = 1f,
|
||||
val descentRatio: Float = 0f,
|
||||
val maxEmojiWidth: Float,
|
||||
val layout: (Int, Int) -> Unit,
|
||||
// val layout: (Int, Int) -> Unit,
|
||||
) {
|
||||
companion object {
|
||||
private val log = LogCategory("EmojiImageRect")
|
||||
|
@ -42,8 +49,6 @@ class EmojiImageRect(
|
|||
var emojiHeight = 0f
|
||||
var transY = 0f
|
||||
|
||||
var lastWidth: Float? = null
|
||||
|
||||
/**
|
||||
* lastAspect に基づいて rectDst と transY を更新する
|
||||
*/
|
||||
|
@ -69,9 +74,13 @@ class EmojiImageRect(
|
|||
) {
|
||||
this.emojiHeight = h
|
||||
val aspect = when (aspectArg) {
|
||||
null -> imageAspectCache[url] ?: 1f
|
||||
null -> {
|
||||
imageAspectCache[url]
|
||||
?: daoImageAspect.load(url)?.also { imageAspectCache[url] = it }
|
||||
?: 1f
|
||||
}
|
||||
else -> {
|
||||
imageAspectCache.put(url, aspectArg)
|
||||
if (url.isNotEmpty()) imageAspectCache[url] = aspectArg
|
||||
aspectArg
|
||||
}
|
||||
}.takeIf { it > 0f } ?: 1f
|
||||
|
@ -89,7 +98,6 @@ class EmojiImageRect(
|
|||
}
|
||||
|
||||
else -> {
|
||||
emojiWidth = emojiHeight
|
||||
// 絵文字のアスペクト比から描画範囲の幅と高さを決める
|
||||
val dstWidth: Float
|
||||
val dstHeight: Float
|
||||
|
@ -103,14 +111,8 @@ class EmojiImageRect(
|
|||
val dstX = (emojiHeight - dstWidth) / 2f
|
||||
val dstY = (emojiHeight - dstHeight) / 2f
|
||||
rectDst.set(dstX, dstY, dstX + dstWidth, dstY + dstHeight)
|
||||
emojiWidth = emojiHeight
|
||||
}
|
||||
}
|
||||
// 出力サイズが変化したならrequestLayout
|
||||
val newWidth = emojiWidth
|
||||
if (lastWidth != null && lastWidth != newWidth) {
|
||||
log.i("updateRect: width changed. $lastWidth → $newWidth")
|
||||
layout((emojiWidth + 0.5f).toInt(), (emojiHeight + 0.5f).toInt())
|
||||
}
|
||||
lastWidth = newWidth
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import jp.juggler.subwaytooter.App1
|
|||
import jp.juggler.subwaytooter.span.AnimatableSpan
|
||||
import jp.juggler.subwaytooter.span.AnimatableSpanInvalidator
|
||||
import jp.juggler.util.data.clip
|
||||
import jp.juggler.util.log.LogCategory
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class NetworkEmojiInvalidator(
|
||||
|
@ -15,6 +16,9 @@ class NetworkEmojiInvalidator(
|
|||
internal val view: TextView,
|
||||
// val parent:View? = null,
|
||||
) : AnimatableSpanInvalidator {
|
||||
companion object {
|
||||
private val log = LogCategory("NetworkEmojiInvalidator")
|
||||
}
|
||||
|
||||
var text: CharSequence
|
||||
get() = view.text
|
||||
|
|
|
@ -55,25 +55,23 @@ class NetworkEmojiView(
|
|||
scaleRatio = 1f,
|
||||
descentRatio = 0f,
|
||||
maxEmojiWidth = maxEmojiWidth,
|
||||
layout = { w, h ->
|
||||
val lp = layoutParams
|
||||
lp.width = w
|
||||
lp.height = h
|
||||
layoutParams = lp
|
||||
requestLayout()
|
||||
},
|
||||
// layout = { w, h ->
|
||||
// val lp = layoutParams
|
||||
// lp.width = w
|
||||
// lp.height = h
|
||||
// layoutParams = lp
|
||||
// requestLayout()
|
||||
// },
|
||||
)
|
||||
|
||||
fun setEmoji(
|
||||
url: String?,
|
||||
initialAspect: Float?,
|
||||
defaultWidth: Int,
|
||||
defaultHeight: Int,
|
||||
) {
|
||||
this.url = url
|
||||
mPaint.isFilterBitmap = true
|
||||
invalidate()
|
||||
emojiImageRect.lastWidth = null
|
||||
emojiImageRect.updateRect(
|
||||
url = url ?: "",
|
||||
aspectArg = initialAspect,
|
||||
|
@ -130,6 +128,13 @@ class NetworkEmojiView(
|
|||
aspectArg = srcAspect,
|
||||
h = dstHeight,
|
||||
)
|
||||
val width = (emojiImageRect.emojiWidth + 0.5f).toInt()
|
||||
if (width != layoutParams.width) {
|
||||
val lp = layoutParams
|
||||
lp.width = width
|
||||
lp.height = (emojiImageRect.emojiHeight + 0.5f).toInt()
|
||||
layoutParams = lp
|
||||
}
|
||||
|
||||
canvas.drawBitmap(b, rectSrc, emojiImageRect.rectDst, mPaint)
|
||||
|
||||
|
|
|
@ -1253,7 +1253,7 @@
|
|||
<string name="approved">承認されました。</string>
|
||||
<string name="account_list">アカウント一覧</string>
|
||||
<string name="show_username_on_filtered_post">フィルタされた投稿にユーザ名を表示する</string>
|
||||
<string name="log_save_level" >ログ収集レベル</string>
|
||||
<string name="log_save_level">ログ収集レベル</string>
|
||||
<string name="send_log_email">ログを電子メールで送信する</string>
|
||||
<string name="bug_report">バグ報告</string>
|
||||
<string name="bug_report_desc">アプリは過去数日間のログを保持していますが、勝手に外部に送信することはありません。ユーザが明示的にログを送信する操作した時だけ参照されます。</string>
|
||||
|
@ -1261,4 +1261,12 @@
|
|||
<string name="image_alpha_too_low">背景画像のアルファが低すぎます。 画像を見ることができますか?</string>
|
||||
<string name="enable_deprecated_something">陳腐化した何かを有効にする</string>
|
||||
<string name="misskey_support_end">Misskeyサポートは終了しました</string>
|
||||
<string name="emoji_size_mastodon">(Mastodon)カスタム絵文字の大きさ(単位:%、デフォルト:100)</string>
|
||||
<string name="emoji_size_misskey">(Misskey)カスタム絵文字の大きさ(単位:%、デフォルト:250)</string>
|
||||
<string name="emoji_size_reaction">リアクションの絵文字の大きさ(単位:%、デフォルト:150)</string>
|
||||
<string name="emoji_size_user_name">ユーザ名の絵文字の大きさ(単位:%、デフォルト:100)</string>
|
||||
<string name="emoji_wide_mode">ワイドカスタム絵文字</string>
|
||||
<string name="enabled">有効</string>
|
||||
<string name="disabled">無効</string>
|
||||
<string name="emoji_texture_pixels">絵文字テクスチャの最大ピクセル数(単位:ピクセル、デフォルト: 256。 タスクキルが必要。端末のRAMが少ない場合は64程度まで下げることをお勧めします)</string>
|
||||
</resources>
|
||||
|
|
|
@ -1269,4 +1269,12 @@
|
|||
<string name="image_alpha_too_low">background image alpha is too low. can you view the image?</string>
|
||||
<string name="enable_deprecated_something">Enable deprecated something</string>
|
||||
<string name="misskey_support_end">Misskey support has ended</string>
|
||||
<string name="emoji_size_mastodon">(Mastodon)Custom emoji scaling(Unit:%. default:100)</string>
|
||||
<string name="emoji_size_misskey">(Misskey)Custom emoji scaling(Unit:%。default:250)</string>
|
||||
<string name="emoji_size_reaction">Reaction emoji scaling(Unit:%。default:150)</string>
|
||||
<string name="emoji_size_user_name">User name emoji scaling(Unit:%。default:100)</string>
|
||||
<string name="emoji_wide_mode">Wide custom emoji</string>
|
||||
<string name="enabled">Enabled</string>
|
||||
<string name="disabled">Disabled</string>
|
||||
<string name="emoji_texture_pixels">Emoji texture max pixels(Unix:pixels, default: 256. task kill required. reduce to 64 if your device\'s RAM is not enough)</string>
|
||||
</resources>
|
||||
|
|
|
@ -119,7 +119,7 @@ for my $lang ( sort keys %langs ){
|
|||
my $sv = $value;
|
||||
$sv =~ s/(%\d+\$[\d\.]*[sdxf])//g;
|
||||
# Unit:%. や %% を除外したい
|
||||
$sv =~ s/%[\s.。%]//g;
|
||||
$sv =~ s/%[\s.,。、%]//g;
|
||||
if( $sv =~ /%/ ){
|
||||
$hasError =1;
|
||||
print "!! ($lang)$name : broken param: $sv // $value\n";
|
||||
|
|
Loading…
Reference in New Issue