diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt index be86c318..59257c93 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt @@ -74,7 +74,7 @@ class ActMain : AppCompatActivity(), var stripIconSize = 1 var screenBottomPadding = 0 var timelineFont: Typeface = Typeface.DEFAULT - var timeline_font_bold: Typeface = Typeface.DEFAULT_BOLD + var timelineFontBold: Typeface = Typeface.DEFAULT_BOLD var eventFadeAlpha = 1f } diff --git a/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainStyle.kt b/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainStyle.kt index 2c39daa3..a5fac281 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainStyle.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainStyle.kt @@ -39,13 +39,13 @@ fun ActMain.reloadFonts() { sv = PrefS.spTimelineFontBold(pref) if (sv.isNotEmpty()) { try { - ActMain.timeline_font_bold = Typeface.createFromFile(sv) + ActMain.timelineFontBold = Typeface.createFromFile(sv) } catch (ex: Throwable) { log.trace(ex) } } else { try { - ActMain.timeline_font_bold = Typeface.create(ActMain.timelineFont, Typeface.BOLD) + ActMain.timelineFontBold = Typeface.create(ActMain.timelineFont, Typeface.BOLD) } catch (ex: Throwable) { log.trace(ex) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/actmain/SideMenuAdapter.kt b/app/src/main/java/jp/juggler/subwaytooter/actmain/SideMenuAdapter.kt index 20b9297f..462c37c9 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/actmain/SideMenuAdapter.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/actmain/SideMenuAdapter.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.graphics.drawable.StateListDrawable +import android.os.Build import android.os.Handler import android.text.Spannable import android.text.SpannableStringBuilder @@ -11,6 +12,7 @@ import android.text.TextPaint import android.text.method.LinkMovementMethod import android.text.style.ClickableSpan import android.text.style.ForegroundColorSpan +import android.text.style.UnderlineSpan import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter @@ -30,7 +32,8 @@ import jp.juggler.subwaytooter.table.SavedAccount import jp.juggler.subwaytooter.util.VersionString import jp.juggler.subwaytooter.util.openBrowser import jp.juggler.util.* -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import org.jetbrains.anko.backgroundColor import java.lang.ref.WeakReference import java.util.* @@ -47,120 +50,146 @@ class SideMenuAdapter( companion object { private val log = LogCategory("SideMenuAdapter") + private const val urlAppVersion = + "https://mastodon-msg.juggler.jp/appVersion/appVersion.json" + private const val urlGithubReleases = + "https://github.com/tateisu/SubwayTooter/releases" + private const val urlOlderDevices = + "https://github.com/tateisu/SubwayTooter/discussions/192" + private val itemTypeCount = ItemType.values().size private var lastVersionView: WeakReference? = null - private var versionRow = SpannableStringBuilder("") + private var versionText = SpannableStringBuilder("") private var releaseInfo: JsonObject? = null - private fun clickableSpan(url: String) = - object : ClickableSpan() { - override fun onClick(widget: View) { - widget.activity?.openBrowser(url) - } - - override fun updateDrawState(ds: TextPaint) { - super.updateDrawState(ds) - ds.isUnderlineText = false - } + private fun clickableSpan( + url: String, + showUnderline: Boolean = false, + ) = object : ClickableSpan() { + override fun onClick(widget: View) { + widget.activity?.openBrowser(url) } - // 文字列を組み立ててhandler経由でViewに設定する - // メインスレッドでもそれ以外でも動作する - fun afterGet(appContext: Context, handler: Handler, currentVersion: String) { - - versionRow = SpannableStringBuilder().apply { - append( - appContext.getString( - R.string.app_name_with_version, - appContext.getString(R.string.app_name), - currentVersion - ) - ) - val newRelease = releaseInfo?.jsonObject( - if (PrefB.bpCheckBetaVersion()) "beta" else "stable" - ) - - val newVersion = - (newRelease?.string("name")?.notEmpty() ?: newRelease?.string("tag_name")) - ?.replace("""(v|version)\s*""".toRegex(RegexOption.IGNORE_CASE), "") - ?.trim() - - if (newVersion == null || newVersion.isEmpty() || VersionString(currentVersion) >= VersionString( - newVersion - ) - ) { - val url = "https://github.com/tateisu/SubwayTooter/releases" - append("\n") - val start = length - append(appContext.getString(R.string.release_note)) - setSpan( - clickableSpan(url), - start, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - ) - } else { - append("\n") - var start = length - append( - appContext.getString( - R.string.new_version_available, - newVersion - ) - ) - setSpan( - ForegroundColorSpan( - appContext.attrColor(R.attr.colorRegexFilterError) - ), - start, length, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - ) - - newRelease?.string("html_url")?.let { url -> - append("\n") - start = length - append(appContext.getString(R.string.release_note_with_assets)) - setSpan( - clickableSpan(url), - start, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - ) - } - } + override fun updateDrawState(ds: TextPaint) { + super.updateDrawState(ds) + ds.isUnderlineText = showUnderline } - handler.post { lastVersionView?.get()?.text = versionRow } } - // メインスレッドから呼ばれる - private fun checkVersion(appContext: Context, handler: Handler) { + private fun SpannableStringBuilder.appendSpanLine( + text: String, + vararg spans: Any, + ) = this.apply { + if (isNotEmpty()) { + append("\n") + } + val start = length + append(text) + for (span in spans) { + setSpan(span, start, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } + + // バージョン情報と更新履歴と新リリース告知の文字列を組み立てる + // メインスレッドでもそれ以外でも動作すること + private fun Context.createVersionRow() = SpannableStringBuilder().apply { val currentVersion = try { - appContext.packageManager.getPackageInfo(appContext.packageName, 0).versionName + packageManager.getPackageInfo(packageName, 0).versionName } catch (ignored: PackageManager.NameNotFoundException) { "??" } - versionRow = SpannableStringBuilder().apply { - append( - appContext.getString( - R.string.app_name_with_version, - appContext.getString(R.string.app_name), - currentVersion + append( + getString( + R.string.app_name_with_version, + getString(R.string.app_name), + currentVersion + ) + ) + val newRelease = releaseInfo?.jsonObject( + if (PrefB.bpCheckBetaVersion()) "beta" else "stable" + ) + + // 使用中のアプリバージョンより新しいリリースがある? + val newVersion = + (newRelease?.string("name")?.notEmpty() ?: newRelease?.string("tag_name")) + ?.replace("""(v|version)\s*""".toRegex(RegexOption.IGNORE_CASE), "") + ?.trim() + ?.notEmpty() + ?.takeIf { VersionString(it) > VersionString(currentVersion) } + + val releaseMinSdkVersion = newRelease?.int("minSdkVersion") + ?: Build.VERSION.SDK_INT + val releaseMinSdkVersionScheduled = newRelease?.int("minSdkVersionScheduled") + ?: Build.VERSION.SDK_INT + + when { + // 新しいバージョンがある + // それはこの端末にインストール可能である + newVersion != null && Build.VERSION.SDK_INT >= releaseMinSdkVersion -> { + appendSpanLine( + getString( + R.string.new_version_available, + newVersion + ), + ForegroundColorSpan( + attrColor(R.attr.colorRegexFilterError) + ), ) + newRelease?.string("html_url")?.let { + appendSpanLine( + getString(R.string.release_note_with_assets), + clickableSpan(it) + ) + } + } + + // 通常時は更新履歴へのリンク + else -> appendSpanLine( + getString(R.string.release_note), + UnderlineSpan(), + clickableSpan(urlGithubReleases), ) } - val lastUpdated = releaseInfo?.string("updated_at")?.let { TootStatus.parseTime(it) } - if (lastUpdated != null && System.currentTimeMillis() - lastUpdated < 86400000L) { - afterGet(appContext, handler, currentVersion) - } else { - launchIO { - val json = - App1.getHttpCached("https://mastodon-msg.juggler.jp/appVersion/appVersion.json") - ?.decodeUTF8()?.decodeJsonObject() - if (json != null) { - releaseInfo = json - afterGet(appContext, handler, currentVersion) + // 端末のOSバージョンがサポートから外れる予定なら、サイドメニューにリンクを追加する + if (Build.VERSION.SDK_INT < releaseMinSdkVersionScheduled) { + appendSpanLine( + getString(R.string.old_devices_warning), + clickableSpan(urlOlderDevices, showUnderline = true), + ) + } + } + + // メインスレッドから呼ばれる + private fun Context.checkVersion() { + // サイドメニューから参照されるバージョン文字列を初期化する + // この時点ではreleaseInfoはnullかもしれない + versionText = createVersionRow() + + // releaseInfoが既にあり、更新時刻が十分に新しいなら情報を取得し直す必要はない + releaseInfo?.string("updated_at") + ?.let { TootStatus.parseTime(it) } + ?.takeIf { it >= System.currentTimeMillis() - 86400000L } + ?.let { return } + + // リリース情報を取得し直す + launchIO { + try { + val json = App1.getHttpCached(urlAppVersion) + ?.decodeUTF8() + ?.decodeJsonObject() + ?: error("missing appVersion json") + releaseInfo = json + versionText = createVersionRow() + withContext(Dispatchers.Main) { + lastVersionView?.get()?.text = versionText } + } catch (ex: Throwable) { + log.e(ex, "checkVersion failed") } } } @@ -451,8 +480,12 @@ class SideMenuAdapter( movementMethod = LinkMovementMethod.getInstance() textSize = 18f isAllCaps = false + setLineSpacing( + 1f, + 1.1f + ) background = null - text = versionRow + text = versionText } ItemType.IT_TIMEZONE -> viewOrInflate(view, parent, R.layout.lv_sidemenu_item).apply { @@ -502,7 +535,7 @@ class SideMenuAdapter( } init { - checkVersion(actMain.applicationContext, handler) + actMain.applicationContext.checkVersion() ListView(actMain).apply { adapter = this@SideMenuAdapter diff --git a/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ColumnViewHolderAnnouncements.kt b/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ColumnViewHolderAnnouncements.kt index 04e4d334..ef4ac3bc 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ColumnViewHolderAnnouncements.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ColumnViewHolderAnnouncements.kt @@ -174,7 +174,7 @@ private fun ColumnViewHolder.showAnnouncementFonts() { tvAnnouncementPeriod.setLineSpacing(0f, spacing) tvAnnouncementContent.setLineSpacing(0f, spacing) } - tvAnnouncementsCaption.typeface = ActMain.timeline_font_bold + tvAnnouncementsCaption.typeface = ActMain.timelineFontBold val fontNormal = ActMain.timelineFont tvAnnouncementsIndex.typeface = fontNormal tvAnnouncementPeriod.typeface = fontNormal 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 ac896a83..957e2e18 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ViewHolderHeaderProfile.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ViewHolderHeaderProfile.kt @@ -667,7 +667,7 @@ internal class ViewHolderHeaderProfile( authorDomain = who ) - val nameTypeface = ActMain.timeline_font_bold + val nameTypeface = ActMain.timelineFontBold val valueTypeface = ActMain.timelineFont for (item in fields) { 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 4ab7ad4e..b7f03bfc 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderShow.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderShow.kt @@ -49,7 +49,7 @@ fun ItemViewHolder.bind( this.accessInfo = column.accessInfo - val fontBold = ActMain.timeline_font_bold + val fontBold = ActMain.timelineFontBold val fontNormal = ActMain.timelineFont viewRoot.scan { v -> try { diff --git a/app/src/main/java/jp/juggler/subwaytooter/mfm/SpanOutputEnv.kt b/app/src/main/java/jp/juggler/subwaytooter/mfm/SpanOutputEnv.kt index 8217c820..18f0616b 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/mfm/SpanOutputEnv.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/mfm/SpanOutputEnv.kt @@ -29,7 +29,7 @@ class SpanOutputEnv( val decorationEnabled = PrefB.bpMfmDecorationEnabled(context) val showUnsupportedMarkup = PrefB.bpMfmDecorationShowUnsupportedMarkup(context) - val fontBold = ActMain.timeline_font_bold + val fontBold = ActMain.timelineFontBold val linkHelper: LinkHelper? = options.linkHelper var spanList = SpanList() diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 4d9659a8..48a24555 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1150,4 +1150,5 @@ Web設定を使う 本文 Misskeyサーバで通知チェックを行う(不安定) + 古い端末のサポート終了 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1e05ad51..5c43f7d9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1159,4 +1159,5 @@ (Use web setting) Content Enable notification check for Misskey server (unstable) + End of support for older devices