From aefcc7c798d385398829f83df0fe0af9d6ad2ac1 Mon Sep 17 00:00:00 2001 From: tateisu Date: Sun, 26 Feb 2023 08:15:57 +0900 Subject: [PATCH] =?UTF-8?q?=E3=82=A2=E3=83=97=E3=83=AA=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=81=AB=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=BB=E3=82=AF=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=81=A8=E8=A8=AD=E5=AE=9A=E9=A0=85=E7=9B=AE?= =?UTF-8?q?=E3=82=92=E3=81=84=E3=81=8F=E3=81=A4=E3=81=8B=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=80=82=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE=E3=82=A2=E3=82=B9?= =?UTF-8?q?=E3=83=9A=E3=82=AF=E3=83=88=E6=AF=94=E3=82=92DB=E3=81=AB?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E3=81=99=E3=82=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/jp/juggler/apng/ApngFrames.kt | 3 + .../java/jp/juggler/subwaytooter/ActMain.kt | 3 + .../subwaytooter/action/Action_Reaction.kt | 12 ++-- .../subwaytooter/api/entity/TootAccount.kt | 3 + .../subwaytooter/api/entity/TootAccountRef.kt | 4 ++ .../subwaytooter/appsetting/AppSettingItem.kt | 62 +++++++++++++++--- .../ColumnViewHolderReactions.kt | 4 +- .../ViewHolderHeaderProfile.kt | 2 + .../subwaytooter/dialog/EmojiPicker.kt | 1 - .../itemviewholder/ItemViewHolderReaction.kt | 18 +++--- .../itemviewholder/ItemViewHolderShow.kt | 4 +- .../mfm/MisskeyMarkdownDecoder.kt | 2 +- .../jp/juggler/subwaytooter/pref/PrefI.kt | 5 ++ .../jp/juggler/subwaytooter/pref/PrefS.kt | 8 ++- .../subwaytooter/pref/impl/StringPref.kt | 1 + .../subwaytooter/span/NetworkEmojiSpan.kt | 47 ++++++++++++-- .../subwaytooter/table/AppDatabaseHolder.kt | 8 ++- .../juggler/subwaytooter/table/ImageAspect.kt | 64 +++++++++++++++++++ .../subwaytooter/util/CustomEmojiCache.kt | 29 ++++++--- .../subwaytooter/util/DecodeOptions.kt | 19 +++++- .../juggler/subwaytooter/util/EmojiDecoder.kt | 10 +-- .../subwaytooter/util/EmojiImageRect.kt | 42 ++++++------ .../util/NetworkEmojiInvalidator.kt | 4 ++ .../subwaytooter/view/NetworkEmojiView.kt | 23 ++++--- app/src/main/res/values-ja/strings.xml | 10 ++- app/src/main/res/values/strings.xml | 8 +++ checkMissingTranslation.pl | 2 +- 27 files changed, 312 insertions(+), 86 deletions(-) create mode 100644 app/src/main/java/jp/juggler/subwaytooter/table/ImageAspect.kt diff --git a/apng_android/src/main/java/jp/juggler/apng/ApngFrames.kt b/apng_android/src/main/java/jp/juggler/apng/ApngFrames.kt index 23125474..43c45701 100644 --- a/apng_android/src/main/java/jp/juggler/apng/ApngFrames.kt +++ b/apng_android/src/main/java/jp/juggler/apng/ApngFrames.kt @@ -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 } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt index 5697ae13..b8738908 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt @@ -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() } diff --git a/app/src/main/java/jp/juggler/subwaytooter/action/Action_Reaction.kt b/app/src/main/java/jp/juggler/subwaytooter/action/Action_Reaction.kt index 70dec249..b2616c08 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/action/Action_Reaction.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/action/Action_Reaction.kt @@ -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) diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt index 6b50efb4..e70905d1 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt @@ -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) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccountRef.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccountRef.kt index a28e582a..6cac3aca 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccountRef.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccountRef.kt @@ -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), ) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/appsetting/AppSettingItem.kt b/app/src/main/java/jp/juggler/subwaytooter/appsetting/AppSettingItem.kt index b17e67f2..e152348d 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/appsetting/AppSettingItem.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/appsetting/AppSettingItem.kt @@ -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)) } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ColumnViewHolderReactions.kt b/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ColumnViewHolderReactions.kt index 3f9c2b12..dc6a539e 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ColumnViewHolderReactions.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ColumnViewHolderReactions.kt @@ -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(), ) diff --git a/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ViewHolderHeaderProfile.kt b/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ViewHolderHeaderProfile.kt index e48e56e5..281405c8 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ViewHolderHeaderProfile.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ViewHolderHeaderProfile.kt @@ -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 diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/EmojiPicker.kt b/app/src/main/java/jp/juggler/subwaytooter/dialog/EmojiPicker.kt index 947b921b..ee0672f9 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/dialog/EmojiPicker.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/EmojiPicker.kt @@ -290,7 +290,6 @@ private class EmojiPicker( item.customEmoji.url }, initialAspect = item.customEmoji.aspect, - defaultWidth = gridSize, defaultHeight = gridSize, ) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderReaction.kt b/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderReaction.kt index ba493e6f..6a77b6f2 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderReaction.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderReaction.kt @@ -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)) { // 自分がリアクションしたやつは背景を変える diff --git a/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderShow.kt b/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderShow.kt index dd4004e1..cb42c165 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderShow.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderShow.kt @@ -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) diff --git a/app/src/main/java/jp/juggler/subwaytooter/mfm/MisskeyMarkdownDecoder.kt b/app/src/main/java/jp/juggler/subwaytooter/mfm/MisskeyMarkdownDecoder.kt index d2a00390..b7d3710e 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/mfm/MisskeyMarkdownDecoder.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/mfm/MisskeyMarkdownDecoder.kt @@ -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) diff --git a/app/src/main/java/jp/juggler/subwaytooter/pref/PrefI.kt b/app/src/main/java/jp/juggler/subwaytooter/pref/PrefI.kt index 88648a63..6bb806d4 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/pref/PrefI.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/pref/PrefI.kt @@ -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) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/pref/PrefS.kt b/app/src/main/java/jp/juggler/subwaytooter/pref/PrefS.kt index 2465158a..79c2223e 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/pref/PrefS.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/pref/PrefS.kt @@ -53,4 +53,10 @@ object PrefS { val spTimelineSpacing = StringPref("TimelineSpacing", "") val spEventTextAlpha = StringPref("EventTextAlpha", "") -} \ No newline at end of file + + 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") +} diff --git a/app/src/main/java/jp/juggler/subwaytooter/pref/impl/StringPref.kt b/app/src/main/java/jp/juggler/subwaytooter/pref/impl/StringPref.kt index 25d98446..a0c281f8 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/pref/impl/StringPref.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/pref/impl/StringPref.kt @@ -19,4 +19,5 @@ class StringPref( defVal != pref.getString(key, defVal) fun toInt() = value.toIntOrNull() ?: defVal.toInt() + fun toFloat() = value.toFloatOrNull() ?: defVal.toFloat() } diff --git a/app/src/main/java/jp/juggler/subwaytooter/span/NetworkEmojiSpan.kt b/app/src/main/java/jp/juggler/subwaytooter/span/NetworkEmojiSpan.kt index 6932c84f..a9adaf46 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/span/NetworkEmojiSpan.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/span/NetworkEmojiSpan.kt @@ -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() diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/AppDatabaseHolder.kt b/app/src/main/java/jp/juggler/subwaytooter/table/AppDatabaseHolder.kt index d2b6321f..2de73e7d 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/table/AppDatabaseHolder.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/table/AppDatabaseHolder.kt @@ -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) +} diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/ImageAspect.kt b/app/src/main/java/jp/juggler/subwaytooter/table/ImageAspect.kt new file mode 100644 index 00000000..b1a8b1e9 --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/table/ImageAspect.kt @@ -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) + } + } +} diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/CustomEmojiCache.kt b/app/src/main/java/jp/juggler/subwaytooter/util/CustomEmojiCache.kt index 9d1071f5..145ebf6a 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/CustomEmojiCache.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/util/CustomEmojiCache.kt @@ -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, val url: String, - val onLoadComplete: () -> Unit, + val onLoadComplete: (ApngFrames?) -> Unit, ) // APNGデコード済のキャッシュデータ @@ -115,7 +119,7 @@ class CustomEmojiCache( fun getFrames( refDrawTarget: WeakReference?, 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() @@ -319,7 +330,7 @@ class CustomEmojiCache( private fun decodeAPNG(data: ByteArray, url: String): ApngFrames? { val errors = ArrayList() - val maxSize = 256 + val maxSize = PrefS.spEmojiPixels.toInt().clip(16, 1024) try { // APNGをデコード AWebPも diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/DecodeOptions.kt b/app/src/main/java/jp/juggler/subwaytooter/util/DecodeOptions.kt index ef13f147..93f9f1d1 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/DecodeOptions.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/util/DecodeOptions.kt @@ -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? = 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 diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt b/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt index 60a74d6a..be295789 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt @@ -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 { // 絵文字プロクシを利用できない diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/EmojiImageRect.kt b/app/src/main/java/jp/juggler/subwaytooter/util/EmojiImageRect.kt index a1d6cbca..496a9983 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/EmojiImageRect.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/util/EmojiImageRect.kt @@ -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 } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/NetworkEmojiInvalidator.kt b/app/src/main/java/jp/juggler/subwaytooter/util/NetworkEmojiInvalidator.kt index 83c818ba..8ea93c7c 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/NetworkEmojiInvalidator.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/util/NetworkEmojiInvalidator.kt @@ -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 diff --git a/app/src/main/java/jp/juggler/subwaytooter/view/NetworkEmojiView.kt b/app/src/main/java/jp/juggler/subwaytooter/view/NetworkEmojiView.kt index 2928154d..48a0a4dc 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/view/NetworkEmojiView.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/view/NetworkEmojiView.kt @@ -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) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 7c591df1..2775c9c7 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1253,7 +1253,7 @@ 承認されました。 アカウント一覧 フィルタされた投稿にユーザ名を表示する - ログ収集レベル + ログ収集レベル ログを電子メールで送信する バグ報告 アプリは過去数日間のログを保持していますが、勝手に外部に送信することはありません。ユーザが明示的にログを送信する操作した時だけ参照されます。 @@ -1261,4 +1261,12 @@ 背景画像のアルファが低すぎます。 画像を見ることができますか? 陳腐化した何かを有効にする Misskeyサポートは終了しました + (Mastodon)カスタム絵文字の大きさ(単位:%、デフォルト:100) + (Misskey)カスタム絵文字の大きさ(単位:%、デフォルト:250) + リアクションの絵文字の大きさ(単位:%、デフォルト:150) + ユーザ名の絵文字の大きさ(単位:%、デフォルト:100) + ワイドカスタム絵文字 + 有効 + 無効 + 絵文字テクスチャの最大ピクセル数(単位:ピクセル、デフォルト: 256。 タスクキルが必要。端末のRAMが少ない場合は64程度まで下げることをお勧めします) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3fa7e4cb..b34d5cda 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1269,4 +1269,12 @@ background image alpha is too low. can you view the image? Enable deprecated something Misskey support has ended + (Mastodon)Custom emoji scaling(Unit:%. default:100) + (Misskey)Custom emoji scaling(Unit:%。default:250) + Reaction emoji scaling(Unit:%。default:150) + User name emoji scaling(Unit:%。default:100) + Wide custom emoji + Enabled + Disabled + Emoji texture max pixels(Unix:pixels, default: 256. task kill required. reduce to 64 if your device\'s RAM is not enough) diff --git a/checkMissingTranslation.pl b/checkMissingTranslation.pl index 6ccdd7ca..bb6a20ee 100644 --- a/checkMissingTranslation.pl +++ b/checkMissingTranslation.pl @@ -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";