古い端末のサポート終了予定をサイドメニューに表示する
This commit is contained in:
parent
3831ff27b9
commit
43884919f8
|
@ -74,7 +74,7 @@ class ActMain : AppCompatActivity(),
|
||||||
var stripIconSize = 1
|
var stripIconSize = 1
|
||||||
var screenBottomPadding = 0
|
var screenBottomPadding = 0
|
||||||
var timelineFont: Typeface = Typeface.DEFAULT
|
var timelineFont: Typeface = Typeface.DEFAULT
|
||||||
var timeline_font_bold: Typeface = Typeface.DEFAULT_BOLD
|
var timelineFontBold: Typeface = Typeface.DEFAULT_BOLD
|
||||||
var eventFadeAlpha = 1f
|
var eventFadeAlpha = 1f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,13 +39,13 @@ fun ActMain.reloadFonts() {
|
||||||
sv = PrefS.spTimelineFontBold(pref)
|
sv = PrefS.spTimelineFontBold(pref)
|
||||||
if (sv.isNotEmpty()) {
|
if (sv.isNotEmpty()) {
|
||||||
try {
|
try {
|
||||||
ActMain.timeline_font_bold = Typeface.createFromFile(sv)
|
ActMain.timelineFontBold = Typeface.createFromFile(sv)
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
log.trace(ex)
|
log.trace(ex)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
ActMain.timeline_font_bold = Typeface.create(ActMain.timelineFont, Typeface.BOLD)
|
ActMain.timelineFontBold = Typeface.create(ActMain.timelineFont, Typeface.BOLD)
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
log.trace(ex)
|
log.trace(ex)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.drawable.StateListDrawable
|
import android.graphics.drawable.StateListDrawable
|
||||||
|
import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
|
@ -11,6 +12,7 @@ import android.text.TextPaint
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.text.style.ClickableSpan
|
import android.text.style.ClickableSpan
|
||||||
import android.text.style.ForegroundColorSpan
|
import android.text.style.ForegroundColorSpan
|
||||||
|
import android.text.style.UnderlineSpan
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.BaseAdapter
|
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.VersionString
|
||||||
import jp.juggler.subwaytooter.util.openBrowser
|
import jp.juggler.subwaytooter.util.openBrowser
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.jetbrains.anko.backgroundColor
|
import org.jetbrains.anko.backgroundColor
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -47,120 +50,146 @@ class SideMenuAdapter(
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LogCategory("SideMenuAdapter")
|
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 val itemTypeCount = ItemType.values().size
|
||||||
|
|
||||||
private var lastVersionView: WeakReference<TextView>? = null
|
private var lastVersionView: WeakReference<TextView>? = null
|
||||||
|
|
||||||
private var versionRow = SpannableStringBuilder("")
|
private var versionText = SpannableStringBuilder("")
|
||||||
|
|
||||||
private var releaseInfo: JsonObject? = null
|
private var releaseInfo: JsonObject? = null
|
||||||
|
|
||||||
private fun clickableSpan(url: String) =
|
private fun clickableSpan(
|
||||||
object : ClickableSpan() {
|
url: String,
|
||||||
override fun onClick(widget: View) {
|
showUnderline: Boolean = false,
|
||||||
widget.activity?.openBrowser(url)
|
) = object : ClickableSpan() {
|
||||||
}
|
override fun onClick(widget: View) {
|
||||||
|
widget.activity?.openBrowser(url)
|
||||||
override fun updateDrawState(ds: TextPaint) {
|
|
||||||
super.updateDrawState(ds)
|
|
||||||
ds.isUnderlineText = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文字列を組み立ててhandler経由でViewに設定する
|
override fun updateDrawState(ds: TextPaint) {
|
||||||
// メインスレッドでもそれ以外でも動作する
|
super.updateDrawState(ds)
|
||||||
fun afterGet(appContext: Context, handler: Handler, currentVersion: String) {
|
ds.isUnderlineText = showUnderline
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
handler.post { lastVersionView?.get()?.text = versionRow }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// メインスレッドから呼ばれる
|
private fun SpannableStringBuilder.appendSpanLine(
|
||||||
private fun checkVersion(appContext: Context, handler: Handler) {
|
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 {
|
val currentVersion = try {
|
||||||
appContext.packageManager.getPackageInfo(appContext.packageName, 0).versionName
|
packageManager.getPackageInfo(packageName, 0).versionName
|
||||||
} catch (ignored: PackageManager.NameNotFoundException) {
|
} catch (ignored: PackageManager.NameNotFoundException) {
|
||||||
"??"
|
"??"
|
||||||
}
|
}
|
||||||
|
|
||||||
versionRow = SpannableStringBuilder().apply {
|
append(
|
||||||
append(
|
getString(
|
||||||
appContext.getString(
|
R.string.app_name_with_version,
|
||||||
R.string.app_name_with_version,
|
getString(R.string.app_name),
|
||||||
appContext.getString(R.string.app_name),
|
currentVersion
|
||||||
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) }
|
// 端末のOSバージョンがサポートから外れる予定なら、サイドメニューにリンクを追加する
|
||||||
if (lastUpdated != null && System.currentTimeMillis() - lastUpdated < 86400000L) {
|
if (Build.VERSION.SDK_INT < releaseMinSdkVersionScheduled) {
|
||||||
afterGet(appContext, handler, currentVersion)
|
appendSpanLine(
|
||||||
} else {
|
getString(R.string.old_devices_warning),
|
||||||
launchIO {
|
clickableSpan(urlOlderDevices, showUnderline = true),
|
||||||
val json =
|
)
|
||||||
App1.getHttpCached("https://mastodon-msg.juggler.jp/appVersion/appVersion.json")
|
}
|
||||||
?.decodeUTF8()?.decodeJsonObject()
|
}
|
||||||
if (json != null) {
|
|
||||||
releaseInfo = json
|
// メインスレッドから呼ばれる
|
||||||
afterGet(appContext, handler, currentVersion)
|
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()
|
movementMethod = LinkMovementMethod.getInstance()
|
||||||
textSize = 18f
|
textSize = 18f
|
||||||
isAllCaps = false
|
isAllCaps = false
|
||||||
|
setLineSpacing(
|
||||||
|
1f,
|
||||||
|
1.1f
|
||||||
|
)
|
||||||
background = null
|
background = null
|
||||||
text = versionRow
|
text = versionText
|
||||||
}
|
}
|
||||||
ItemType.IT_TIMEZONE ->
|
ItemType.IT_TIMEZONE ->
|
||||||
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_item).apply {
|
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_item).apply {
|
||||||
|
@ -502,7 +535,7 @@ class SideMenuAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkVersion(actMain.applicationContext, handler)
|
actMain.applicationContext.checkVersion()
|
||||||
|
|
||||||
ListView(actMain).apply {
|
ListView(actMain).apply {
|
||||||
adapter = this@SideMenuAdapter
|
adapter = this@SideMenuAdapter
|
||||||
|
|
|
@ -174,7 +174,7 @@ private fun ColumnViewHolder.showAnnouncementFonts() {
|
||||||
tvAnnouncementPeriod.setLineSpacing(0f, spacing)
|
tvAnnouncementPeriod.setLineSpacing(0f, spacing)
|
||||||
tvAnnouncementContent.setLineSpacing(0f, spacing)
|
tvAnnouncementContent.setLineSpacing(0f, spacing)
|
||||||
}
|
}
|
||||||
tvAnnouncementsCaption.typeface = ActMain.timeline_font_bold
|
tvAnnouncementsCaption.typeface = ActMain.timelineFontBold
|
||||||
val fontNormal = ActMain.timelineFont
|
val fontNormal = ActMain.timelineFont
|
||||||
tvAnnouncementsIndex.typeface = fontNormal
|
tvAnnouncementsIndex.typeface = fontNormal
|
||||||
tvAnnouncementPeriod.typeface = fontNormal
|
tvAnnouncementPeriod.typeface = fontNormal
|
||||||
|
|
|
@ -667,7 +667,7 @@ internal class ViewHolderHeaderProfile(
|
||||||
authorDomain = who
|
authorDomain = who
|
||||||
)
|
)
|
||||||
|
|
||||||
val nameTypeface = ActMain.timeline_font_bold
|
val nameTypeface = ActMain.timelineFontBold
|
||||||
val valueTypeface = ActMain.timelineFont
|
val valueTypeface = ActMain.timelineFont
|
||||||
|
|
||||||
for (item in fields) {
|
for (item in fields) {
|
||||||
|
|
|
@ -49,7 +49,7 @@ fun ItemViewHolder.bind(
|
||||||
|
|
||||||
this.accessInfo = column.accessInfo
|
this.accessInfo = column.accessInfo
|
||||||
|
|
||||||
val fontBold = ActMain.timeline_font_bold
|
val fontBold = ActMain.timelineFontBold
|
||||||
val fontNormal = ActMain.timelineFont
|
val fontNormal = ActMain.timelineFont
|
||||||
viewRoot.scan { v ->
|
viewRoot.scan { v ->
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -29,7 +29,7 @@ class SpanOutputEnv(
|
||||||
val decorationEnabled = PrefB.bpMfmDecorationEnabled(context)
|
val decorationEnabled = PrefB.bpMfmDecorationEnabled(context)
|
||||||
val showUnsupportedMarkup = PrefB.bpMfmDecorationShowUnsupportedMarkup(context)
|
val showUnsupportedMarkup = PrefB.bpMfmDecorationShowUnsupportedMarkup(context)
|
||||||
|
|
||||||
val fontBold = ActMain.timeline_font_bold
|
val fontBold = ActMain.timelineFontBold
|
||||||
val linkHelper: LinkHelper? = options.linkHelper
|
val linkHelper: LinkHelper? = options.linkHelper
|
||||||
var spanList = SpanList()
|
var spanList = SpanList()
|
||||||
|
|
||||||
|
|
|
@ -1150,4 +1150,5 @@
|
||||||
<string name="use_web_settings">Web設定を使う</string>
|
<string name="use_web_settings">Web設定を使う</string>
|
||||||
<string name="content">本文</string>
|
<string name="content">本文</string>
|
||||||
<string name="enable_misskey_notification_check">Misskeyサーバで通知チェックを行う(不安定)</string>
|
<string name="enable_misskey_notification_check">Misskeyサーバで通知チェックを行う(不安定)</string>
|
||||||
|
<string name="old_devices_warning">古い端末のサポート終了</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1159,4 +1159,5 @@
|
||||||
<string name="use_web_settings">(Use web setting)</string>
|
<string name="use_web_settings">(Use web setting)</string>
|
||||||
<string name="content">Content</string>
|
<string name="content">Content</string>
|
||||||
<string name="enable_misskey_notification_check">Enable notification check for Misskey server (unstable)</string>
|
<string name="enable_misskey_notification_check">Enable notification check for Misskey server (unstable)</string>
|
||||||
|
<string name="old_devices_warning">End of support for older devices</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue