アプリ設定に絵文字セクションと設定項目をいくつか追加。絵文字のアスペクト比を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() {
|
constructor(bitmap: Bitmap) : this() {
|
||||||
defaultImage = bitmap
|
defaultImage = bitmap
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import jp.juggler.subwaytooter.span.MyClickableSpan
|
||||||
import jp.juggler.subwaytooter.span.MyClickableSpanHandler
|
import jp.juggler.subwaytooter.span.MyClickableSpanHandler
|
||||||
import jp.juggler.subwaytooter.table.daoSavedAccount
|
import jp.juggler.subwaytooter.table.daoSavedAccount
|
||||||
import jp.juggler.subwaytooter.util.*
|
import jp.juggler.subwaytooter.util.*
|
||||||
|
import jp.juggler.subwaytooter.util.DecodeOptions.Companion.reloadEmojiScale
|
||||||
import jp.juggler.subwaytooter.view.MyDrawerLayout
|
import jp.juggler.subwaytooter.view.MyDrawerLayout
|
||||||
import jp.juggler.subwaytooter.view.MyEditText
|
import jp.juggler.subwaytooter.view.MyEditText
|
||||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||||
|
@ -370,6 +371,7 @@ class ActMain : AppCompatActivity(),
|
||||||
|
|
||||||
acctPadLr = (0.5f + 4f * density).toInt()
|
acctPadLr = (0.5f + 4f * density).toInt()
|
||||||
reloadTextSize()
|
reloadTextSize()
|
||||||
|
reloadEmojiScale()
|
||||||
|
|
||||||
initUI()
|
initUI()
|
||||||
|
|
||||||
|
@ -455,6 +457,7 @@ class ActMain : AppCompatActivity(),
|
||||||
super.onStart()
|
super.onStart()
|
||||||
galaxyBackgroundWorkaround()
|
galaxyBackgroundWorkaround()
|
||||||
benchmark("onStart total") {
|
benchmark("onStart total") {
|
||||||
|
reloadEmojiScale()
|
||||||
benchmark("reload color") { reloadColors() }
|
benchmark("reload color") { reloadColors() }
|
||||||
benchmark("reload timezone") { reloadTimeZone() }
|
benchmark("reload timezone") { reloadTimeZone() }
|
||||||
|
|
||||||
|
|
|
@ -120,8 +120,8 @@ fun ActMain.reactionAdd(
|
||||||
activity,
|
activity,
|
||||||
accessInfo,
|
accessInfo,
|
||||||
decodeEmoji = true,
|
decodeEmoji = true,
|
||||||
enlargeEmoji = 1.5f,
|
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
||||||
enlargeCustomEmoji = 1.5f,
|
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
||||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||||
)
|
)
|
||||||
val emojiSpan = TootReaction.toSpannableStringBuilder(options, code, urlArg)
|
val emojiSpan = TootReaction.toSpannableStringBuilder(options, code, urlArg)
|
||||||
|
@ -219,8 +219,8 @@ fun ActMain.reactionRemove(
|
||||||
activity,
|
activity,
|
||||||
accessInfo,
|
accessInfo,
|
||||||
decodeEmoji = true,
|
decodeEmoji = true,
|
||||||
enlargeEmoji = 1.5f,
|
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
||||||
enlargeCustomEmoji = 1.5f,
|
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
||||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||||
)
|
)
|
||||||
val emojiSpan = reaction.toSpannableStringBuilder(options, status)
|
val emojiSpan = reaction.toSpannableStringBuilder(options, status)
|
||||||
|
@ -330,8 +330,8 @@ private fun ActMain.reactionWithoutUi(
|
||||||
activity,
|
activity,
|
||||||
accessInfo,
|
accessInfo,
|
||||||
decodeEmoji = true,
|
decodeEmoji = true,
|
||||||
enlargeEmoji = 1.5f,
|
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
||||||
enlargeCustomEmoji = 1.5f,
|
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
||||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||||
)
|
)
|
||||||
val emojiSpan = TootReaction.toSpannableStringBuilder(options, reactionCode, reactionImage)
|
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.SavedAccount
|
||||||
import jp.juggler.subwaytooter.table.daoUserRelation
|
import jp.juggler.subwaytooter.table.daoUserRelation
|
||||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
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.LinkHelper
|
||||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||||
import jp.juggler.subwaytooter.util.matchHost
|
import jp.juggler.subwaytooter.util.matchHost
|
||||||
|
@ -168,6 +169,8 @@ open class TootAccount(
|
||||||
emojiMapProfile = profile_emojis,
|
emojiMapProfile = profile_emojis,
|
||||||
emojiMapCustom = custom_emojis,
|
emojiMapCustom = custom_emojis,
|
||||||
authorDomain = this,
|
authorDomain = this,
|
||||||
|
enlargeCustomEmoji = emojiScaleUserName,
|
||||||
|
enlargeEmoji = emojiScaleUserName,
|
||||||
).decodeEmoji(sv)
|
).decodeEmoji(sv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ import android.text.Spannable
|
||||||
import jp.juggler.subwaytooter.api.TootAccountMap
|
import jp.juggler.subwaytooter.api.TootAccountMap
|
||||||
import jp.juggler.subwaytooter.api.TootParser
|
import jp.juggler.subwaytooter.api.TootParser
|
||||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
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(
|
class TootAccountRef private constructor(
|
||||||
val mapId: Int,
|
val mapId: Int,
|
||||||
|
@ -50,6 +52,8 @@ class TootAccountRef private constructor(
|
||||||
unwrapEmojiImageTag = true,
|
unwrapEmojiImageTag = true,
|
||||||
authorDomain = account,
|
authorDomain = account,
|
||||||
emojiSizeMode = parser.emojiSizeMode,
|
emojiSizeMode = parser.emojiSizeMode,
|
||||||
|
enlargeEmoji = if(parser.linkHelper.isMisskey) emojiScaleMisskey else emojiScaleMastodon,
|
||||||
|
enlargeCustomEmoji = if(parser.linkHelper.isMisskey) emojiScaleMisskey else emojiScaleMastodon,
|
||||||
).decodeHTML(account.note),
|
).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.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)
|
sw(PrefB.bpShowTranslateButton, R.string.show_translate_button)
|
||||||
|
|
||||||
item(
|
item(
|
||||||
|
@ -464,10 +463,6 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
||||||
R.string.warn_hashtag_ascii_and_non_ascii
|
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)
|
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)
|
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) {
|
section(R.string.appearance) {
|
||||||
sw(PrefB.bpSimpleList, R.string.simple_list)
|
sw(PrefB.bpSimpleList, R.string.simple_list)
|
||||||
sw(PrefB.bpShowFollowButtonInButtonBar, R.string.show_follow_button_in_button_bar)
|
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.bpShowBookmarkButton, R.string.show_bookmark_button)
|
||||||
sw(PrefB.bpHideFollowCount, R.string.hide_followers_count)
|
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)
|
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.bpEnableDeprecatedSomething,R.string.enable_deprecated_something)
|
||||||
|
|
||||||
sw(PrefB.bpEmojiPickerCategoryOther, R.string.show_emoji_picker_other_category)
|
|
||||||
action(R.string.drawable_list) {
|
action(R.string.drawable_list) {
|
||||||
action = { startActivity(Intent(this, ActDrawableList::class.java)) }
|
action = { startActivity(Intent(this, ActDrawableList::class.java)) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,8 @@ fun ColumnViewHolder.updateReactionQueryView() {
|
||||||
act,
|
act,
|
||||||
column.accessInfo,
|
column.accessInfo,
|
||||||
decodeEmoji = true,
|
decodeEmoji = true,
|
||||||
enlargeEmoji = 1.5f,
|
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
||||||
enlargeCustomEmoji = 1.5f,
|
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
||||||
emojiSizeMode = column.accessInfo.emojiSizeMode(),
|
emojiSizeMode = column.accessInfo.emojiSizeMode(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -624,6 +624,8 @@ internal class ViewHolderHeaderProfile(
|
||||||
emojiMapProfile = who.profile_emojis,
|
emojiMapProfile = who.profile_emojis,
|
||||||
authorDomain = who,
|
authorDomain = who,
|
||||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||||
|
enlargeCustomEmoji = DecodeOptions.emojiScaleUserName,
|
||||||
|
enlargeEmoji = DecodeOptions.emojiScaleUserName,
|
||||||
)
|
)
|
||||||
|
|
||||||
val nameTypeface = ActMain.timelineFontBold
|
val nameTypeface = ActMain.timelineFontBold
|
||||||
|
|
|
@ -290,7 +290,6 @@ private class EmojiPicker(
|
||||||
item.customEmoji.url
|
item.customEmoji.url
|
||||||
},
|
},
|
||||||
initialAspect = item.customEmoji.aspect,
|
initialAspect = item.customEmoji.aspect,
|
||||||
defaultWidth = gridSize,
|
|
||||||
defaultHeight = gridSize,
|
defaultHeight = gridSize,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.google.android.flexbox.FlexWrap
|
||||||
import com.google.android.flexbox.FlexboxLayout
|
import com.google.android.flexbox.FlexboxLayout
|
||||||
import com.google.android.flexbox.JustifyContent
|
import com.google.android.flexbox.JustifyContent
|
||||||
import jp.juggler.subwaytooter.ActMain
|
import jp.juggler.subwaytooter.ActMain
|
||||||
|
import jp.juggler.subwaytooter.ActMain.Companion.boostButtonSize
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.action.reactionAdd
|
import jp.juggler.subwaytooter.action.reactionAdd
|
||||||
import jp.juggler.subwaytooter.action.reactionFromAnotherAccount
|
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.api.entity.TootStatus
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.subwaytooter.pref.PrefI
|
import jp.juggler.subwaytooter.pref.PrefI
|
||||||
|
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
|
||||||
import jp.juggler.subwaytooter.util.emojiSizeMode
|
import jp.juggler.subwaytooter.util.emojiSizeMode
|
||||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||||
|
@ -29,6 +31,7 @@ import jp.juggler.util.ui.attrColor
|
||||||
import jp.juggler.util.ui.getAdaptiveRippleDrawableRound
|
import jp.juggler.util.ui.getAdaptiveRippleDrawableRound
|
||||||
import org.jetbrains.anko.allCaps
|
import org.jetbrains.anko.allCaps
|
||||||
import org.jetbrains.anko.dip
|
import org.jetbrains.anko.dip
|
||||||
|
import org.jetbrains.anko.wrapContent
|
||||||
|
|
||||||
private val log = LogCategory("ItemViewHolderReaction")
|
private val log = LogCategory("ItemViewHolderReaction")
|
||||||
|
|
||||||
|
@ -42,11 +45,10 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
|
||||||
|
|
||||||
fun Float.round() = (this + 0.5f).toInt()
|
fun Float.round() = (this + 0.5f).toInt()
|
||||||
|
|
||||||
val imageScale = 1.5f
|
val imageScale = DecodeOptions.emojiScaleReaction
|
||||||
val buttonHeight = ActMain.boostButtonSize // px
|
val textHeight = (boostButtonSize.toFloat()/2)
|
||||||
val marginBetween = (buttonHeight * 0.05f).round()
|
val marginBetween = (boostButtonSize * 0.05f).round()
|
||||||
val paddingH = (buttonHeight * 0.1f).round()
|
val paddingH = (boostButtonSize * 0.1f).round()
|
||||||
val textHeight = (buttonHeight * 0.7f) / imageScale
|
|
||||||
|
|
||||||
val act = this@makeReactionsView.activity // not Button(View).getActivity()
|
val act = this@makeReactionsView.activity // not Button(View).getActivity()
|
||||||
|
|
||||||
|
@ -63,7 +65,7 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
|
||||||
|
|
||||||
if (reactionSet?.isEmpty() != false) {
|
if (reactionSet?.isEmpty() != false) {
|
||||||
val v = View(act).apply {
|
val v = View(act).apply {
|
||||||
layoutParams = FlexboxLayout.LayoutParams(0, buttonHeight)
|
layoutParams = FlexboxLayout.LayoutParams(0, wrapContent)
|
||||||
setPadding(paddingH, 0, paddingH, 0)
|
setPadding(paddingH, 0, paddingH, 0)
|
||||||
}
|
}
|
||||||
box.addView(v)
|
box.addView(v)
|
||||||
|
@ -85,13 +87,13 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
|
||||||
val b = AppCompatButton(act).apply {
|
val b = AppCompatButton(act).apply {
|
||||||
layoutParams = FlexboxLayout.LayoutParams(
|
layoutParams = FlexboxLayout.LayoutParams(
|
||||||
FlexboxLayout.LayoutParams.WRAP_CONTENT,
|
FlexboxLayout.LayoutParams.WRAP_CONTENT,
|
||||||
buttonHeight,
|
wrapContent,
|
||||||
).apply {
|
).apply {
|
||||||
if (index > 0) startMargin = marginBetween
|
if (index > 0) startMargin = marginBetween
|
||||||
bottomMargin = dip(3)
|
bottomMargin = dip(3)
|
||||||
}
|
}
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
minWidthCompat = buttonHeight
|
minWidthCompat = textHeight.round()
|
||||||
|
|
||||||
background = if (reactionSet.isMyReaction(reaction)) {
|
background = if (reactionSet.isMyReaction(reaction)) {
|
||||||
// 自分がリアクションしたやつは背景を変える
|
// 自分がリアクションしたやつは背景を変える
|
||||||
|
|
|
@ -354,8 +354,8 @@ fun ItemViewHolder.showBoost(
|
||||||
activity,
|
activity,
|
||||||
accessInfo,
|
accessInfo,
|
||||||
decodeEmoji = true,
|
decodeEmoji = true,
|
||||||
enlargeEmoji = 1.5f,
|
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
||||||
enlargeCustomEmoji = 1.5f,
|
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
||||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||||
)
|
)
|
||||||
val ssb = reaction.toSpannableStringBuilder(options, boostStatus)
|
val ssb = reaction.toSpannableStringBuilder(options, boostStatus)
|
||||||
|
|
|
@ -61,7 +61,7 @@ object MisskeyMarkdownDecoder {
|
||||||
fun decodeMarkdown(options: DecodeOptions, src: String?) =
|
fun decodeMarkdown(options: DecodeOptions, src: String?) =
|
||||||
SpannableStringBuilderEx().apply {
|
SpannableStringBuilderEx().apply {
|
||||||
val save = options.enlargeCustomEmoji
|
val save = options.enlargeCustomEmoji
|
||||||
options.enlargeCustomEmoji = 2.5f
|
options.enlargeCustomEmoji = DecodeOptions.emojiScaleMisskey
|
||||||
try {
|
try {
|
||||||
val env = SpanOutputEnv(options, this)
|
val env = SpanOutputEnv(options, this)
|
||||||
|
|
||||||
|
|
|
@ -124,4 +124,9 @@ object PrefI {
|
||||||
val ipMediaBackground = IntPref("MediaBackground", 1)
|
val ipMediaBackground = IntPref("MediaBackground", 1)
|
||||||
|
|
||||||
val ipLogSaveLevel = IntPref("LogSaveLevel", Log.WARN)
|
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 spTimelineSpacing = StringPref("TimelineSpacing", "")
|
||||||
|
|
||||||
val spEventTextAlpha = StringPref("EventTextAlpha", "")
|
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)
|
defVal != pref.getString(key, defVal)
|
||||||
|
|
||||||
fun toInt() = value.toIntOrNull() ?: defVal.toInt()
|
fun toInt() = value.toIntOrNull() ?: defVal.toInt()
|
||||||
|
fun toFloat() = value.toFloatOrNull() ?: defVal.toFloat()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,13 @@ class NetworkEmojiSpan constructor(
|
||||||
private val url: String,
|
private val url: String,
|
||||||
sizeMode: EmojiSizeMode,
|
sizeMode: EmojiSizeMode,
|
||||||
scale: Float = 1f,
|
scale: Float = 1f,
|
||||||
private val initialAspect:Float? = null,
|
private val initialAspect: Float? = null,
|
||||||
private val errorDrawableId: Int = R.drawable.outline_broken_image_24,
|
private val errorDrawableId: Int = R.drawable.outline_broken_image_24,
|
||||||
) : ReplacementSpan(), AnimatableSpan {
|
) : ReplacementSpan(), AnimatableSpan {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
internal val log = LogCategory("NetworkEmojiSpan")
|
internal val log = LogCategory("NetworkEmojiSpan")
|
||||||
private const val scaleRatio = 1.14f
|
const val scaleRatio = 1.14f
|
||||||
private const val descentRatio = 0.211f
|
private const val descentRatio = 0.211f
|
||||||
|
|
||||||
// 最大幅
|
// 最大幅
|
||||||
|
@ -47,13 +47,23 @@ class NetworkEmojiSpan constructor(
|
||||||
|
|
||||||
private val rectSrc = Rect()
|
private val rectSrc = Rect()
|
||||||
|
|
||||||
|
private var lastMeasuredWidth = 0f
|
||||||
|
|
||||||
private val emojiImageRect = EmojiImageRect(
|
private val emojiImageRect = EmojiImageRect(
|
||||||
sizeMode = sizeMode,
|
sizeMode = sizeMode,
|
||||||
scale = scale,
|
scale = scale,
|
||||||
scaleRatio = scaleRatio,
|
scaleRatio = scaleRatio,
|
||||||
descentRatio = descentRatio,
|
descentRatio = descentRatio,
|
||||||
maxEmojiWidth = maxEmojiWidth,
|
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(
|
override fun setInvalidateCallback(
|
||||||
|
@ -72,8 +82,12 @@ class NetworkEmojiSpan constructor(
|
||||||
fm: Paint.FontMetricsInt?,
|
fm: Paint.FontMetricsInt?,
|
||||||
): Int {
|
): Int {
|
||||||
emojiImageRect.updateRect(
|
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()
|
val height = (emojiImageRect.emojiHeight + 0.5f).toInt()
|
||||||
if (fm != null) {
|
if (fm != null) {
|
||||||
val cDescent = (0.5f + height * descentRatio).toInt()
|
val cDescent = (0.5f + height * descentRatio).toInt()
|
||||||
|
@ -83,7 +97,10 @@ class NetworkEmojiSpan constructor(
|
||||||
if (fm.descent < cDescent) fm.descent = cDescent
|
if (fm.descent < cDescent) fm.descent = cDescent
|
||||||
if (fm.bottom < cDescent) fm.bottom = 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(
|
override fun draw(
|
||||||
|
@ -115,6 +132,7 @@ class NetworkEmojiSpan constructor(
|
||||||
|
|
||||||
// APNGデータの取得
|
// APNGデータの取得
|
||||||
val frames = App1.custom_emoji_cache.getFrames(refDrawTarget, url) {
|
val frames = App1.custom_emoji_cache.getFrames(refDrawTarget, url) {
|
||||||
|
handleFrameLoaded(it)
|
||||||
invalidateCallback.delayInvalidate(0L)
|
invalidateCallback.delayInvalidate(0L)
|
||||||
} ?: return false
|
} ?: return false
|
||||||
|
|
||||||
|
@ -144,6 +162,17 @@ class NetworkEmojiSpan constructor(
|
||||||
textPaint.textSize,
|
textPaint.textSize,
|
||||||
baseline.toFloat()
|
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()
|
canvas.save()
|
||||||
try {
|
try {
|
||||||
|
@ -172,6 +201,12 @@ class NetworkEmojiSpan constructor(
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleFrameLoaded(frames: ApngFrames?) {
|
||||||
|
frames?.aspect?.let {
|
||||||
|
invalidateCallback?.requestLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun drawError(
|
private fun drawError(
|
||||||
canvas: Canvas,
|
canvas: Canvas,
|
||||||
x: Float,
|
x: Float,
|
||||||
|
@ -190,7 +225,7 @@ class NetworkEmojiSpan constructor(
|
||||||
url = "",
|
url = "",
|
||||||
aspectArg = srcWidth / srcHeight,
|
aspectArg = srcWidth / srcHeight,
|
||||||
textSize = textPaint.textSize,
|
textSize = textPaint.textSize,
|
||||||
baseline = baseline.toFloat()
|
baseline = baseline.toFloat(),
|
||||||
)
|
)
|
||||||
|
|
||||||
canvas.save()
|
canvas.save()
|
||||||
|
|
|
@ -70,7 +70,9 @@ import java.util.concurrent.atomic.AtomicReference
|
||||||
// 2022/3/15 63=>64 SavedAccountテーブルに項目追加
|
// 2022/3/15 63=>64 SavedAccountテーブルに項目追加
|
||||||
// 2023/2/2 64 => 65 PushMessage, AccountNotificationStatus,NotificationShown テーブルの追加。
|
// 2023/2/2 64 => 65 PushMessage, AccountNotificationStatus,NotificationShown テーブルの追加。
|
||||||
// 2023/2/11 65=>66 LogDataがなければ作る
|
// 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"
|
const val DB_NAME = "app_db"
|
||||||
|
|
||||||
// テーブルのリスト
|
// テーブルのリスト
|
||||||
|
@ -96,6 +98,7 @@ val TABLE_LIST = arrayOf(
|
||||||
PushMessage.Companion, // v65
|
PushMessage.Companion, // v65
|
||||||
AccountNotificationStatus.Companion, // v65,
|
AccountNotificationStatus.Companion, // v65,
|
||||||
NotificationShown.Companion, // v65
|
NotificationShown.Companion, // v65
|
||||||
|
ImageAspect.Companion, // v67
|
||||||
)
|
)
|
||||||
|
|
||||||
private val log = LogCategory("AppDatabaseHolder")
|
private val log = LogCategory("AppDatabaseHolder")
|
||||||
|
@ -201,3 +204,6 @@ val daoTagHistory get() = TagHistory.Access(appDatabase)
|
||||||
val daoUserRelation get() = UserRelation.Access(appDatabase)
|
val daoUserRelation get() = UserRelation.Access(appDatabase)
|
||||||
val daoPushMessage get() = PushMessage.Access(appDatabase)
|
val daoPushMessage get() = PushMessage.Access(appDatabase)
|
||||||
val daoLogData get() = LogData.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 com.caverock.androidsvg.SVG
|
||||||
import jp.juggler.apng.ApngFrames
|
import jp.juggler.apng.ApngFrames
|
||||||
import jp.juggler.subwaytooter.App1
|
import jp.juggler.subwaytooter.App1
|
||||||
|
import jp.juggler.subwaytooter.pref.PrefS
|
||||||
import jp.juggler.subwaytooter.table.EmojiCacheDbOpenHelper
|
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.data.*
|
||||||
import jp.juggler.util.log.*
|
import jp.juggler.util.log.*
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -45,7 +49,7 @@ class CustomEmojiCache(
|
||||||
private class Request(
|
private class Request(
|
||||||
val refTarget: WeakReference<Any>,
|
val refTarget: WeakReference<Any>,
|
||||||
val url: String,
|
val url: String,
|
||||||
val onLoadComplete: () -> Unit,
|
val onLoadComplete: (ApngFrames?) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
// APNGデコード済のキャッシュデータ
|
// APNGデコード済のキャッシュデータ
|
||||||
|
@ -115,7 +119,7 @@ class CustomEmojiCache(
|
||||||
fun getFrames(
|
fun getFrames(
|
||||||
refDrawTarget: WeakReference<Any>?,
|
refDrawTarget: WeakReference<Any>?,
|
||||||
url: String,
|
url: String,
|
||||||
onLoadComplete: () -> Unit,
|
onLoadComplete: (ApngFrames?) -> Unit,
|
||||||
): ApngFrames? {
|
): ApngFrames? {
|
||||||
try {
|
try {
|
||||||
if (refDrawTarget?.get() == null) {
|
if (refDrawTarget?.get() == null) {
|
||||||
|
@ -200,7 +204,7 @@ class CustomEmojiCache(
|
||||||
val item = getCached(now, request.url)
|
val item = getCached(now, request.url)
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
if (item.frames != null) {
|
if (item.frames != null) {
|
||||||
fireCallback(request)
|
fireCallback(request, item.frames)
|
||||||
}
|
}
|
||||||
return@synchronized true
|
return@synchronized true
|
||||||
}
|
}
|
||||||
|
@ -231,7 +235,7 @@ class CustomEmojiCache(
|
||||||
data = try {
|
data = try {
|
||||||
App1.getHttpCached(request.url)
|
App1.getHttpCached(request.url)
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
log.w( "get failed. url=${request.url}")
|
log.w("get failed. url=${request.url}")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
te = elapsedTime
|
te = elapsedTime
|
||||||
|
@ -267,7 +271,7 @@ class CustomEmojiCache(
|
||||||
item.frames?.dispose()
|
item.frames?.dispose()
|
||||||
item.frames = frames
|
item.frames = frames
|
||||||
}
|
}
|
||||||
fireCallback(request)
|
fireCallback(request, item.frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
te = elapsedTime
|
te = elapsedTime
|
||||||
|
@ -285,8 +289,15 @@ class CustomEmojiCache(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fireCallback(request: Request) {
|
private fun fireCallback(request: Request, frames: ApngFrames?) {
|
||||||
handler.post { request.onLoadComplete() }
|
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) {
|
private fun sweepCache(now: Long) {
|
||||||
|
@ -295,7 +306,7 @@ class CustomEmojiCache(
|
||||||
val over = cache.size - CACHE_MAX
|
val over = cache.size - CACHE_MAX
|
||||||
|
|
||||||
// 超過した数がある程度大きくなるまで掃除しない
|
// 超過した数がある程度大きくなるまで掃除しない
|
||||||
if (over <= 64) return
|
if (over <= CACHE_MAX / 2) return
|
||||||
|
|
||||||
// 掃除する候補
|
// 掃除する候補
|
||||||
val list = ArrayList<CacheItem>()
|
val list = ArrayList<CacheItem>()
|
||||||
|
@ -319,7 +330,7 @@ class CustomEmojiCache(
|
||||||
private fun decodeAPNG(data: ByteArray, url: String): ApngFrames? {
|
private fun decodeAPNG(data: ByteArray, url: String): ApngFrames? {
|
||||||
val errors = ArrayList<Throwable>()
|
val errors = ArrayList<Throwable>()
|
||||||
|
|
||||||
val maxSize = 256
|
val maxSize = PrefS.spEmojiPixels.toInt().clip(16, 1024)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// APNGをデコード AWebPも
|
// 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.TootAttachmentLike
|
||||||
import jp.juggler.subwaytooter.api.entity.TootMention
|
import jp.juggler.subwaytooter.api.entity.TootMention
|
||||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
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.subwaytooter.table.HighlightWord
|
||||||
import jp.juggler.util.data.WordTrieTree
|
import jp.juggler.util.data.WordTrieTree
|
||||||
|
import jp.juggler.util.data.clip
|
||||||
import org.jetbrains.anko.collections.forEachReversedByIndex
|
import org.jetbrains.anko.collections.forEachReversedByIndex
|
||||||
|
|
||||||
class DecodeOptions(
|
class DecodeOptions(
|
||||||
|
@ -25,8 +26,8 @@ class DecodeOptions(
|
||||||
var emojiMapProfile: Map<String, NicoProfileEmoji>? = null,
|
var emojiMapProfile: Map<String, NicoProfileEmoji>? = null,
|
||||||
var highlightTrie: WordTrieTree? = null,
|
var highlightTrie: WordTrieTree? = null,
|
||||||
var unwrapEmojiImageTag: Boolean = false,
|
var unwrapEmojiImageTag: Boolean = false,
|
||||||
var enlargeCustomEmoji: Float = 1f,
|
var enlargeCustomEmoji: Float = emojiScaleMastodon,
|
||||||
var enlargeEmoji: Float = 1f,
|
var enlargeEmoji: Float = emojiScaleMastodon,
|
||||||
// force use HTML instead of Misskey Markdown
|
// force use HTML instead of Misskey Markdown
|
||||||
var forceHtml: Boolean = false,
|
var forceHtml: Boolean = false,
|
||||||
var mentionFullAcct: Boolean = false,
|
var mentionFullAcct: Boolean = false,
|
||||||
|
@ -36,6 +37,18 @@ class DecodeOptions(
|
||||||
var authorDomain: HostAndDomain? = null,
|
var authorDomain: HostAndDomain? = null,
|
||||||
var emojiSizeMode: EmojiSizeMode = EmojiSizeMode.Square,
|
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 =
|
internal fun isMediaAttachment(url: String?): Boolean =
|
||||||
url?.let { u -> attachmentList?.any { it.hasUrl(u) } } ?: false
|
url?.let { u -> attachmentList?.any { it.hasUrl(u) } } ?: false
|
||||||
|
|
|
@ -380,11 +380,11 @@ object EmojiDecoder {
|
||||||
val userHost = cols.elementAtOrNull(1)
|
val userHost = cols.elementAtOrNull(1)
|
||||||
?: options.authorDomain?.apiHost?.ascii
|
?: options.authorDomain?.apiHost?.ascii
|
||||||
?: apiHostAscii
|
?: apiHostAscii
|
||||||
log.i(
|
// log.i(
|
||||||
"decodeEmoji Misskey13 c0=${cols.elementAtOrNull(0)} c1=${
|
// "decodeEmoji Misskey13 c0=${cols.elementAtOrNull(0)} c1=${
|
||||||
cols.elementAtOrNull(1)
|
// cols.elementAtOrNull(1)
|
||||||
} apiHostAscii=$apiHostAscii, userHost=$userHost"
|
// } apiHostAscii=$apiHostAscii, userHost=$userHost"
|
||||||
)
|
// )
|
||||||
|
|
||||||
when {
|
when {
|
||||||
// 絵文字プロクシを利用できない
|
// 絵文字プロクシを利用できない
|
||||||
|
|
|
@ -2,7 +2,9 @@ package jp.juggler.subwaytooter.util
|
||||||
|
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
import jp.juggler.subwaytooter.api.entity.TootInstance
|
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||||
|
import jp.juggler.subwaytooter.pref.PrefI
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
|
import jp.juggler.subwaytooter.table.daoImageAspect
|
||||||
import jp.juggler.util.log.LogCategory
|
import jp.juggler.util.log.LogCategory
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
@ -11,14 +13,19 @@ enum class EmojiSizeMode {
|
||||||
Wide,
|
Wide,
|
||||||
}
|
}
|
||||||
|
|
||||||
fun SavedAccount?.emojiSizeMode(): EmojiSizeMode {
|
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) }
|
val ti = this?.let { TootInstance.getCached(it) }
|
||||||
return when {
|
when {
|
||||||
ti == null -> EmojiSizeMode.Square
|
ti == null -> EmojiSizeMode.Square
|
||||||
ti.isMisskey || !ti.fedibirdCapabilities.isNullOrEmpty() -> EmojiSizeMode.Wide
|
ti.isMisskey || !ti.fedibirdCapabilities.isNullOrEmpty() -> EmojiSizeMode.Wide
|
||||||
else -> EmojiSizeMode.Square
|
else -> EmojiSizeMode.Square
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* カスタム絵文字のSpanやビューの描画領域やサイズを計算する
|
* カスタム絵文字のSpanやビューの描画領域やサイズを計算する
|
||||||
|
@ -29,7 +36,7 @@ class EmojiImageRect(
|
||||||
val scaleRatio: Float = 1f,
|
val scaleRatio: Float = 1f,
|
||||||
val descentRatio: Float = 0f,
|
val descentRatio: Float = 0f,
|
||||||
val maxEmojiWidth: Float,
|
val maxEmojiWidth: Float,
|
||||||
val layout: (Int, Int) -> Unit,
|
// val layout: (Int, Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LogCategory("EmojiImageRect")
|
private val log = LogCategory("EmojiImageRect")
|
||||||
|
@ -42,8 +49,6 @@ class EmojiImageRect(
|
||||||
var emojiHeight = 0f
|
var emojiHeight = 0f
|
||||||
var transY = 0f
|
var transY = 0f
|
||||||
|
|
||||||
var lastWidth: Float? = null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* lastAspect に基づいて rectDst と transY を更新する
|
* lastAspect に基づいて rectDst と transY を更新する
|
||||||
*/
|
*/
|
||||||
|
@ -69,9 +74,13 @@ class EmojiImageRect(
|
||||||
) {
|
) {
|
||||||
this.emojiHeight = h
|
this.emojiHeight = h
|
||||||
val aspect = when (aspectArg) {
|
val aspect = when (aspectArg) {
|
||||||
null -> imageAspectCache[url] ?: 1f
|
null -> {
|
||||||
|
imageAspectCache[url]
|
||||||
|
?: daoImageAspect.load(url)?.also { imageAspectCache[url] = it }
|
||||||
|
?: 1f
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
imageAspectCache.put(url, aspectArg)
|
if (url.isNotEmpty()) imageAspectCache[url] = aspectArg
|
||||||
aspectArg
|
aspectArg
|
||||||
}
|
}
|
||||||
}.takeIf { it > 0f } ?: 1f
|
}.takeIf { it > 0f } ?: 1f
|
||||||
|
@ -89,7 +98,6 @@ class EmojiImageRect(
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
emojiWidth = emojiHeight
|
|
||||||
// 絵文字のアスペクト比から描画範囲の幅と高さを決める
|
// 絵文字のアスペクト比から描画範囲の幅と高さを決める
|
||||||
val dstWidth: Float
|
val dstWidth: Float
|
||||||
val dstHeight: Float
|
val dstHeight: Float
|
||||||
|
@ -103,14 +111,8 @@ class EmojiImageRect(
|
||||||
val dstX = (emojiHeight - dstWidth) / 2f
|
val dstX = (emojiHeight - dstWidth) / 2f
|
||||||
val dstY = (emojiHeight - dstHeight) / 2f
|
val dstY = (emojiHeight - dstHeight) / 2f
|
||||||
rectDst.set(dstX, dstY, dstX + dstWidth, dstY + dstHeight)
|
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.AnimatableSpan
|
||||||
import jp.juggler.subwaytooter.span.AnimatableSpanInvalidator
|
import jp.juggler.subwaytooter.span.AnimatableSpanInvalidator
|
||||||
import jp.juggler.util.data.clip
|
import jp.juggler.util.data.clip
|
||||||
|
import jp.juggler.util.log.LogCategory
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
class NetworkEmojiInvalidator(
|
class NetworkEmojiInvalidator(
|
||||||
|
@ -15,6 +16,9 @@ class NetworkEmojiInvalidator(
|
||||||
internal val view: TextView,
|
internal val view: TextView,
|
||||||
// val parent:View? = null,
|
// val parent:View? = null,
|
||||||
) : AnimatableSpanInvalidator {
|
) : AnimatableSpanInvalidator {
|
||||||
|
companion object {
|
||||||
|
private val log = LogCategory("NetworkEmojiInvalidator")
|
||||||
|
}
|
||||||
|
|
||||||
var text: CharSequence
|
var text: CharSequence
|
||||||
get() = view.text
|
get() = view.text
|
||||||
|
|
|
@ -55,25 +55,23 @@ class NetworkEmojiView(
|
||||||
scaleRatio = 1f,
|
scaleRatio = 1f,
|
||||||
descentRatio = 0f,
|
descentRatio = 0f,
|
||||||
maxEmojiWidth = maxEmojiWidth,
|
maxEmojiWidth = maxEmojiWidth,
|
||||||
layout = { w, h ->
|
// layout = { w, h ->
|
||||||
val lp = layoutParams
|
// val lp = layoutParams
|
||||||
lp.width = w
|
// lp.width = w
|
||||||
lp.height = h
|
// lp.height = h
|
||||||
layoutParams = lp
|
// layoutParams = lp
|
||||||
requestLayout()
|
// requestLayout()
|
||||||
},
|
// },
|
||||||
)
|
)
|
||||||
|
|
||||||
fun setEmoji(
|
fun setEmoji(
|
||||||
url: String?,
|
url: String?,
|
||||||
initialAspect: Float?,
|
initialAspect: Float?,
|
||||||
defaultWidth: Int,
|
|
||||||
defaultHeight: Int,
|
defaultHeight: Int,
|
||||||
) {
|
) {
|
||||||
this.url = url
|
this.url = url
|
||||||
mPaint.isFilterBitmap = true
|
mPaint.isFilterBitmap = true
|
||||||
invalidate()
|
invalidate()
|
||||||
emojiImageRect.lastWidth = null
|
|
||||||
emojiImageRect.updateRect(
|
emojiImageRect.updateRect(
|
||||||
url = url ?: "",
|
url = url ?: "",
|
||||||
aspectArg = initialAspect,
|
aspectArg = initialAspect,
|
||||||
|
@ -130,6 +128,13 @@ class NetworkEmojiView(
|
||||||
aspectArg = srcAspect,
|
aspectArg = srcAspect,
|
||||||
h = dstHeight,
|
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)
|
canvas.drawBitmap(b, rectSrc, emojiImageRect.rectDst, mPaint)
|
||||||
|
|
||||||
|
|
|
@ -1253,7 +1253,7 @@
|
||||||
<string name="approved">承認されました。</string>
|
<string name="approved">承認されました。</string>
|
||||||
<string name="account_list">アカウント一覧</string>
|
<string name="account_list">アカウント一覧</string>
|
||||||
<string name="show_username_on_filtered_post">フィルタされた投稿にユーザ名を表示する</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="send_log_email">ログを電子メールで送信する</string>
|
||||||
<string name="bug_report">バグ報告</string>
|
<string name="bug_report">バグ報告</string>
|
||||||
<string name="bug_report_desc">アプリは過去数日間のログを保持していますが、勝手に外部に送信することはありません。ユーザが明示的にログを送信する操作した時だけ参照されます。</string>
|
<string name="bug_report_desc">アプリは過去数日間のログを保持していますが、勝手に外部に送信することはありません。ユーザが明示的にログを送信する操作した時だけ参照されます。</string>
|
||||||
|
@ -1261,4 +1261,12 @@
|
||||||
<string name="image_alpha_too_low">背景画像のアルファが低すぎます。 画像を見ることができますか?</string>
|
<string name="image_alpha_too_low">背景画像のアルファが低すぎます。 画像を見ることができますか?</string>
|
||||||
<string name="enable_deprecated_something">陳腐化した何かを有効にする</string>
|
<string name="enable_deprecated_something">陳腐化した何かを有効にする</string>
|
||||||
<string name="misskey_support_end">Misskeyサポートは終了しました</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>
|
</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="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="enable_deprecated_something">Enable deprecated something</string>
|
||||||
<string name="misskey_support_end">Misskey support has ended</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>
|
</resources>
|
||||||
|
|
|
@ -119,7 +119,7 @@ for my $lang ( sort keys %langs ){
|
||||||
my $sv = $value;
|
my $sv = $value;
|
||||||
$sv =~ s/(%\d+\$[\d\.]*[sdxf])//g;
|
$sv =~ s/(%\d+\$[\d\.]*[sdxf])//g;
|
||||||
# Unit:%. や %% を除外したい
|
# Unit:%. や %% を除外したい
|
||||||
$sv =~ s/%[\s.。%]//g;
|
$sv =~ s/%[\s.,。、%]//g;
|
||||||
if( $sv =~ /%/ ){
|
if( $sv =~ /%/ ){
|
||||||
$hasError =1;
|
$hasError =1;
|
||||||
print "!! ($lang)$name : broken param: $sv // $value\n";
|
print "!! ($lang)$name : broken param: $sv // $value\n";
|
||||||
|
|
Loading…
Reference in New Issue