more clean up
This commit is contained in:
parent
73afdb7b59
commit
ac2f54c22a
|
@ -166,6 +166,7 @@
|
|||
<w>utoken</w>
|
||||
<w>weblate</w>
|
||||
<w>webpushcallback</w>
|
||||
<w>webpushendpoint</w>
|
||||
<w>webpushserverkey</w>
|
||||
<w>webpushtokencheck</w>
|
||||
<w>weiner</w>
|
||||
|
|
|
@ -424,7 +424,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener,
|
|||
btnNotificationSoundReset = findViewById(R.id.btnNotificationSoundReset)
|
||||
btnNotificationStyleEdit = findViewById(R.id.btnNotificationStyleEdit)
|
||||
btnNotificationStyleEditReply = findViewById(R.id.btnNotificationStyleEditReply)
|
||||
btnNotificationStyleEditReply.vg(Pref.bpSeparateReplyNotificationGroup(pref))
|
||||
btnNotificationStyleEditReply.vg(PrefB.bpSeparateReplyNotificationGroup(pref))
|
||||
|
||||
nameInvalidator = NetworkEmojiInvalidator(handler, etDisplayName)
|
||||
noteInvalidator = NetworkEmojiInvalidator(handler, etNote)
|
||||
|
@ -894,7 +894,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener,
|
|||
launchMain {
|
||||
runApiTask(account) { client ->
|
||||
client.authentication1(
|
||||
Pref.spClientName(this@ActAccountSetting),
|
||||
PrefS.spClientName(this@ActAccountSetting),
|
||||
forceUpdateClient = true
|
||||
)
|
||||
}?.let { result ->
|
||||
|
|
|
@ -243,7 +243,7 @@ class ActMain : AppCompatActivity(),
|
|||
override fun run() {
|
||||
handler.removeCallbacks(this)
|
||||
if (!isStartedEx) return
|
||||
if (Pref.bpRelativeTimestamp(pref)) {
|
||||
if (PrefB.bpRelativeTimestamp(pref)) {
|
||||
appState.columnList.forEach { it.fireRelativeTime() }
|
||||
handler.postDelayed(this, 10000L)
|
||||
}
|
||||
|
@ -324,7 +324,7 @@ class ActMain : AppCompatActivity(),
|
|||
set(value) {
|
||||
if (value != quickTootVisibility) {
|
||||
quickTootVisibility = value
|
||||
pref.edit().put(Pref.spQuickTootVisibility, value.id.toString()).apply()
|
||||
pref.edit().put(PrefS.spQuickTootVisibility, value.id.toString()).apply()
|
||||
showQuickTootVisibility()
|
||||
}
|
||||
}
|
||||
|
@ -399,7 +399,7 @@ class ActMain : AppCompatActivity(),
|
|||
},
|
||||
{ env ->
|
||||
|
||||
val dbId = Pref.lpTabletTootDefaultAccount(App1.pref)
|
||||
val dbId = PrefL.lpTabletTootDefaultAccount(App1.pref)
|
||||
if (dbId != -1L) {
|
||||
val a = SavedAccount.loadAccount(this@ActMain, dbId)
|
||||
if (a != null && !a.isPseudo) return a
|
||||
|
@ -547,17 +547,17 @@ class ActMain : AppCompatActivity(),
|
|||
appState = App1.getAppState(this)
|
||||
pref = App1.pref
|
||||
|
||||
EmojiDecoder.handleUnicodeEmoji = Pref.bpInAppUnicodeEmoji(pref)
|
||||
EmojiDecoder.handleUnicodeEmoji = PrefB.bpInAppUnicodeEmoji(pref)
|
||||
|
||||
density = appState.density
|
||||
acctPadLr = (0.5f + 4f * density).toInt()
|
||||
|
||||
timelineFontSizeSp = Pref.fpTimelineFontSize(pref).clipFontSize()
|
||||
acctFontSizeSp = Pref.fpAcctFontSize(pref).clipFontSize()
|
||||
notificationTlFontSizeSp = Pref.fpNotificationTlFontSize(pref).clipFontSize()
|
||||
headerTextSizeSp = Pref.fpHeaderTextSize(pref).clipFontSize()
|
||||
timelineFontSizeSp = PrefF.fpTimelineFontSize(pref).clipFontSize()
|
||||
acctFontSizeSp = PrefF.fpAcctFontSize(pref).clipFontSize()
|
||||
notificationTlFontSizeSp = PrefF.fpNotificationTlFontSize(pref).clipFontSize()
|
||||
headerTextSizeSp = PrefF.fpHeaderTextSize(pref).clipFontSize()
|
||||
|
||||
val fv = Pref.spTimelineSpacing(pref).toFloatOrNull()
|
||||
val fv = PrefS.spTimelineSpacing(pref).toFloatOrNull()
|
||||
timelineSpacing = if (fv != null && fv.isFinite() && fv != 0f) fv else null
|
||||
|
||||
initUI()
|
||||
|
@ -566,7 +566,7 @@ class ActMain : AppCompatActivity(),
|
|||
|
||||
if (appState.columnCount > 0) {
|
||||
|
||||
val columnPos = Pref.ipLastColumnPos(pref)
|
||||
val columnPos = PrefI.ipLastColumnPos(pref)
|
||||
log.d("ipLastColumnPos load $columnPos")
|
||||
|
||||
// 前回最後に表示していたカラムの位置にスクロールする
|
||||
|
@ -667,14 +667,14 @@ class ActMain : AppCompatActivity(),
|
|||
|
||||
benchmark("reload color") {
|
||||
// カラーカスタマイズを読み直す
|
||||
ListDivider.color = Pref.ipListDividerColor(pref)
|
||||
TabletColumnDivider.color = Pref.ipListDividerColor(pref)
|
||||
ItemViewHolder.toot_color_unlisted = Pref.ipTootColorUnlisted(pref)
|
||||
ItemViewHolder.toot_color_follower = Pref.ipTootColorFollower(pref)
|
||||
ItemViewHolder.toot_color_direct_user = Pref.ipTootColorDirectUser(pref)
|
||||
ItemViewHolder.toot_color_direct_me = Pref.ipTootColorDirectMe(pref)
|
||||
MyClickableSpan.showLinkUnderline = Pref.bpShowLinkUnderline(pref)
|
||||
MyClickableSpan.defaultLinkColor = Pref.ipLinkColor(pref).notZero()
|
||||
ListDivider.color = PrefI.ipListDividerColor(pref)
|
||||
TabletColumnDivider.color = PrefI.ipListDividerColor(pref)
|
||||
ItemViewHolder.toot_color_unlisted = PrefI.ipTootColorUnlisted(pref)
|
||||
ItemViewHolder.toot_color_follower = PrefI.ipTootColorFollower(pref)
|
||||
ItemViewHolder.toot_color_direct_user = PrefI.ipTootColorDirectUser(pref)
|
||||
ItemViewHolder.toot_color_direct_me = PrefI.ipTootColorDirectMe(pref)
|
||||
MyClickableSpan.showLinkUnderline = PrefB.bpShowLinkUnderline(pref)
|
||||
MyClickableSpan.defaultLinkColor = PrefI.ipLinkColor(pref).notZero()
|
||||
?: attrColor(R.attr.colorLink)
|
||||
|
||||
CustomShare.reloadCache(this, pref)
|
||||
|
@ -683,7 +683,7 @@ class ActMain : AppCompatActivity(),
|
|||
benchmark("reload timezone") {
|
||||
try {
|
||||
var tz = TimeZone.getDefault()
|
||||
val tzId = Pref.spTimeZone(pref)
|
||||
val tzId = PrefS.spTimeZone(pref)
|
||||
if (tzId.isNotEmpty()) {
|
||||
tz = TimeZone.getTimeZone(tzId)
|
||||
}
|
||||
|
@ -831,7 +831,7 @@ class ActMain : AppCompatActivity(),
|
|||
|
||||
// TODO MyClickableSpan.link_callback = WeakReference(link_click_listener)
|
||||
|
||||
if (Pref.bpDontScreenOff(pref)) {
|
||||
if (PrefB.bpDontScreenOff(pref)) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
|
@ -859,7 +859,7 @@ class ActMain : AppCompatActivity(),
|
|||
{ env -> env.pager.currentItem },
|
||||
{ env -> env.visibleColumnsIndices.first })
|
||||
log.d("ipLastColumnPos save $lastPos")
|
||||
pref.edit().put(Pref.ipLastColumnPos, lastPos).apply()
|
||||
pref.edit().put(PrefI.ipLastColumnPos, lastPos).apply()
|
||||
|
||||
appState.columnList.forEach { it.saveScrollPosition() }
|
||||
|
||||
|
@ -1004,8 +1004,8 @@ class ActMain : AppCompatActivity(),
|
|||
this.postedRedraftId = null
|
||||
}
|
||||
|
||||
val refreshAfterToot = Pref.ipRefreshAfterToot(pref)
|
||||
if (refreshAfterToot != Pref.RAT_DONT_REFRESH) {
|
||||
val refreshAfterToot = PrefI.ipRefreshAfterToot(pref)
|
||||
if (refreshAfterToot != PrefI.RAT_DONT_REFRESH) {
|
||||
appState.columnList
|
||||
.filter { it.accessInfo.acct == postedAcct }
|
||||
.forEach {
|
||||
|
@ -1046,7 +1046,7 @@ class ActMain : AppCompatActivity(),
|
|||
|
||||
private fun performQuickPost(account: SavedAccount?) {
|
||||
if (account == null) {
|
||||
val a = if (tabletEnv != null && !Pref.bpQuickTootOmitAccountSelection(pref)) {
|
||||
val a = if (tabletEnv != null && !PrefB.bpQuickTootOmitAccountSelection(pref)) {
|
||||
// タブレットモードでオプションが無効なら
|
||||
// 簡易投稿は常にアカウント選択する
|
||||
null
|
||||
|
@ -1150,19 +1150,19 @@ class ActMain : AppCompatActivity(),
|
|||
}
|
||||
|
||||
// カラムが1個以上ある場合は設定に合わせて挙動を変える
|
||||
when (Pref.ipBackButtonAction(pref)) {
|
||||
when (PrefI.ipBackButtonAction(pref)) {
|
||||
|
||||
Pref.BACK_EXIT_APP -> this@ActMain.finish()
|
||||
PrefI.BACK_EXIT_APP -> this@ActMain.finish()
|
||||
|
||||
Pref.BACK_OPEN_COLUMN_LIST -> openColumnList()
|
||||
PrefI.BACK_OPEN_COLUMN_LIST -> openColumnList()
|
||||
|
||||
Pref.BACK_CLOSE_COLUMN -> {
|
||||
PrefI.BACK_CLOSE_COLUMN -> {
|
||||
|
||||
val closeableColumnList = getClosableColumnList()
|
||||
when (closeableColumnList.size) {
|
||||
0 -> {
|
||||
if (Pref.bpExitAppWhenCloseProtectedColumn(pref) &&
|
||||
Pref.bpDontConfirmBeforeCloseColumn(pref)
|
||||
if (PrefB.bpExitAppWhenCloseProtectedColumn(pref) &&
|
||||
PrefB.bpDontConfirmBeforeCloseColumn(pref)
|
||||
) {
|
||||
this@ActMain.finish()
|
||||
} else {
|
||||
|
@ -1209,12 +1209,12 @@ class ActMain : AppCompatActivity(),
|
|||
App1.initEdgeToEdge(this)
|
||||
|
||||
quickTootVisibility =
|
||||
TootVisibility.parseSavedVisibility(Pref.spQuickTootVisibility(pref))
|
||||
TootVisibility.parseSavedVisibility(PrefS.spQuickTootVisibility(pref))
|
||||
?: quickTootVisibility
|
||||
|
||||
Column.reloadDefaultColor(this, pref)
|
||||
|
||||
var sv = Pref.spTimelineFont(pref)
|
||||
var sv = PrefS.spTimelineFont(pref)
|
||||
if (sv.isNotEmpty()) {
|
||||
try {
|
||||
timelineFont = Typeface.createFromFile(sv)
|
||||
|
@ -1223,7 +1223,7 @@ class ActMain : AppCompatActivity(),
|
|||
}
|
||||
}
|
||||
|
||||
sv = Pref.spTimelineFontBold(pref)
|
||||
sv = PrefS.spTimelineFontBold(pref)
|
||||
if (sv.isNotEmpty()) {
|
||||
try {
|
||||
timeline_font_bold = Typeface.createFromFile(sv)
|
||||
|
@ -1252,21 +1252,21 @@ class ActMain : AppCompatActivity(),
|
|||
return (0.5f + iconSizeDp * density).toInt()
|
||||
}
|
||||
|
||||
avatarIconSize = parseIconSize(Pref.spAvatarIconSize)
|
||||
notificationTlIconSize = parseIconSize(Pref.spNotificationTlIconSize)
|
||||
boostButtonSize = parseIconSize(Pref.spBoostButtonSize)
|
||||
replyIconSize = parseIconSize(Pref.spReplyIconSize)
|
||||
headerIconSize = parseIconSize(Pref.spHeaderIconSize)
|
||||
stripIconSize = parseIconSize(Pref.spStripIconSize)
|
||||
screenBottomPadding = parseIconSize(Pref.spScreenBottomPadding, minDp = 0f)
|
||||
avatarIconSize = parseIconSize(PrefS.spAvatarIconSize)
|
||||
notificationTlIconSize = parseIconSize(PrefS.spNotificationTlIconSize)
|
||||
boostButtonSize = parseIconSize(PrefS.spBoostButtonSize)
|
||||
replyIconSize = parseIconSize(PrefS.spReplyIconSize)
|
||||
headerIconSize = parseIconSize(PrefS.spHeaderIconSize)
|
||||
stripIconSize = parseIconSize(PrefS.spStripIconSize)
|
||||
screenBottomPadding = parseIconSize(PrefS.spScreenBottomPadding, minDp = 0f)
|
||||
|
||||
run {
|
||||
var roundRatio = 33f
|
||||
try {
|
||||
if (Pref.bpDontRound(pref)) {
|
||||
if (PrefB.bpDontRound(pref)) {
|
||||
roundRatio = 0f
|
||||
} else {
|
||||
sv = Pref.spRoundRatio(pref)
|
||||
sv = PrefS.spRoundRatio(pref)
|
||||
if (sv.isNotEmpty()) {
|
||||
val fv = sv.toFloat()
|
||||
if (fv.isFinite()) {
|
||||
|
@ -1283,7 +1283,7 @@ class ActMain : AppCompatActivity(),
|
|||
run {
|
||||
var boostAlpha = 0.8f
|
||||
try {
|
||||
val f = (Pref.spBoostAlpha.toInt(pref).toFloat() + 0.5f) / 100f
|
||||
val f = (PrefS.spBoostAlpha.toInt(pref).toFloat() + 0.5f) / 100f
|
||||
boostAlpha = when {
|
||||
f >= 1f -> 1f
|
||||
f < 0f -> 0.66f
|
||||
|
@ -1321,8 +1321,8 @@ class ActMain : AppCompatActivity(),
|
|||
|
||||
etQuickToot.typeface = timelineFont
|
||||
|
||||
when (Pref.ipJustifyWindowContentPortrait(pref)) {
|
||||
Pref.JWCP_START -> {
|
||||
when (PrefI.ipJustifyWindowContentPortrait(pref)) {
|
||||
PrefI.JWCP_START -> {
|
||||
val iconW = (stripIconSize * 1.5f + 0.5f).toInt()
|
||||
val padding = resources.displayMetrics.widthPixels / 2 - iconW
|
||||
|
||||
|
@ -1339,7 +1339,7 @@ class ActMain : AppCompatActivity(),
|
|||
)
|
||||
}
|
||||
|
||||
Pref.JWCP_END -> {
|
||||
PrefI.JWCP_END -> {
|
||||
val iconW = (stripIconSize * 1.5f + 0.5f).toInt()
|
||||
val borderWidth = (1f * density + 0.5f).toInt()
|
||||
val padding = resources.displayMetrics.widthPixels / 2 - iconW - borderWidth
|
||||
|
@ -1358,7 +1358,7 @@ class ActMain : AppCompatActivity(),
|
|||
}
|
||||
}
|
||||
|
||||
if (!Pref.bpQuickTootBar(pref)) {
|
||||
if (!PrefB.bpQuickTootBar(pref)) {
|
||||
llQuickTootBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
@ -1367,7 +1367,7 @@ class ActMain : AppCompatActivity(),
|
|||
btnQuickToot.setOnClickListener(this)
|
||||
btnQuickTootMenu.setOnClickListener(this)
|
||||
|
||||
if (Pref.bpDontUseActionButtonWithQuickTootBar(pref)) {
|
||||
if (PrefB.bpDontUseActionButtonWithQuickTootBar(pref)) {
|
||||
etQuickToot.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
||||
etQuickToot.imeOptions = EditorInfo.IME_ACTION_NONE
|
||||
// 最後に指定する必要がある?
|
||||
|
@ -1397,7 +1397,7 @@ class ActMain : AppCompatActivity(),
|
|||
val density = dm.density
|
||||
|
||||
var mediaThumbHeightDp = 64
|
||||
sv = Pref.spMediaThumbHeight(pref)
|
||||
sv = PrefS.spMediaThumbHeight(pref)
|
||||
if (sv.isNotEmpty()) {
|
||||
try {
|
||||
val iv = Integer.parseInt(sv)
|
||||
|
@ -1411,7 +1411,7 @@ class ActMain : AppCompatActivity(),
|
|||
appState.mediaThumbHeight = (0.5f + mediaThumbHeightDp * density).toInt()
|
||||
|
||||
var columnWMinDp = COLUMN_WIDTH_MIN_DP
|
||||
sv = Pref.spColumnWidth(pref)
|
||||
sv = PrefS.spColumnWidth(pref)
|
||||
if (sv.isNotEmpty()) {
|
||||
try {
|
||||
val iv = Integer.parseInt(sv)
|
||||
|
@ -1426,7 +1426,7 @@ class ActMain : AppCompatActivity(),
|
|||
|
||||
val sw = dm.widthPixels
|
||||
|
||||
if (Pref.bpDisableTabletMode(pref) || sw < columnWMin * 2) {
|
||||
if (PrefB.bpDisableTabletMode(pref) || sw < columnWMin * 2) {
|
||||
// SmartPhone mode
|
||||
phoneEnv = PhoneEnv()
|
||||
} else {
|
||||
|
@ -1571,7 +1571,7 @@ class ActMain : AppCompatActivity(),
|
|||
viewRoot.tag = index
|
||||
viewRoot.setOnClickListener { v ->
|
||||
val idx = v.tag as Int
|
||||
if (Pref.bpScrollTopFromColumnStrip(pref) && isVisibleColumn(idx)) {
|
||||
if (PrefB.bpScrollTopFromColumnStrip(pref) && isVisibleColumn(idx)) {
|
||||
column.viewHolder?.scrollToTop2()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
@ -1871,7 +1871,7 @@ class ActMain : AppCompatActivity(),
|
|||
)
|
||||
|
||||
client.authentication2Misskey(
|
||||
Pref.spClientName(pref),
|
||||
PrefS.spClientName(pref),
|
||||
token,
|
||||
ti.misskeyVersion
|
||||
)?.also {
|
||||
|
@ -1936,7 +1936,7 @@ class ActMain : AppCompatActivity(),
|
|||
|
||||
val refToken = AtomicReference<String>(null)
|
||||
client.authentication2Mastodon(
|
||||
Pref.spClientName(pref),
|
||||
PrefS.spClientName(pref),
|
||||
code,
|
||||
outAccessToken = refToken
|
||||
)?.also { result ->
|
||||
|
@ -2116,7 +2116,7 @@ class ActMain : AppCompatActivity(),
|
|||
return
|
||||
}
|
||||
|
||||
if (!bConfirmed && !Pref.bpDontConfirmBeforeCloseColumn(pref)) {
|
||||
if (!bConfirmed && !PrefB.bpDontConfirmBeforeCloseColumn(pref)) {
|
||||
AlertDialog.Builder(this)
|
||||
.setMessage(R.string.confirm_close_column)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
|
@ -2200,7 +2200,7 @@ class ActMain : AppCompatActivity(),
|
|||
vararg params: Any,
|
||||
): Column {
|
||||
return addColumn(
|
||||
Pref.bpAllowColumnDuplication(pref),
|
||||
PrefB.bpAllowColumnDuplication(pref),
|
||||
indexArg,
|
||||
ai,
|
||||
type,
|
||||
|
@ -2242,11 +2242,11 @@ class ActMain : AppCompatActivity(),
|
|||
|
||||
fun showFooterColor() {
|
||||
|
||||
val footerButtonBgColor = Pref.ipFooterButtonBgColor(pref)
|
||||
val footerButtonFgColor = Pref.ipFooterButtonFgColor(pref)
|
||||
val footerTabBgColor = Pref.ipFooterTabBgColor(pref)
|
||||
val footerTabDividerColor = Pref.ipFooterTabDividerColor(pref)
|
||||
val footerTabIndicatorColor = Pref.ipFooterTabIndicatorColor(pref)
|
||||
val footerButtonBgColor = PrefI.ipFooterButtonBgColor(pref)
|
||||
val footerButtonFgColor = PrefI.ipFooterButtonFgColor(pref)
|
||||
val footerTabBgColor = PrefI.ipFooterTabBgColor(pref)
|
||||
val footerTabDividerColor = PrefI.ipFooterTabDividerColor(pref)
|
||||
val footerTabIndicatorColor = PrefI.ipFooterTabIndicatorColor(pref)
|
||||
|
||||
val colorColumnStripBackground = footerTabBgColor.notZero()
|
||||
?: attrColor(R.attr.colorColumnStripBackground)
|
||||
|
@ -2377,7 +2377,7 @@ class ActMain : AppCompatActivity(),
|
|||
private fun resizeColumnWidth(env: TabletEnv) {
|
||||
|
||||
var columnWMinDp = COLUMN_WIDTH_MIN_DP
|
||||
val sv = Pref.spColumnWidth(pref)
|
||||
val sv = PrefS.spColumnWidth(pref)
|
||||
if (sv.isNotEmpty()) {
|
||||
try {
|
||||
val iv = Integer.parseInt(sv)
|
||||
|
@ -2618,7 +2618,7 @@ class ActMain : AppCompatActivity(),
|
|||
}
|
||||
|
||||
private fun resizeAutoCW(columnW: Int) {
|
||||
val sv = Pref.spAutoCWLines(pref)
|
||||
val sv = PrefS.spAutoCWLines(pref)
|
||||
nAutoCwLines = sv.optInt() ?: -1
|
||||
if (nAutoCwLines > 0) {
|
||||
val lvPad = (0.5f + 12 * density).toInt()
|
||||
|
@ -2720,7 +2720,7 @@ class ActMain : AppCompatActivity(),
|
|||
|
||||
// 同意ずみなら表示しない
|
||||
val digest = bytes.digestSHA256().encodeBase64Url()
|
||||
if (digest == Pref.spAgreedPrivacyPolicyDigest(pref)) return
|
||||
if (digest == PrefS.spAgreedPrivacyPolicyDigest(pref)) return
|
||||
|
||||
val dialog = AlertDialog.Builder(this)
|
||||
.setTitle(R.string.privacy_policy)
|
||||
|
@ -2732,7 +2732,7 @@ class ActMain : AppCompatActivity(),
|
|||
finish()
|
||||
}
|
||||
.setPositiveButton(R.string.agree) { _, _ ->
|
||||
pref.edit().put(Pref.spAgreedPrivacyPolicyDigest, digest).apply()
|
||||
pref.edit().put(PrefS.spAgreedPrivacyPolicyDigest, digest).apply()
|
||||
}
|
||||
.create()
|
||||
dlgPrivacyPolicy = WeakReference(dialog)
|
||||
|
|
|
@ -84,7 +84,7 @@ class ActPost : AppCompatActivity(),
|
|||
internal const val KEY_REPLY_STATUS = "reply_status"
|
||||
internal const val KEY_REDRAFT_STATUS = "redraft_status"
|
||||
internal const val KEY_INITIAL_TEXT = "initial_text"
|
||||
internal const val KEY_SENT_INTENT = "sent_intent"
|
||||
internal const val KEY_SHARED_INTENT = "sent_intent"
|
||||
internal const val KEY_QUOTE = "quote"
|
||||
internal const val KEY_SCHEDULED_STATUS = "scheduled_status"
|
||||
|
||||
|
@ -356,7 +356,7 @@ class ActPost : AppCompatActivity(),
|
|||
}
|
||||
|
||||
if (sharedIntent != null) {
|
||||
putExtra(KEY_SENT_INTENT, sharedIntent)
|
||||
putExtra(KEY_SHARED_INTENT, sharedIntent)
|
||||
}
|
||||
|
||||
if (scheduledStatus != null) {
|
||||
|
@ -740,7 +740,7 @@ class ActPost : AppCompatActivity(),
|
|||
setContentView(R.layout.act_post)
|
||||
App1.initEdgeToEdge(this)
|
||||
|
||||
if (Pref.bpPostButtonBarTop(this)) {
|
||||
if (PrefB.bpPostButtonBarTop(this)) {
|
||||
val bar = findViewById<View>(R.id.llFooterBar)
|
||||
val parent = bar.parent as ViewGroup
|
||||
parent.removeView(bar)
|
||||
|
@ -2061,7 +2061,7 @@ class ActPost : AppCompatActivity(),
|
|||
}
|
||||
lastAttachmentComplete = now
|
||||
|
||||
if (Pref.bpAppendAttachmentUrlToContent(pref)) {
|
||||
if (PrefB.bpAppendAttachmentUrlToContent(pref)) {
|
||||
// 投稿欄の末尾に追記する
|
||||
val selStart = etContent.selectionStart
|
||||
val selEnd = etContent.selectionEnd
|
||||
|
@ -3046,13 +3046,42 @@ class ActPost : AppCompatActivity(),
|
|||
accountList.find { it.db_id == accountDbId }?.let { selectAccount(it) }
|
||||
}
|
||||
|
||||
val sentIntent = intent.getParcelableExtra<Intent>(KEY_SENT_INTENT)
|
||||
if (sentIntent != null) {
|
||||
val sharedIntent = intent.getParcelableExtra<Intent>(KEY_SHARED_INTENT)
|
||||
if (sharedIntent != null) {
|
||||
initializeFromSharedIntent(sharedIntent)
|
||||
}
|
||||
|
||||
val hasUri = when (sentIntent.action) {
|
||||
appendContentText(intent.getStringExtra(KEY_INITIAL_TEXT))
|
||||
|
||||
val account = this.account
|
||||
|
||||
if (account != null) {
|
||||
intent.getStringExtra(KEY_REPLY_STATUS)
|
||||
?.let { initializeFromReplyStatus(account, it) }
|
||||
}
|
||||
|
||||
appendContentText(account?.default_text, selectBefore = true)
|
||||
cbNSFW.isChecked = account?.default_sensitive ?: false
|
||||
|
||||
if (account != null) {
|
||||
// 再編集
|
||||
intent.getStringExtra(KEY_REDRAFT_STATUS)
|
||||
?.let { initializeFromRedraftStatus(account, it) }
|
||||
|
||||
// 予約編集の再編集
|
||||
intent.getStringExtra(KEY_SCHEDULED_STATUS)
|
||||
?.let { initializeFromScheduledStatus(account, it) }
|
||||
}
|
||||
|
||||
afterUpdateText()
|
||||
}
|
||||
|
||||
private fun initializeFromSharedIntent(sharedIntent: Intent) {
|
||||
try {
|
||||
val hasUri = when (sharedIntent.action) {
|
||||
Intent.ACTION_VIEW -> {
|
||||
val uri = sentIntent.data
|
||||
val type = sentIntent.type
|
||||
val uri = sharedIntent.data
|
||||
val type = sharedIntent.type
|
||||
if (uri != null) {
|
||||
addAttachment(uri, type)
|
||||
true
|
||||
|
@ -3062,8 +3091,8 @@ class ActPost : AppCompatActivity(),
|
|||
}
|
||||
|
||||
Intent.ACTION_SEND -> {
|
||||
val uri = sentIntent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
val type = sentIntent.type
|
||||
val uri = sharedIntent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
val type = sharedIntent.type
|
||||
if (uri != null) {
|
||||
addAttachment(uri, type)
|
||||
true
|
||||
|
@ -3074,7 +3103,7 @@ class ActPost : AppCompatActivity(),
|
|||
|
||||
Intent.ACTION_SEND_MULTIPLE -> {
|
||||
val listUri =
|
||||
sentIntent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
sharedIntent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
?.filterNotNull()
|
||||
if (listUri?.isNotEmpty() == true) {
|
||||
for (uri in listUri) {
|
||||
|
@ -3089,279 +3118,262 @@ class ActPost : AppCompatActivity(),
|
|||
else -> false
|
||||
}
|
||||
|
||||
if (!hasUri || !Pref.bpIgnoreTextInSharedMedia(pref)) {
|
||||
appendContentText(sentIntent)
|
||||
if (!hasUri || !PrefB.bpIgnoreTextInSharedMedia(pref)) {
|
||||
appendContentText(sharedIntent)
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
appendContentText(intent.getStringExtra(KEY_INITIAL_TEXT))
|
||||
private fun initializeFromReplyStatus(account: SavedAccount, jsonText: String) {
|
||||
try {
|
||||
val replyStatus =
|
||||
TootParser(this@ActPost, account).status(jsonText.decodeJsonObject())
|
||||
?: error("initializeFromReplyStatus: parse failed.")
|
||||
|
||||
val account = this.account
|
||||
val isQuote = intent.getBooleanExtra(KEY_QUOTE, false)
|
||||
if (isQuote) {
|
||||
cbQuote.isChecked = true
|
||||
|
||||
var sv = intent.getStringExtra(KEY_REPLY_STATUS)
|
||||
if (sv != null && account != null) {
|
||||
// 引用リノートはCWやメンションを引き継がない
|
||||
} else {
|
||||
|
||||
// CW をリプライ元に合わせる
|
||||
if (replyStatus.spoiler_text.isNotEmpty()) {
|
||||
cbContentWarning.isChecked = true
|
||||
etContentWarning.setText(replyStatus.spoiler_text)
|
||||
}
|
||||
|
||||
// 新しいメンションリスト
|
||||
val mentionList = ArrayList<Acct>()
|
||||
|
||||
// 自己レス以外なら元レスへのメンションを追加
|
||||
// 最初に追加する https://github.com/tateisu/SubwayTooter/issues/94
|
||||
if (!account.isMe(replyStatus.account)) {
|
||||
mentionList.add(account.getFullAcct(replyStatus.account))
|
||||
}
|
||||
|
||||
// 元レスに含まれていたメンションを複製
|
||||
replyStatus.mentions?.forEach { mention ->
|
||||
|
||||
val whoAcct = mention.acct
|
||||
|
||||
// 空データなら追加しない
|
||||
if (!whoAcct.isValid) return@forEach
|
||||
|
||||
// 自分なら追加しない
|
||||
if (account.isMe(whoAcct)) return@forEach
|
||||
|
||||
// 既出でないなら追加する
|
||||
val acct = account.getFullAcct(whoAcct)
|
||||
if (!mentionList.contains(acct)) mentionList.add(acct)
|
||||
}
|
||||
|
||||
if (mentionList.isNotEmpty()) {
|
||||
appendContentText(
|
||||
StringBuilder().apply {
|
||||
for (acct in mentionList) {
|
||||
if (isNotEmpty()) append(' ')
|
||||
append("@${acct.ascii}")
|
||||
}
|
||||
append(' ')
|
||||
}.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// リプライ表示をつける
|
||||
inReplyToId = replyStatus.id
|
||||
inReplyToText = replyStatus.content
|
||||
inReplyToImage = replyStatus.account.avatar_static
|
||||
inReplyToUrl = replyStatus.url
|
||||
|
||||
// 公開範囲
|
||||
try {
|
||||
val replyStatus =
|
||||
TootParser(this@ActPost, account).status(sv.decodeJsonObject())
|
||||
// 比較する前にデフォルトの公開範囲を計算する
|
||||
|
||||
val isQuote = intent.getBooleanExtra(KEY_QUOTE, false)
|
||||
visibility = visibility
|
||||
?: account.visibility
|
||||
// ?: TootVisibility.Public
|
||||
// VISIBILITY_WEB_SETTING だと 1.5未満のタンスでトラブルになる
|
||||
|
||||
if (replyStatus != null) {
|
||||
if (visibility == TootVisibility.Unknown) {
|
||||
visibility = TootVisibility.PrivateFollowers
|
||||
}
|
||||
|
||||
if (isQuote) {
|
||||
cbQuote.isChecked = true
|
||||
val sample = when (val base = replyStatus.visibility) {
|
||||
TootVisibility.Unknown -> TootVisibility.PrivateFollowers
|
||||
else -> base
|
||||
}
|
||||
|
||||
// 引用リノートはCWやメンションを引き継がない
|
||||
} else {
|
||||
|
||||
// CW をリプライ元に合わせる
|
||||
if (replyStatus.spoiler_text.isNotEmpty()) {
|
||||
cbContentWarning.isChecked = true
|
||||
etContentWarning.setText(replyStatus.spoiler_text)
|
||||
}
|
||||
|
||||
// 新しいメンションリスト
|
||||
val mentionList = ArrayList<Acct>()
|
||||
|
||||
// 自己レス以外なら元レスへのメンションを追加
|
||||
// 最初に追加する https://github.com/tateisu/SubwayTooter/issues/94
|
||||
if (!account.isMe(replyStatus.account)) {
|
||||
mentionList.add(account.getFullAcct(replyStatus.account))
|
||||
}
|
||||
|
||||
// 元レスに含まれていたメンションを複製
|
||||
replyStatus.mentions?.forEach { mention ->
|
||||
|
||||
val whoAcct = mention.acct
|
||||
|
||||
// 空データなら追加しない
|
||||
if (!whoAcct.isValid) return@forEach
|
||||
|
||||
// 自分なら追加しない
|
||||
if (account.isMe(whoAcct)) return@forEach
|
||||
|
||||
// 既出でないなら追加する
|
||||
val acct = account.getFullAcct(whoAcct)
|
||||
if (!mentionList.contains(acct)) mentionList.add(acct)
|
||||
}
|
||||
|
||||
if (mentionList.isNotEmpty()) {
|
||||
appendContentText(
|
||||
StringBuilder().apply {
|
||||
for (acct in mentionList) {
|
||||
if (isNotEmpty()) append(' ')
|
||||
append("@${acct.ascii}")
|
||||
}
|
||||
append(' ')
|
||||
}.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// リプライ表示をつける
|
||||
inReplyToId = replyStatus.id
|
||||
inReplyToText = replyStatus.content
|
||||
inReplyToImage = replyStatus.account.avatar_static
|
||||
inReplyToUrl = replyStatus.url
|
||||
|
||||
// 公開範囲
|
||||
try {
|
||||
// 比較する前にデフォルトの公開範囲を計算する
|
||||
|
||||
visibility = visibility
|
||||
?: account.visibility
|
||||
// ?: TootVisibility.Public
|
||||
// VISIBILITY_WEB_SETTING だと 1.5未満のタンスでトラブルになる
|
||||
|
||||
if (visibility == TootVisibility.Unknown) {
|
||||
visibility = TootVisibility.PrivateFollowers
|
||||
}
|
||||
|
||||
val sample = when (val base = replyStatus.visibility) {
|
||||
TootVisibility.Unknown -> TootVisibility.PrivateFollowers
|
||||
else -> base
|
||||
}
|
||||
|
||||
if (TootVisibility.WebSetting == visibility) {
|
||||
// 「Web設定に合わせる」だった場合は無条件にリプライ元の公開範囲に変更する
|
||||
this.visibility = sample
|
||||
} else if (TootVisibility.isVisibilitySpoilRequired(
|
||||
this.visibility, sample
|
||||
)
|
||||
) {
|
||||
// デフォルトの方が公開範囲が大きい場合、リプライ元に合わせて公開範囲を狭める
|
||||
this.visibility = sample
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
if (TootVisibility.WebSetting == visibility) {
|
||||
// 「Web設定に合わせる」だった場合は無条件にリプライ元の公開範囲に変更する
|
||||
this.visibility = sample
|
||||
} else if (TootVisibility.isVisibilitySpoilRequired(
|
||||
this.visibility, sample
|
||||
)
|
||||
) {
|
||||
// デフォルトの方が公開範囲が大きい場合、リプライ元に合わせて公開範囲を狭める
|
||||
this.visibility = sample
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
appendContentText(account?.default_text, selectBefore = true)
|
||||
private fun initializeFromRedraftStatus(account: SavedAccount, jsonText: String) {
|
||||
try {
|
||||
val baseStatus =
|
||||
TootParser(this@ActPost, account).status(jsonText.decodeJsonObject())
|
||||
?: error("initializeFromRedraftStatus: parse failed.")
|
||||
|
||||
cbNSFW.isChecked = account?.default_sensitive ?: false
|
||||
redraftStatusId = baseStatus.id
|
||||
|
||||
// 再編集
|
||||
sv = intent.getStringExtra(KEY_REDRAFT_STATUS)
|
||||
if (sv != null && account != null) {
|
||||
try {
|
||||
val baseStatus =
|
||||
TootParser(this@ActPost, account).status(sv.decodeJsonObject())
|
||||
if (baseStatus != null) {
|
||||
this.visibility = baseStatus.visibility
|
||||
|
||||
redraftStatusId = baseStatus.id
|
||||
|
||||
this.visibility = baseStatus.visibility
|
||||
|
||||
val srcAttachments = baseStatus.media_attachments
|
||||
if (srcAttachments?.isNotEmpty() == true) {
|
||||
updateStateAttachmentList()
|
||||
this.attachmentList.clear()
|
||||
try {
|
||||
for (src in srcAttachments) {
|
||||
if (src is TootAttachment) {
|
||||
src.redraft = true
|
||||
val pa = PostAttachment(src)
|
||||
pa.status = PostAttachment.STATUS_UPLOADED
|
||||
this.attachmentList.add(pa)
|
||||
}
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
val srcAttachments = baseStatus.media_attachments
|
||||
if (srcAttachments?.isNotEmpty() == true) {
|
||||
updateStateAttachmentList()
|
||||
this.attachmentList.clear()
|
||||
try {
|
||||
for (src in srcAttachments) {
|
||||
if (src is TootAttachment) {
|
||||
src.redraft = true
|
||||
val pa = PostAttachment(src)
|
||||
pa.status = PostAttachment.STATUS_UPLOADED
|
||||
this.attachmentList.add(pa)
|
||||
}
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
cbNSFW.isChecked = baseStatus.sensitive == true
|
||||
cbNSFW.isChecked = baseStatus.sensitive == true
|
||||
|
||||
// 再編集の場合はdefault_textは反映されない
|
||||
// 再編集の場合はdefault_textは反映されない
|
||||
|
||||
val decodeOptions = DecodeOptions(
|
||||
this,
|
||||
mentionFullAcct = true,
|
||||
mentions = baseStatus.mentions,
|
||||
mentionDefaultHostDomain = account
|
||||
val decodeOptions = DecodeOptions(
|
||||
this,
|
||||
mentionFullAcct = true,
|
||||
mentions = baseStatus.mentions,
|
||||
mentionDefaultHostDomain = account
|
||||
)
|
||||
|
||||
var text: CharSequence = if (account.isMisskey) {
|
||||
baseStatus.content ?: ""
|
||||
} else {
|
||||
decodeOptions.decodeHTML(baseStatus.content)
|
||||
}
|
||||
etContent.setText(text)
|
||||
etContent.setSelection(text.length)
|
||||
|
||||
text = decodeOptions.decodeEmoji(baseStatus.spoiler_text)
|
||||
etContentWarning.setText(text)
|
||||
etContentWarning.setSelection(text.length)
|
||||
cbContentWarning.isChecked = text.isNotEmpty()
|
||||
|
||||
val srcEnquete = baseStatus.enquete
|
||||
val srcItems = srcEnquete?.items
|
||||
when {
|
||||
srcItems == null -> {
|
||||
//
|
||||
}
|
||||
|
||||
srcEnquete.pollType == TootPollsType.FriendsNico &&
|
||||
srcEnquete.type != TootPolls.TYPE_ENQUETE -> {
|
||||
// フレニコAPIのアンケート結果は再編集の対象外
|
||||
}
|
||||
|
||||
else -> {
|
||||
spEnquete.setSelection(
|
||||
if (srcEnquete.pollType == TootPollsType.FriendsNico) {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
}
|
||||
)
|
||||
|
||||
var text: CharSequence = if (account.isMisskey) {
|
||||
baseStatus.content ?: ""
|
||||
} else {
|
||||
decodeOptions.decodeHTML(baseStatus.content)
|
||||
}
|
||||
etContent.setText(text)
|
||||
text = decodeOptions.decodeHTML(srcEnquete.question)
|
||||
etContent.text = text
|
||||
etContent.setSelection(text.length)
|
||||
|
||||
text = decodeOptions.decodeEmoji(baseStatus.spoiler_text)
|
||||
etContentWarning.setText(text)
|
||||
etContentWarning.setSelection(text.length)
|
||||
cbContentWarning.isChecked = text.isNotEmpty()
|
||||
|
||||
val srcEnquete = baseStatus.enquete
|
||||
val srcItems = srcEnquete?.items
|
||||
when {
|
||||
srcItems == null -> {
|
||||
//
|
||||
}
|
||||
|
||||
srcEnquete.pollType == TootPollsType.FriendsNico && srcEnquete.type != TootPolls.TYPE_ENQUETE -> {
|
||||
// フレニコAPIのアンケート結果は再編集の対象外
|
||||
}
|
||||
|
||||
else -> {
|
||||
spEnquete.setSelection(
|
||||
if (srcEnquete.pollType == TootPollsType.FriendsNico) {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
var srcIndex = 0
|
||||
for (et in etChoices) {
|
||||
if (srcIndex < srcItems.size) {
|
||||
val choice = srcItems[srcIndex]
|
||||
when {
|
||||
srcIndex == srcItems.size - 1 && choice.text == "\uD83E\uDD14" -> {
|
||||
// :thinking_face: は再現しない
|
||||
}
|
||||
)
|
||||
text = decodeOptions.decodeHTML(srcEnquete.question)
|
||||
etContent.text = text
|
||||
etContent.setSelection(text.length)
|
||||
|
||||
var srcIndex = 0
|
||||
loop@ for (et in etChoices) {
|
||||
if (srcIndex < srcItems.size) {
|
||||
val choice = srcItems[srcIndex]
|
||||
when {
|
||||
srcIndex == srcItems.size - 1 && choice.text == "\uD83E\uDD14" -> {
|
||||
// :thinking_face: は再現しない
|
||||
}
|
||||
|
||||
else -> {
|
||||
et.setText(decodeOptions.decodeEmoji(choice.text))
|
||||
++srcIndex
|
||||
continue@loop
|
||||
}
|
||||
}
|
||||
}
|
||||
et.setText("")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
// 予約編集の再編集
|
||||
sv = intent.getStringExtra(KEY_SCHEDULED_STATUS)
|
||||
if (sv != null && account != null) {
|
||||
try {
|
||||
val item = parseItem(
|
||||
::TootScheduled,
|
||||
TootParser(this@ActPost, account),
|
||||
sv.decodeJsonObject(),
|
||||
log
|
||||
)
|
||||
if (item != null) {
|
||||
scheduledStatus = item
|
||||
|
||||
timeSchedule = item.timeScheduledAt
|
||||
|
||||
val text = item.text
|
||||
etContent.setText(text)
|
||||
|
||||
val cw = item.spoilerText
|
||||
if (cw?.isNotEmpty() == true) {
|
||||
etContentWarning.setText(cw)
|
||||
cbContentWarning.isChecked = true
|
||||
} else {
|
||||
cbContentWarning.isChecked = false
|
||||
}
|
||||
visibility = item.visibility
|
||||
|
||||
// 2019/1/7 どうも添付データを古い投稿から引き継げないようだ…。
|
||||
// 2019/1/22 https://github.com/tootsuite/mastodon/pull/9894 で直った。
|
||||
val srcAttachments = item.mediaAttachments
|
||||
if (srcAttachments?.isNotEmpty() == true) {
|
||||
updateStateAttachmentList()
|
||||
this.attachmentList.clear()
|
||||
try {
|
||||
for (src in srcAttachments) {
|
||||
if (src is TootAttachment) {
|
||||
src.redraft = true
|
||||
val pa = PostAttachment(src)
|
||||
pa.status = PostAttachment.STATUS_UPLOADED
|
||||
this.attachmentList.add(pa)
|
||||
else -> {
|
||||
et.setText(decodeOptions.decodeEmoji(choice.text))
|
||||
++srcIndex
|
||||
continue
|
||||
}
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
et.setText("")
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeFromScheduledStatus(account: SavedAccount, jsonText: String) {
|
||||
try {
|
||||
val item = parseItem(
|
||||
::TootScheduled,
|
||||
TootParser(this@ActPost, account),
|
||||
jsonText.decodeJsonObject(),
|
||||
log
|
||||
) ?: error("initializeFromScheduledStatus: parse failed.")
|
||||
|
||||
scheduledStatus = item
|
||||
|
||||
timeSchedule = item.timeScheduledAt
|
||||
|
||||
val text = item.text
|
||||
etContent.setText(text)
|
||||
|
||||
val cw = item.spoilerText
|
||||
if (cw?.isNotEmpty() == true) {
|
||||
etContentWarning.setText(cw)
|
||||
cbContentWarning.isChecked = true
|
||||
} else {
|
||||
cbContentWarning.isChecked = false
|
||||
}
|
||||
visibility = item.visibility
|
||||
|
||||
// 2019/1/7 どうも添付データを古い投稿から引き継げないようだ…。
|
||||
// 2019/1/22 https://github.com/tootsuite/mastodon/pull/9894 で直った。
|
||||
val srcAttachments = item.mediaAttachments
|
||||
if (srcAttachments?.isNotEmpty() == true) {
|
||||
updateStateAttachmentList()
|
||||
this.attachmentList.clear()
|
||||
try {
|
||||
for (src in srcAttachments) {
|
||||
if (src is TootAttachment) {
|
||||
src.redraft = true
|
||||
val pa = PostAttachment(src)
|
||||
pa.status = PostAttachment.STATUS_UPLOADED
|
||||
this.attachmentList.add(pa)
|
||||
}
|
||||
}
|
||||
cbNSFW.isChecked = item.sensitive
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
cbNSFW.isChecked = item.sensitive
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
afterUpdateText()
|
||||
}
|
||||
|
||||
override fun onMyClickableSpanClicked(viewClicked: View, span: MyClickableSpan) {
|
||||
|
|
|
@ -216,7 +216,7 @@ class App1 : Application() {
|
|||
"SubwayTooter/${BuildConfig.VERSION_NAME} Android/${Build.VERSION.RELEASE}"
|
||||
|
||||
private fun getUserAgent(): String {
|
||||
val userAgentCustom = Pref.spUserAgent(pref)
|
||||
val userAgentCustom = PrefS.spUserAgent(pref)
|
||||
return when {
|
||||
userAgentCustom.isNotEmpty() && !reNotAllowedInUserAgent.matcher(userAgentCustom)
|
||||
.find() -> userAgentCustom
|
||||
|
@ -384,7 +384,7 @@ class App1 : Application() {
|
|||
.build()
|
||||
|
||||
// 内蔵メディアビューア用のHTTP設定はタイムアウトを調整可能
|
||||
val mediaReadTimeout = max(3, Pref.spMediaReadTimeout.toInt(pref))
|
||||
val mediaReadTimeout = max(3, PrefS.spMediaReadTimeout.toInt(pref))
|
||||
ok_http_client_media_viewer = prepareOkHttp(mediaReadTimeout, mediaReadTimeout)
|
||||
.cache(cache)
|
||||
.build()
|
||||
|
@ -513,7 +513,7 @@ class App1 : Application() {
|
|||
|
||||
prepare(activity.applicationContext, "setActivityTheme")
|
||||
|
||||
val theme_idx = Pref.ipUiTheme(pref)
|
||||
val theme_idx = PrefI.ipUiTheme(pref)
|
||||
activity.setTheme(
|
||||
if (forceDark || theme_idx == 1) {
|
||||
if (noActionBar) R.style.AppTheme_Dark_NoActionBar else R.style.AppTheme_Dark
|
||||
|
|
|
@ -269,7 +269,7 @@ object AppDataExporter {
|
|||
continue
|
||||
}
|
||||
|
||||
when (val prefItem = Pref.map[k]) {
|
||||
when (val prefItem = BasePref.allPref[k]) {
|
||||
is BooleanPref -> e.putBoolean(k, reader.nextBoolean())
|
||||
is IntPref -> e.putInt(k, reader.nextInt())
|
||||
is LongPref -> e.putLong(k, reader.nextLong())
|
||||
|
@ -395,10 +395,10 @@ object AppDataExporter {
|
|||
}
|
||||
|
||||
run {
|
||||
val old_id = Pref.lpTabletTootDefaultAccount(app_state.pref)
|
||||
val old_id = PrefL.lpTabletTootDefaultAccount(app_state.pref)
|
||||
if (old_id != -1L) {
|
||||
val new_id = account_id_map[old_id]
|
||||
app_state.pref.edit().put(Pref.lpTabletTootDefaultAccount, new_id ?: -1L).apply()
|
||||
app_state.pref.edit().put(PrefL.lpTabletTootDefaultAccount, new_id ?: -1L).apply()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -206,15 +206,15 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
|
||||
group(R.string.notification_style_before_oreo) {
|
||||
|
||||
checkbox(Pref.bpNotificationSound, R.string.sound) {
|
||||
checkbox(PrefB.bpNotificationSound, R.string.sound) {
|
||||
enabled = Build.VERSION.SDK_INT < 26
|
||||
}
|
||||
|
||||
checkbox(Pref.bpNotificationVibration, R.string.vibration) {
|
||||
checkbox(PrefB.bpNotificationVibration, R.string.vibration) {
|
||||
enabled = Build.VERSION.SDK_INT < 26
|
||||
}
|
||||
|
||||
checkbox(Pref.bpNotificationLED, R.string.led) {
|
||||
checkbox(PrefB.bpNotificationLED, R.string.led) {
|
||||
enabled = Build.VERSION.SDK_INT < 26
|
||||
}
|
||||
|
||||
|
@ -222,26 +222,26 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
|
||||
text(
|
||||
Pref.spPullNotificationCheckInterval,
|
||||
PrefS.spPullNotificationCheckInterval,
|
||||
R.string.pull_notification_check_interval,
|
||||
InputTypeEx.number
|
||||
)
|
||||
|
||||
sw(Pref.bpShowAcctInSystemNotification, R.string.show_acct_in_system_notification)
|
||||
sw(PrefB.bpShowAcctInSystemNotification, R.string.show_acct_in_system_notification)
|
||||
|
||||
sw(Pref.bpSeparateReplyNotificationGroup, R.string.separate_notification_group_for_reply) {
|
||||
sw(PrefB.bpSeparateReplyNotificationGroup, R.string.separate_notification_group_for_reply) {
|
||||
enabled = Build.VERSION.SDK_INT >= 26
|
||||
}
|
||||
|
||||
sw(Pref.bpDivideNotification, R.string.divide_notification)
|
||||
sw(PrefB.bpDivideNotification, R.string.divide_notification)
|
||||
}
|
||||
|
||||
section(R.string.behavior) {
|
||||
|
||||
sw(Pref.bpDontConfirmBeforeCloseColumn, R.string.dont_confirm_before_close_column)
|
||||
sw(PrefB.bpDontConfirmBeforeCloseColumn, R.string.dont_confirm_before_close_column)
|
||||
|
||||
spinner(
|
||||
Pref.ipBackButtonAction,
|
||||
PrefI.ipBackButtonAction,
|
||||
R.string.back_button_action,
|
||||
R.string.ask_always,
|
||||
R.string.close_column,
|
||||
|
@ -249,15 +249,15 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
R.string.app_exit
|
||||
)
|
||||
|
||||
sw(Pref.bpExitAppWhenCloseProtectedColumn, R.string.exit_app_when_close_protected_column)
|
||||
sw(Pref.bpScrollTopFromColumnStrip, R.string.scroll_top_from_column_strip)
|
||||
sw(Pref.bpDontScreenOff, R.string.dont_screen_off)
|
||||
sw(Pref.bpDontUseCustomTabs, R.string.dont_use_custom_tabs)
|
||||
sw(Pref.bpPriorChrome, R.string.prior_chrome_custom_tabs)
|
||||
sw(PrefB.bpExitAppWhenCloseProtectedColumn, R.string.exit_app_when_close_protected_column)
|
||||
sw(PrefB.bpScrollTopFromColumnStrip, R.string.scroll_top_from_column_strip)
|
||||
sw(PrefB.bpDontScreenOff, R.string.dont_screen_off)
|
||||
sw(PrefB.bpDontUseCustomTabs, R.string.dont_use_custom_tabs)
|
||||
sw(PrefB.bpPriorChrome, R.string.prior_chrome_custom_tabs)
|
||||
|
||||
item(
|
||||
SettingType.TextWithSelector,
|
||||
Pref.spWebBrowser,
|
||||
PrefS.spWebBrowser,
|
||||
R.string.web_browser
|
||||
) {
|
||||
onClickEdit = {
|
||||
|
@ -292,23 +292,23 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
}
|
||||
|
||||
sw(Pref.bpAllowColumnDuplication, R.string.allow_column_duplication)
|
||||
sw(Pref.bpForceGap, R.string.force_gap_when_refresh)
|
||||
sw(PrefB.bpAllowColumnDuplication, R.string.allow_column_duplication)
|
||||
sw(PrefB.bpForceGap, R.string.force_gap_when_refresh)
|
||||
spinner(
|
||||
Pref.ipGapHeadScrollPosition,
|
||||
PrefI.ipGapHeadScrollPosition,
|
||||
R.string.scroll_position_after_read_gap_from_head,
|
||||
R.string.gap_head,
|
||||
R.string.gap_tail,
|
||||
)
|
||||
spinner(
|
||||
Pref.ipGapTailScrollPosition,
|
||||
PrefI.ipGapTailScrollPosition,
|
||||
R.string.scroll_position_after_read_gap_from_tail,
|
||||
R.string.gap_head,
|
||||
R.string.gap_tail,
|
||||
)
|
||||
text(Pref.spClientName, R.string.client_name, InputTypeEx.text)
|
||||
text(PrefS.spClientName, R.string.client_name, InputTypeEx.text)
|
||||
|
||||
text(Pref.spUserAgent, R.string.user_agent, InputTypeEx.textMultiLine) {
|
||||
text(PrefS.spUserAgent, R.string.user_agent, InputTypeEx.textMultiLine) {
|
||||
hint = App1.userAgentDefault
|
||||
filter = { it.replace(ActAppSetting.reLinefeed, " ").trim() }
|
||||
getError = {
|
||||
|
@ -320,13 +320,13 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
}
|
||||
|
||||
sw(Pref.bpDontRemoveDeletedToot, R.string.dont_remove_deleted_toot_from_timeline)
|
||||
sw(Pref.bpCustomEmojiSeparatorZwsp, R.string.custom_emoji_separator_zwsp)
|
||||
sw(Pref.bpShowTranslateButton, R.string.show_translate_button)
|
||||
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(
|
||||
SettingType.TextWithSelector,
|
||||
Pref.spTranslateAppComponent,
|
||||
PrefS.spTranslateAppComponent,
|
||||
R.string.translation_app
|
||||
) {
|
||||
val target = CustomShareTarget.Translate
|
||||
|
@ -337,7 +337,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
|
||||
item(
|
||||
SettingType.TextWithSelector,
|
||||
Pref.spCustomShare1,
|
||||
PrefS.spCustomShare1,
|
||||
R.string.custom_share_button_1
|
||||
) {
|
||||
val target = CustomShareTarget.CustomShare1
|
||||
|
@ -348,7 +348,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
|
||||
item(
|
||||
SettingType.TextWithSelector,
|
||||
Pref.spCustomShare2,
|
||||
PrefS.spCustomShare2,
|
||||
R.string.custom_share_button_2
|
||||
) {
|
||||
val target = CustomShareTarget.CustomShare2
|
||||
|
@ -358,7 +358,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
item(
|
||||
SettingType.TextWithSelector,
|
||||
Pref.spCustomShare3,
|
||||
PrefS.spCustomShare3,
|
||||
R.string.custom_share_button_3
|
||||
) {
|
||||
val target = CustomShareTarget.CustomShare3
|
||||
|
@ -368,22 +368,19 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
|
||||
spinner(
|
||||
Pref.ipAdditionalButtonsPosition,
|
||||
PrefI.ipAdditionalButtonsPosition,
|
||||
R.string.additional_buttons_position,
|
||||
R.string.top,
|
||||
R.string.bottom,
|
||||
R.string.start,
|
||||
R.string.end
|
||||
*(AdditionalButtonsPosition.values().sortedBy { it.idx }.map { it.captionId }.toIntArray())
|
||||
)
|
||||
|
||||
sw(Pref.bpEnablePixelfed, R.string.enable_connect_to_pixelfed_server)
|
||||
sw(Pref.bpShowFilteredWord, R.string.show_filtered_word)
|
||||
sw(Pref.bpEnableDomainTimeline, R.string.enable_domain_timeline)
|
||||
sw(PrefB.bpEnablePixelfed, R.string.enable_connect_to_pixelfed_server)
|
||||
sw(PrefB.bpShowFilteredWord, R.string.show_filtered_word)
|
||||
sw(PrefB.bpEnableDomainTimeline, R.string.enable_domain_timeline)
|
||||
}
|
||||
|
||||
section(R.string.post) {
|
||||
|
||||
// spinner(Pref.ipResizeImage, R.string.resize_image) { activity ->
|
||||
// spinner(PrefI.ipResizeImage, R.string.resize_image) { activity ->
|
||||
// ActPost.resizeConfigList.map {
|
||||
// when (it.type) {
|
||||
// ResizeType.None -> activity.getString(R.string.dont_resize)
|
||||
|
@ -397,67 +394,67 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
// }
|
||||
// }
|
||||
//
|
||||
// text(Pref.spMediaSizeMax, R.string.media_attachment_max_byte_size, InputTypeEx.number)
|
||||
// text(Pref.spMovieSizeMax, R.string.media_attachment_max_byte_size_movie, InputTypeEx.number)
|
||||
// text(PrefS.spMediaSizeMax, R.string.media_attachment_max_byte_size, InputTypeEx.number)
|
||||
// text(PrefS.spMovieSizeMax, R.string.media_attachment_max_byte_size_movie, InputTypeEx.number)
|
||||
// text(
|
||||
// Pref.spMediaSizeMaxPixelfed,
|
||||
// PrefS.spMediaSizeMaxPixelfed,
|
||||
// R.string.media_attachment_max_byte_size_pixelfed,
|
||||
// InputTypeEx.number
|
||||
// )
|
||||
|
||||
spinner(
|
||||
Pref.ipRefreshAfterToot,
|
||||
PrefI.ipRefreshAfterToot,
|
||||
R.string.refresh_after_toot,
|
||||
R.string.refresh_scroll_to_toot,
|
||||
R.string.refresh_no_scroll,
|
||||
R.string.dont_refresh
|
||||
)
|
||||
|
||||
sw(Pref.bpPostButtonBarTop, R.string.show_post_button_bar_top)
|
||||
sw(PrefB.bpPostButtonBarTop, R.string.show_post_button_bar_top)
|
||||
|
||||
sw(
|
||||
Pref.bpDontDuplicationCheck,
|
||||
PrefB.bpDontDuplicationCheck,
|
||||
R.string.dont_add_duplication_check_header
|
||||
)
|
||||
|
||||
sw(Pref.bpQuickTootBar, R.string.show_quick_toot_bar)
|
||||
sw(PrefB.bpQuickTootBar, R.string.show_quick_toot_bar)
|
||||
|
||||
sw(
|
||||
Pref.bpDontUseActionButtonWithQuickTootBar,
|
||||
PrefB.bpDontUseActionButtonWithQuickTootBar,
|
||||
R.string.dont_use_action_button_with_quick_toot_bar
|
||||
)
|
||||
|
||||
text(Pref.spQuoteNameFormat, R.string.format_of_quote_name, InputTypeEx.text) {
|
||||
text(PrefS.spQuoteNameFormat, R.string.format_of_quote_name, InputTypeEx.text) {
|
||||
filter = { it } // don't trim
|
||||
}
|
||||
|
||||
sw(
|
||||
Pref.bpAppendAttachmentUrlToContent,
|
||||
PrefB.bpAppendAttachmentUrlToContent,
|
||||
R.string.append_attachment_url_to_content
|
||||
)
|
||||
|
||||
sw(
|
||||
Pref.bpWarnHashtagAsciiAndNonAscii,
|
||||
PrefB.bpWarnHashtagAsciiAndNonAscii,
|
||||
R.string.warn_hashtag_ascii_and_non_ascii
|
||||
)
|
||||
|
||||
sw(
|
||||
Pref.bpEmojiPickerCloseOnSelected,
|
||||
PrefB.bpEmojiPickerCloseOnSelected,
|
||||
R.string.close_emoji_picker_when_selected
|
||||
)
|
||||
|
||||
sw(Pref.bpIgnoreTextInSharedMedia, R.string.ignore_text_in_shared_media)
|
||||
sw(PrefB.bpIgnoreTextInSharedMedia, R.string.ignore_text_in_shared_media)
|
||||
}
|
||||
|
||||
section(R.string.tablet_mode) {
|
||||
|
||||
sw(Pref.bpDisableTabletMode, R.string.disable_tablet_mode)
|
||||
sw(PrefB.bpDisableTabletMode, R.string.disable_tablet_mode)
|
||||
|
||||
text(Pref.spColumnWidth, R.string.minimum_column_width, InputTypeEx.number)
|
||||
text(PrefS.spColumnWidth, R.string.minimum_column_width, InputTypeEx.number)
|
||||
|
||||
item(
|
||||
SettingType.Spinner,
|
||||
Pref.lpTabletTootDefaultAccount,
|
||||
PrefL.lpTabletTootDefaultAccount,
|
||||
R.string.toot_button_default_account
|
||||
) {
|
||||
val lp = pref.cast<LongPref>()!!
|
||||
|
@ -474,12 +471,12 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
|
||||
sw(
|
||||
Pref.bpQuickTootOmitAccountSelection,
|
||||
PrefB.bpQuickTootOmitAccountSelection,
|
||||
R.string.quick_toot_omit_account_selection
|
||||
)
|
||||
|
||||
spinner(
|
||||
Pref.ipJustifyWindowContentPortrait,
|
||||
PrefI.ipJustifyWindowContentPortrait,
|
||||
R.string.justify_window_content_portrait,
|
||||
R.string.default_,
|
||||
R.string.start,
|
||||
|
@ -487,39 +484,39 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
)
|
||||
|
||||
sw(
|
||||
Pref.bpMultiWindowPost,
|
||||
PrefB.bpMultiWindowPost,
|
||||
R.string.multi_window_post
|
||||
)
|
||||
sw(
|
||||
Pref.bpManyWindowPost,
|
||||
PrefB.bpManyWindowPost,
|
||||
R.string.many_window_post
|
||||
)
|
||||
}
|
||||
|
||||
section(R.string.media_attachment) {
|
||||
sw(Pref.bpUseInternalMediaViewer, R.string.use_internal_media_viewer)
|
||||
sw(Pref.bpPriorLocalURL, R.string.prior_local_url_when_open_attachment)
|
||||
text(Pref.spMediaThumbHeight, R.string.media_thumbnail_height, InputTypeEx.number)
|
||||
sw(Pref.bpDontCropMediaThumb, R.string.dont_crop_media_thumbnail)
|
||||
sw(Pref.bpVerticalArrangeThumbnails, R.string.thumbnails_arrange_vertically)
|
||||
sw(PrefB.bpUseInternalMediaViewer, R.string.use_internal_media_viewer)
|
||||
sw(PrefB.bpPriorLocalURL, R.string.prior_local_url_when_open_attachment)
|
||||
text(PrefS.spMediaThumbHeight, R.string.media_thumbnail_height, InputTypeEx.number)
|
||||
sw(PrefB.bpDontCropMediaThumb, R.string.dont_crop_media_thumbnail)
|
||||
sw(PrefB.bpVerticalArrangeThumbnails, R.string.thumbnails_arrange_vertically)
|
||||
}
|
||||
|
||||
section(R.string.animation) {
|
||||
sw(Pref.bpEnableGifAnimation, R.string.enable_gif_animation)
|
||||
sw(Pref.bpDisableEmojiAnimation, R.string.disable_custom_emoji_animation)
|
||||
sw(PrefB.bpEnableGifAnimation, R.string.enable_gif_animation)
|
||||
sw(PrefB.bpDisableEmojiAnimation, R.string.disable_custom_emoji_animation)
|
||||
}
|
||||
|
||||
section(R.string.appearance) {
|
||||
sw(Pref.bpSimpleList, R.string.simple_list)
|
||||
sw(Pref.bpShowFollowButtonInButtonBar, R.string.show_follow_button_in_button_bar)
|
||||
sw(Pref.bpDontShowPreviewCard, R.string.dont_show_preview_card)
|
||||
sw(Pref.bpShortAcctLocalUser, R.string.short_acct_local_user)
|
||||
sw(Pref.bpMentionFullAcct, R.string.mention_full_acct)
|
||||
sw(Pref.bpRelativeTimestamp, R.string.relative_timestamp)
|
||||
sw(PrefB.bpSimpleList, R.string.simple_list)
|
||||
sw(PrefB.bpShowFollowButtonInButtonBar, R.string.show_follow_button_in_button_bar)
|
||||
sw(PrefB.bpDontShowPreviewCard, R.string.dont_show_preview_card)
|
||||
sw(PrefB.bpShortAcctLocalUser, R.string.short_acct_local_user)
|
||||
sw(PrefB.bpMentionFullAcct, R.string.mention_full_acct)
|
||||
sw(PrefB.bpRelativeTimestamp, R.string.relative_timestamp)
|
||||
|
||||
item(
|
||||
SettingType.Spinner,
|
||||
Pref.spTimeZone,
|
||||
PrefS.spTimeZone,
|
||||
R.string.timezone
|
||||
) {
|
||||
val sp: StringPref = pref.cast()!!
|
||||
|
@ -535,27 +532,27 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
}
|
||||
|
||||
sw(Pref.bpShowAppName, R.string.always_show_application)
|
||||
sw(Pref.bpShowLanguage, R.string.always_show_language)
|
||||
text(Pref.spAutoCWLines, R.string.auto_cw, InputTypeEx.number)
|
||||
text(Pref.spCardDescriptionLength, R.string.card_description_length, InputTypeEx.number)
|
||||
sw(PrefB.bpShowAppName, R.string.always_show_application)
|
||||
sw(PrefB.bpShowLanguage, R.string.always_show_language)
|
||||
text(PrefS.spAutoCWLines, R.string.auto_cw, InputTypeEx.number)
|
||||
text(PrefS.spCardDescriptionLength, R.string.card_description_length, InputTypeEx.number)
|
||||
|
||||
spinner(
|
||||
Pref.ipRepliesCount,
|
||||
PrefI.ipRepliesCount,
|
||||
R.string.display_replies_count,
|
||||
R.string.replies_count_simple,
|
||||
R.string.replies_count_actual,
|
||||
R.string.replies_count_none
|
||||
)
|
||||
spinner(
|
||||
Pref.ipBoostsCount,
|
||||
PrefI.ipBoostsCount,
|
||||
R.string.display_boost_count,
|
||||
R.string.replies_count_simple,
|
||||
R.string.replies_count_actual,
|
||||
R.string.replies_count_none
|
||||
)
|
||||
spinner(
|
||||
Pref.ipFavouritesCount,
|
||||
PrefI.ipFavouritesCount,
|
||||
R.string.display_favourite_count,
|
||||
R.string.replies_count_simple,
|
||||
R.string.replies_count_actual,
|
||||
|
@ -563,7 +560,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
)
|
||||
|
||||
spinner(
|
||||
Pref.ipVisibilityStyle,
|
||||
PrefI.ipVisibilityStyle,
|
||||
R.string.visibility_style,
|
||||
R.string.visibility_style_by_account,
|
||||
R.string.mastodon,
|
||||
|
@ -572,7 +569,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
|
||||
AppSettingItem.TIMELINE_FONT = item(
|
||||
SettingType.TextWithSelector,
|
||||
Pref.spTimelineFont,
|
||||
PrefS.spTimelineFont,
|
||||
R.string.timeline_font
|
||||
) {
|
||||
val item = this
|
||||
|
@ -593,7 +590,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
|
||||
AppSettingItem.TIMELINE_FONT_BOLD = item(
|
||||
SettingType.TextWithSelector,
|
||||
Pref.spTimelineFontBold,
|
||||
PrefS.spTimelineFontBold,
|
||||
R.string.timeline_font_bold
|
||||
) {
|
||||
val item = this
|
||||
|
@ -613,7 +610,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
|
||||
AppSettingItem.FONT_SIZE_TIMELINE = textX(
|
||||
Pref.fpTimelineFontSize,
|
||||
PrefF.fpTimelineFontSize,
|
||||
R.string.timeline_font_size,
|
||||
InputTypeEx.numberDecimal
|
||||
) {
|
||||
|
@ -627,20 +624,20 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
captionFontSize = {
|
||||
val fv = fp(pref)
|
||||
when {
|
||||
!fv.isFinite() -> Pref.default_timeline_font_size
|
||||
!fv.isFinite() -> PrefF.default_timeline_font_size
|
||||
fv < 1f -> 1f
|
||||
else -> fv
|
||||
}
|
||||
}
|
||||
captionSpacing = {
|
||||
Pref.spTimelineSpacing(pref).toFloatOrNull()
|
||||
PrefS.spTimelineSpacing(pref).toFloatOrNull()
|
||||
}
|
||||
changed = {
|
||||
findItemViewHolder(item)?.updateCaption()
|
||||
}
|
||||
}
|
||||
|
||||
textX(Pref.fpAcctFontSize, R.string.acct_font_size, InputTypeEx.numberDecimal) {
|
||||
textX(PrefF.fpAcctFontSize, R.string.acct_font_size, InputTypeEx.numberDecimal) {
|
||||
val item = this
|
||||
val fp: FloatPref = item.pref.cast()!!
|
||||
|
||||
|
@ -650,7 +647,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
captionFontSize = {
|
||||
val fv = fp(pref)
|
||||
when {
|
||||
!fv.isFinite() -> Pref.default_acct_font_size
|
||||
!fv.isFinite() -> PrefF.default_acct_font_size
|
||||
fv < 1f -> 1f
|
||||
else -> fv
|
||||
}
|
||||
|
@ -660,7 +657,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
|
||||
AppSettingItem.FONT_SIZE_NOTIFICATION_TL = textX(
|
||||
Pref.fpNotificationTlFontSize,
|
||||
PrefF.fpNotificationTlFontSize,
|
||||
R.string.notification_tl_font_size,
|
||||
InputTypeEx.numberDecimal
|
||||
) {
|
||||
|
@ -673,13 +670,13 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
captionFontSize = {
|
||||
val fv = fp(pref)
|
||||
when {
|
||||
!fv.isFinite() -> Pref.default_notification_tl_font_size
|
||||
!fv.isFinite() -> PrefF.default_notification_tl_font_size
|
||||
fv < 1f -> 1f
|
||||
else -> fv
|
||||
}
|
||||
}
|
||||
captionSpacing = {
|
||||
Pref.spTimelineSpacing(pref).toFloatOrNull()
|
||||
PrefS.spTimelineSpacing(pref).toFloatOrNull()
|
||||
}
|
||||
changed = {
|
||||
findItemViewHolder(item)?.updateCaption()
|
||||
|
@ -687,34 +684,34 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
|
||||
text(
|
||||
Pref.spNotificationTlIconSize,
|
||||
PrefS.spNotificationTlIconSize,
|
||||
R.string.notification_tl_icon_size,
|
||||
InputTypeEx.numberDecimal
|
||||
)
|
||||
|
||||
text(Pref.spTimelineSpacing, R.string.timeline_line_spacing, InputTypeEx.numberDecimal) {
|
||||
text(PrefS.spTimelineSpacing, R.string.timeline_line_spacing, InputTypeEx.numberDecimal) {
|
||||
changed = {
|
||||
findItemViewHolder(AppSettingItem.FONT_SIZE_TIMELINE)?.updateCaption()
|
||||
findItemViewHolder(AppSettingItem.FONT_SIZE_NOTIFICATION_TL)?.updateCaption()
|
||||
}
|
||||
}
|
||||
|
||||
text(Pref.spBoostButtonSize, R.string.boost_button_size, InputTypeEx.numberDecimal)
|
||||
text(PrefS.spBoostButtonSize, R.string.boost_button_size, InputTypeEx.numberDecimal)
|
||||
|
||||
spinner(
|
||||
Pref.ipBoostButtonJustify,
|
||||
PrefI.ipBoostButtonJustify,
|
||||
R.string.boost_button_alignment,
|
||||
R.string.start,
|
||||
R.string.center,
|
||||
R.string.end
|
||||
)
|
||||
|
||||
text(Pref.spAvatarIconSize, R.string.avatar_icon_size, InputTypeEx.numberDecimal)
|
||||
text(Pref.spRoundRatio, R.string.avatar_icon_round_ratio, InputTypeEx.numberDecimal)
|
||||
sw(Pref.bpDontRound, R.string.avatar_icon_dont_round)
|
||||
text(Pref.spReplyIconSize, R.string.reply_icon_size, InputTypeEx.numberDecimal)
|
||||
text(Pref.spHeaderIconSize, R.string.header_icon_size, InputTypeEx.numberDecimal)
|
||||
textX(Pref.fpHeaderTextSize, R.string.header_text_size, InputTypeEx.numberDecimal) {
|
||||
text(PrefS.spAvatarIconSize, R.string.avatar_icon_size, InputTypeEx.numberDecimal)
|
||||
text(PrefS.spRoundRatio, R.string.avatar_icon_round_ratio, InputTypeEx.numberDecimal)
|
||||
sw(PrefB.bpDontRound, R.string.avatar_icon_dont_round)
|
||||
text(PrefS.spReplyIconSize, R.string.reply_icon_size, InputTypeEx.numberDecimal)
|
||||
text(PrefS.spHeaderIconSize, R.string.header_icon_size, InputTypeEx.numberDecimal)
|
||||
textX(PrefF.fpHeaderTextSize, R.string.header_text_size, InputTypeEx.numberDecimal) {
|
||||
val item = this
|
||||
val fp: FloatPref = item.pref.cast()!!
|
||||
|
||||
|
@ -724,7 +721,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
captionFontSize = {
|
||||
val fv = fp(pref)
|
||||
when {
|
||||
!fv.isFinite() -> Pref.default_header_font_size
|
||||
!fv.isFinite() -> PrefF.default_header_font_size
|
||||
fv < 1f -> 1f
|
||||
else -> fv
|
||||
}
|
||||
|
@ -735,95 +732,95 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
}
|
||||
|
||||
text(Pref.spStripIconSize, R.string.strip_icon_size, InputTypeEx.numberDecimal)
|
||||
text(PrefS.spStripIconSize, R.string.strip_icon_size, InputTypeEx.numberDecimal)
|
||||
|
||||
text(Pref.spScreenBottomPadding, R.string.screen_bottom_padding, InputTypeEx.numberDecimal)
|
||||
text(PrefS.spScreenBottomPadding, R.string.screen_bottom_padding, InputTypeEx.numberDecimal)
|
||||
|
||||
sw(Pref.bpOpenSticker, R.string.show_open_sticker) {
|
||||
sw(PrefB.bpOpenSticker, R.string.show_open_sticker) {
|
||||
desc = R.string.powered_by_open_sticker
|
||||
descClick = { openBrowser("https://github.com/cutls/OpenSticker") }
|
||||
}
|
||||
|
||||
sw(Pref.bpLinksInContextMenu, R.string.show_links_in_context_menu)
|
||||
sw(Pref.bpShowLinkUnderline, R.string.show_link_underline)
|
||||
sw(PrefB.bpLinksInContextMenu, R.string.show_links_in_context_menu)
|
||||
sw(PrefB.bpShowLinkUnderline, R.string.show_link_underline)
|
||||
sw(
|
||||
Pref.bpMoveNotificationsQuickFilter,
|
||||
PrefB.bpMoveNotificationsQuickFilter,
|
||||
R.string.move_notifications_quick_filter_to_column_setting
|
||||
)
|
||||
sw(Pref.bpShowSearchClear, R.string.show_clear_button_in_search_bar)
|
||||
sw(PrefB.bpShowSearchClear, R.string.show_clear_button_in_search_bar)
|
||||
sw(
|
||||
Pref.bpDontShowColumnBackgroundImage,
|
||||
PrefB.bpDontShowColumnBackgroundImage,
|
||||
R.string.dont_show_column_background_image
|
||||
)
|
||||
|
||||
group(R.string.show_in_directory) {
|
||||
checkbox(Pref.bpDirectoryLastActive, R.string.last_active)
|
||||
checkbox(Pref.bpDirectoryFollowers, R.string.followers)
|
||||
checkbox(Pref.bpDirectoryTootCount, R.string.toot_count)
|
||||
checkbox(Pref.bpDirectoryNote, R.string.note)
|
||||
checkbox(PrefB.bpDirectoryLastActive, R.string.last_active)
|
||||
checkbox(PrefB.bpDirectoryFollowers, R.string.followers)
|
||||
checkbox(PrefB.bpDirectoryTootCount, R.string.toot_count)
|
||||
checkbox(PrefB.bpDirectoryNote, R.string.note)
|
||||
}
|
||||
|
||||
sw(
|
||||
Pref.bpAlwaysExpandContextMenuItems,
|
||||
PrefB.bpAlwaysExpandContextMenuItems,
|
||||
R.string.always_expand_context_menu_sub_items
|
||||
)
|
||||
sw(Pref.bpShowBookmarkButton, R.string.show_bookmark_button)
|
||||
sw(Pref.bpHideFollowCount, R.string.hide_followers_count)
|
||||
sw(Pref.bpEmojioneShortcode, R.string.emojione_shortcode_support) {
|
||||
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(Pref.bpInAppUnicodeEmoji, R.string.in_app_unicode_emoji)
|
||||
sw(PrefB.bpInAppUnicodeEmoji, R.string.in_app_unicode_emoji)
|
||||
|
||||
sw(Pref.bpKeepReactionSpace, R.string.keep_reaction_space)
|
||||
sw(PrefB.bpKeepReactionSpace, R.string.keep_reaction_space)
|
||||
}
|
||||
|
||||
section(R.string.color) {
|
||||
|
||||
spinner(
|
||||
Pref.ipUiTheme,
|
||||
PrefI.ipUiTheme,
|
||||
R.string.ui_theme,
|
||||
R.string.theme_light,
|
||||
R.string.theme_dark
|
||||
)
|
||||
|
||||
colorAlpha(Pref.ipListDividerColor, R.string.list_divider_color)
|
||||
colorAlpha(Pref.ipLinkColor, R.string.link_color)
|
||||
colorAlpha(PrefI.ipListDividerColor, R.string.list_divider_color)
|
||||
colorAlpha(PrefI.ipLinkColor, R.string.link_color)
|
||||
|
||||
group(R.string.toot_background_color) {
|
||||
colorAlpha(Pref.ipTootColorUnlisted, R.string.unlisted_visibility)
|
||||
colorAlpha(Pref.ipTootColorFollower, R.string.followers_visibility)
|
||||
colorAlpha(Pref.ipTootColorDirectUser, R.string.direct_with_user_visibility)
|
||||
colorAlpha(Pref.ipTootColorDirectMe, R.string.direct_only_me_visibility)
|
||||
colorAlpha(PrefI.ipTootColorUnlisted, R.string.unlisted_visibility)
|
||||
colorAlpha(PrefI.ipTootColorFollower, R.string.followers_visibility)
|
||||
colorAlpha(PrefI.ipTootColorDirectUser, R.string.direct_with_user_visibility)
|
||||
colorAlpha(PrefI.ipTootColorDirectMe, R.string.direct_only_me_visibility)
|
||||
}
|
||||
|
||||
group(R.string.event_background_color) {
|
||||
colorAlpha(Pref.ipEventBgColorBoost, R.string.boost)
|
||||
colorAlpha(Pref.ipEventBgColorFavourite, R.string.favourites)
|
||||
colorAlpha(Pref.ipEventBgColorBookmark, R.string.bookmarks)
|
||||
colorAlpha(Pref.ipEventBgColorMention, R.string.reply)
|
||||
colorAlpha(Pref.ipEventBgColorFollow, R.string.follow)
|
||||
colorAlpha(Pref.ipEventBgColorUnfollow, R.string.unfollow_misskey)
|
||||
colorAlpha(Pref.ipEventBgColorFollowRequest, R.string.follow_request)
|
||||
colorAlpha(Pref.ipEventBgColorReaction, R.string.reaction)
|
||||
colorAlpha(Pref.ipEventBgColorQuote, R.string.quote_renote)
|
||||
colorAlpha(Pref.ipEventBgColorVote, R.string.vote_polls)
|
||||
colorAlpha(Pref.ipEventBgColorStatus, R.string.status)
|
||||
colorAlpha(PrefI.ipEventBgColorBoost, R.string.boost)
|
||||
colorAlpha(PrefI.ipEventBgColorFavourite, R.string.favourites)
|
||||
colorAlpha(PrefI.ipEventBgColorBookmark, R.string.bookmarks)
|
||||
colorAlpha(PrefI.ipEventBgColorMention, R.string.reply)
|
||||
colorAlpha(PrefI.ipEventBgColorFollow, R.string.follow)
|
||||
colorAlpha(PrefI.ipEventBgColorUnfollow, R.string.unfollow_misskey)
|
||||
colorAlpha(PrefI.ipEventBgColorFollowRequest, R.string.follow_request)
|
||||
colorAlpha(PrefI.ipEventBgColorReaction, R.string.reaction)
|
||||
colorAlpha(PrefI.ipEventBgColorQuote, R.string.quote_renote)
|
||||
colorAlpha(PrefI.ipEventBgColorVote, R.string.vote_polls)
|
||||
colorAlpha(PrefI.ipEventBgColorStatus, R.string.status)
|
||||
|
||||
colorAlpha(
|
||||
Pref.ipConversationMainTootBgColor,
|
||||
PrefI.ipConversationMainTootBgColor,
|
||||
R.string.conversation_main_toot_background_color
|
||||
)
|
||||
|
||||
colorAlpha(Pref.ipEventBgColorGap, R.string.gap)
|
||||
colorAlpha(PrefI.ipEventBgColorGap, R.string.gap)
|
||||
}
|
||||
|
||||
group(R.string.button_accent_color) {
|
||||
colorAlpha(Pref.ipButtonBoostedColor, R.string.boost)
|
||||
colorAlpha(Pref.ipButtonFavoritedColor, R.string.favourites)
|
||||
colorAlpha(Pref.ipButtonBookmarkedColor, R.string.bookmarks)
|
||||
colorAlpha(Pref.ipButtonFollowingColor, R.string.follow)
|
||||
colorAlpha(Pref.ipButtonFollowRequestColor, R.string.follow_request)
|
||||
colorAlpha(Pref.ipButtonReactionedColor, R.string.reaction)
|
||||
colorAlpha(PrefI.ipButtonBoostedColor, R.string.boost)
|
||||
colorAlpha(PrefI.ipButtonFavoritedColor, R.string.favourites)
|
||||
colorAlpha(PrefI.ipButtonBookmarkedColor, R.string.bookmarks)
|
||||
colorAlpha(PrefI.ipButtonFollowingColor, R.string.follow)
|
||||
colorAlpha(PrefI.ipButtonFollowRequestColor, R.string.follow_request)
|
||||
colorAlpha(PrefI.ipButtonReactionedColor, R.string.reaction)
|
||||
}
|
||||
|
||||
group(R.string.column_color_default) {
|
||||
|
@ -834,8 +831,8 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
val ivColumnHeader: ImageView = viewRoot.findViewById(R.id.ivColumnHeader)
|
||||
val tvColumnName: TextView = viewRoot.findViewById(R.id.tvColumnName)
|
||||
|
||||
val colorColumnHeaderBg = Pref.ipCcdHeaderBg(activity.pref)
|
||||
val colorColumnHeaderFg = Pref.ipCcdHeaderFg(activity.pref)
|
||||
val colorColumnHeaderBg = PrefI.ipCcdHeaderBg(activity.pref)
|
||||
val colorColumnHeaderFg = PrefI.ipCcdHeaderFg(activity.pref)
|
||||
|
||||
val headerBg = when {
|
||||
colorColumnHeaderBg != 0 -> colorColumnHeaderBg
|
||||
|
@ -854,10 +851,10 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
ivColumnHeader.imageTintList = ColorStateList.valueOf(headerFg)
|
||||
}
|
||||
|
||||
colorOpaque(Pref.ipCcdHeaderBg, R.string.header_background_color) {
|
||||
colorOpaque(PrefI.ipCcdHeaderBg, R.string.header_background_color) {
|
||||
changed = { showSample(AppSettingItem.SAMPLE_CCD_HEADER) }
|
||||
}
|
||||
colorOpaque(Pref.ipCcdHeaderFg, R.string.header_foreground_color) {
|
||||
colorOpaque(PrefI.ipCcdHeaderFg, R.string.header_foreground_color) {
|
||||
changed = { showSample(AppSettingItem.SAMPLE_CCD_HEADER) }
|
||||
}
|
||||
|
||||
|
@ -867,9 +864,9 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
val tvSampleAcct: TextView = viewRoot.findViewById(R.id.tvSampleAcct)
|
||||
val tvSampleContent: TextView = viewRoot.findViewById(R.id.tvSampleContent)
|
||||
|
||||
val colorColumnBg = Pref.ipCcdContentBg(activity.pref)
|
||||
val colorColumnAcct = Pref.ipCcdContentAcct(activity.pref)
|
||||
val colorColumnText = Pref.ipCcdContentText(activity.pref)
|
||||
val colorColumnBg = PrefI.ipCcdContentBg(activity.pref)
|
||||
val colorColumnAcct = PrefI.ipCcdContentAcct(activity.pref)
|
||||
val colorColumnText = PrefI.ipCcdContentText(activity.pref)
|
||||
|
||||
flColumnBackground.setBackgroundColor(colorColumnBg) // may 0
|
||||
|
||||
|
@ -884,18 +881,18 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
)
|
||||
}
|
||||
|
||||
colorOpaque(Pref.ipCcdContentBg, R.string.content_background_color) {
|
||||
colorOpaque(PrefI.ipCcdContentBg, R.string.content_background_color) {
|
||||
changed = { showSample(AppSettingItem.SAMPLE_CCD_BODY) }
|
||||
}
|
||||
colorAlpha(Pref.ipCcdContentAcct, R.string.content_acct_color) {
|
||||
colorAlpha(PrefI.ipCcdContentAcct, R.string.content_acct_color) {
|
||||
changed = { showSample(AppSettingItem.SAMPLE_CCD_BODY) }
|
||||
}
|
||||
colorAlpha(Pref.ipCcdContentText, R.string.content_text_color) {
|
||||
colorAlpha(PrefI.ipCcdContentText, R.string.content_text_color) {
|
||||
changed = { showSample(AppSettingItem.SAMPLE_CCD_BODY) }
|
||||
}
|
||||
}
|
||||
|
||||
text(Pref.spBoostAlpha, R.string.boost_button_alpha, InputTypeEx.numberDecimal)
|
||||
text(PrefS.spBoostAlpha, R.string.boost_button_alpha, InputTypeEx.numberDecimal)
|
||||
|
||||
group(R.string.footer_color) {
|
||||
AppSettingItem.SAMPLE_FOOTER =
|
||||
|
@ -908,11 +905,11 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
val vFooterDivider2: View = viewRoot.findViewById(R.id.vFooterDivider2)
|
||||
val vIndicator: View = viewRoot.findViewById(R.id.vIndicator)
|
||||
|
||||
val footerButtonBgColor = Pref.ipFooterButtonBgColor(pref)
|
||||
val footerButtonFgColor = Pref.ipFooterButtonFgColor(pref)
|
||||
val footerTabBgColor = Pref.ipFooterTabBgColor(pref)
|
||||
val footerTabDividerColor = Pref.ipFooterTabDividerColor(pref)
|
||||
val footerTabIndicatorColor = Pref.ipFooterTabIndicatorColor(pref)
|
||||
val footerButtonBgColor = PrefI.ipFooterButtonBgColor(pref)
|
||||
val footerButtonFgColor = PrefI.ipFooterButtonFgColor(pref)
|
||||
val footerTabBgColor = PrefI.ipFooterTabBgColor(pref)
|
||||
val footerTabDividerColor = PrefI.ipFooterTabDividerColor(pref)
|
||||
val footerTabIndicatorColor = PrefI.ipFooterTabIndicatorColor(pref)
|
||||
|
||||
val colorColumnStripBackground = footerTabBgColor.notZero()
|
||||
?: activity.attrColor(R.attr.colorColumnStripBackground)
|
||||
|
@ -948,46 +945,46 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
)
|
||||
}
|
||||
|
||||
colorOpaque(Pref.ipFooterButtonBgColor, R.string.button_background_color) {
|
||||
colorOpaque(PrefI.ipFooterButtonBgColor, R.string.button_background_color) {
|
||||
changed = { showSample(AppSettingItem.SAMPLE_FOOTER) }
|
||||
}
|
||||
colorOpaque(Pref.ipFooterButtonFgColor, R.string.button_foreground_color) {
|
||||
colorOpaque(PrefI.ipFooterButtonFgColor, R.string.button_foreground_color) {
|
||||
changed = { showSample(AppSettingItem.SAMPLE_FOOTER) }
|
||||
}
|
||||
colorOpaque(Pref.ipFooterTabBgColor, R.string.quick_toot_bar_background_color) {
|
||||
colorOpaque(PrefI.ipFooterTabBgColor, R.string.quick_toot_bar_background_color) {
|
||||
changed = { showSample(AppSettingItem.SAMPLE_FOOTER) }
|
||||
}
|
||||
colorOpaque(Pref.ipFooterTabDividerColor, R.string.tab_divider_color) {
|
||||
colorOpaque(PrefI.ipFooterTabDividerColor, R.string.tab_divider_color) {
|
||||
changed = { showSample(AppSettingItem.SAMPLE_FOOTER) }
|
||||
}
|
||||
colorAlpha(Pref.ipFooterTabIndicatorColor, R.string.tab_indicator_color) {
|
||||
colorAlpha(PrefI.ipFooterTabIndicatorColor, R.string.tab_indicator_color) {
|
||||
changed = { showSample(AppSettingItem.SAMPLE_FOOTER) }
|
||||
}
|
||||
}
|
||||
|
||||
colorOpaque(Pref.ipSwitchOnColor, R.string.switch_button_color) {
|
||||
colorOpaque(PrefI.ipSwitchOnColor, R.string.switch_button_color) {
|
||||
changed = { setSwitchColor() }
|
||||
}
|
||||
|
||||
colorOpaque(Pref.ipStatusBarColor, R.string.status_bar_color) {
|
||||
colorOpaque(PrefI.ipStatusBarColor, R.string.status_bar_color) {
|
||||
changed = { setStatusBarColor() }
|
||||
}
|
||||
|
||||
colorOpaque(Pref.ipNavigationBarColor, R.string.navigation_bar_color) {
|
||||
colorOpaque(PrefI.ipNavigationBarColor, R.string.navigation_bar_color) {
|
||||
changed = { setStatusBarColor() }
|
||||
}
|
||||
|
||||
colorOpaque(Pref.ipSearchBgColor, R.string.search_bar_background_color)
|
||||
colorAlpha(Pref.ipAnnouncementsBgColor, R.string.announcement_background_color)
|
||||
colorAlpha(Pref.ipVerifiedLinkBgColor, R.string.verified_link_background_color)
|
||||
colorAlpha(Pref.ipVerifiedLinkFgColor, R.string.verified_link_foreground_color)
|
||||
colorOpaque(PrefI.ipSearchBgColor, R.string.search_bar_background_color)
|
||||
colorAlpha(PrefI.ipAnnouncementsBgColor, R.string.announcement_background_color)
|
||||
colorAlpha(PrefI.ipVerifiedLinkBgColor, R.string.verified_link_background_color)
|
||||
colorAlpha(PrefI.ipVerifiedLinkFgColor, R.string.verified_link_foreground_color)
|
||||
}
|
||||
|
||||
section(R.string.performance) {
|
||||
sw(Pref.bpShareViewPool, R.string.share_view_pool)
|
||||
sw(Pref.bpDontUseStreaming, R.string.dont_use_streaming_api)
|
||||
sw(Pref.bpDontRefreshOnResume, R.string.dont_refresh_on_activity_resume)
|
||||
text(Pref.spMediaReadTimeout, R.string.timeout_for_embed_media_viewer, InputTypeEx.number)
|
||||
sw(PrefB.bpShareViewPool, R.string.share_view_pool)
|
||||
sw(PrefB.bpDontUseStreaming, R.string.dont_use_streaming_api)
|
||||
sw(PrefB.bpDontRefreshOnResume, R.string.dont_refresh_on_activity_resume)
|
||||
text(PrefS.spMediaReadTimeout, R.string.timeout_for_embed_media_viewer, InputTypeEx.number)
|
||||
action(R.string.delete_custom_emoji_cache) {
|
||||
action = {
|
||||
App1.custom_emoji_cache.delete()
|
||||
|
@ -996,9 +993,9 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
|||
}
|
||||
|
||||
section(R.string.developer_options) {
|
||||
sw(Pref.bpCheckBetaVersion, R.string.check_beta_release)
|
||||
sw(PrefB.bpCheckBetaVersion, R.string.check_beta_release)
|
||||
|
||||
sw(Pref.bpEmojiPickerCategoryOther, R.string.show_emoji_picker_other_category)
|
||||
sw(PrefB.bpEmojiPickerCategoryOther, R.string.show_emoji_picker_other_category)
|
||||
action(R.string.drawable_list) {
|
||||
action = { startActivity(Intent(this, ActDrawableList::class.java)) }
|
||||
}
|
||||
|
|
|
@ -90,22 +90,22 @@ class Column(
|
|||
|
||||
fun reloadDefaultColor(activity: AppCompatActivity, pref: SharedPreferences) {
|
||||
|
||||
defaultColorHeaderBg = Pref.ipCcdHeaderBg(pref).notZero()
|
||||
defaultColorHeaderBg = PrefI.ipCcdHeaderBg(pref).notZero()
|
||||
?: activity.attrColor(R.attr.color_column_header)
|
||||
|
||||
defaultColorHeaderName = Pref.ipCcdHeaderFg(pref).notZero()
|
||||
defaultColorHeaderName = PrefI.ipCcdHeaderFg(pref).notZero()
|
||||
?: activity.attrColor(R.attr.colorColumnHeaderName)
|
||||
|
||||
defaultColorHeaderPageNumber = Pref.ipCcdHeaderFg(pref).notZero()
|
||||
defaultColorHeaderPageNumber = PrefI.ipCcdHeaderFg(pref).notZero()
|
||||
?: activity.attrColor(R.attr.colorColumnHeaderPageNumber)
|
||||
|
||||
defaultColorContentBg = Pref.ipCcdContentBg(pref)
|
||||
defaultColorContentBg = PrefI.ipCcdContentBg(pref)
|
||||
// may zero
|
||||
|
||||
defaultColorContentAcct = Pref.ipCcdContentAcct(pref).notZero()
|
||||
defaultColorContentAcct = PrefI.ipCcdContentAcct(pref).notZero()
|
||||
?: activity.attrColor(R.attr.colorTimeSmall)
|
||||
|
||||
defaultColorContentText = Pref.ipCcdContentText(pref).notZero()
|
||||
defaultColorContentText = PrefI.ipCcdContentText(pref).notZero()
|
||||
?: activity.attrColor(R.attr.colorContentText)
|
||||
}
|
||||
|
||||
|
|
|
@ -239,7 +239,7 @@ fun Column.onActivityStart() {
|
|||
|
||||
if (!bRefreshLoading &&
|
||||
canAutoRefresh() &&
|
||||
!Pref.bpDontRefreshOnResume(appState.pref) &&
|
||||
!PrefB.bpDontRefreshOnResume(appState.pref) &&
|
||||
!dontAutoRefresh
|
||||
) {
|
||||
// リフレッシュしてからストリーミング開始
|
||||
|
@ -279,7 +279,7 @@ fun Column.startLoading() {
|
|||
|
||||
initFilter()
|
||||
|
||||
Column.showOpenSticker = Pref.bpOpenSticker(appState.pref)
|
||||
Column.showOpenSticker = PrefB.bpOpenSticker(appState.pref)
|
||||
|
||||
mRefreshLoadingErrorPopupState = 0
|
||||
mRefreshLoadingError = ""
|
||||
|
|
|
@ -112,12 +112,11 @@ class ColumnTask_Gap(
|
|||
return
|
||||
}
|
||||
|
||||
val iv = if (isHead) {
|
||||
Pref.ipGapHeadScrollPosition
|
||||
} else {
|
||||
Pref.ipGapTailScrollPosition
|
||||
val iv = when {
|
||||
isHead -> PrefI.ipGapHeadScrollPosition
|
||||
else -> PrefI.ipGapTailScrollPosition
|
||||
}.invoke(pref)
|
||||
val scrollHead = iv == Pref.GSP_HEAD
|
||||
val scrollHead = iv == PrefI.GSP_HEAD
|
||||
|
||||
if (scrollHead) {
|
||||
// ギャップを頭から読んだ場合、スクロール位置の調整は不要
|
||||
|
|
|
@ -23,7 +23,7 @@ class ColumnTask_Loading(
|
|||
override suspend fun background(): TootApiResult? {
|
||||
ctStarted.set(true)
|
||||
|
||||
if (Pref.bpOpenSticker(pref)) {
|
||||
if (PrefB.bpOpenSticker(pref)) {
|
||||
OpenSticker.loadAndWait()
|
||||
}
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ class ColumnTask_Refresh(
|
|||
column.listData.addAll(0, listNew)
|
||||
column.fireShowContent(reason = "refresh updated head", changeList = changeList)
|
||||
|
||||
if (statusIndex >= 0 && refreshAfterToot == Pref.RAT_REFRESH_SCROLL) {
|
||||
if (statusIndex >= 0 && refreshAfterToot == PrefI.RAT_REFRESH_SCROLL) {
|
||||
// 投稿後にその投稿にスクロールする
|
||||
if (holder != null) {
|
||||
holder.setScrollPosition(
|
||||
|
@ -286,7 +286,7 @@ class ColumnTask_Refresh(
|
|||
isCancelled -> false
|
||||
listTmp?.isNotEmpty() != true -> false
|
||||
willAddGap -> true
|
||||
else -> Pref.bpForceGap(App1.pref)
|
||||
else -> PrefB.bpForceGap(App1.pref)
|
||||
}
|
||||
|
||||
if (doesAddGap()) {
|
||||
|
@ -474,7 +474,7 @@ class ColumnTask_Refresh(
|
|||
|
||||
if (!isCancelled &&
|
||||
listTmp?.isNotEmpty() == true &&
|
||||
(willAddGap || Pref.bpForceGap(context))
|
||||
(willAddGap || PrefB.bpForceGap(context))
|
||||
) {
|
||||
addOne(listTmp, TootGap.mayNull(maxId, lastSinceId), head = addToHead)
|
||||
}
|
||||
|
@ -561,7 +561,7 @@ class ColumnTask_Refresh(
|
|||
|
||||
if (!isCancelled &&
|
||||
listTmp?.isNotEmpty() == true &&
|
||||
(willAddGap || Pref.bpForceGap(context))
|
||||
(willAddGap || PrefB.bpForceGap(context))
|
||||
) {
|
||||
addOne(listTmp, TootGap.mayNull(maxId, lastSinceId), head = addToHead)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -61,22 +61,12 @@ fun ColumnViewHolder.showAnnouncements(force: Boolean = true) {
|
|||
return
|
||||
}
|
||||
lastAnnouncementShown = SystemClock.elapsedRealtime()
|
||||
|
||||
fun clearExtras() {
|
||||
for (invalidator in extraInvalidatorList) {
|
||||
invalidator.register(null)
|
||||
}
|
||||
extraInvalidatorList.clear()
|
||||
}
|
||||
llAnnouncementExtra.removeAllViews()
|
||||
clearExtras()
|
||||
|
||||
val listShown = TootAnnouncement.filterShown(column.announcements)
|
||||
if (listShown?.isEmpty() != false) {
|
||||
btnAnnouncements.vg(false)
|
||||
llAnnouncementsBox.vg(false)
|
||||
btnAnnouncementsBadge.vg(false)
|
||||
llColumnHeader.invalidate()
|
||||
showAnnouncementsEmpty()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -97,15 +87,41 @@ fun ColumnViewHolder.showAnnouncements(force: Boolean = true) {
|
|||
return
|
||||
}
|
||||
|
||||
val contentColor = column.getContentColor()
|
||||
|
||||
val item = listShown.find { it.id == column.announcementId }
|
||||
?: listShown[0]
|
||||
|
||||
val itemIndex = listShown.indexOf(item)
|
||||
|
||||
val enablePaging = listShown.size > 1
|
||||
val contentColor = column.getContentColor()
|
||||
|
||||
showAnnouncementColors(expand, enablePaging, contentColor)
|
||||
showAnnouncementFonts()
|
||||
|
||||
tvAnnouncementsIndex.vg(expand)?.text =
|
||||
activity.getString(R.string.announcements_index, itemIndex + 1, listShown.size)
|
||||
|
||||
llAnnouncements.vg(expand)
|
||||
|
||||
showAnnouncementContent(item, contentColor)
|
||||
showReactionBox(column, item, contentColor)
|
||||
}
|
||||
|
||||
private fun ColumnViewHolder.clearExtras() {
|
||||
for (invalidator in extraInvalidatorList) {
|
||||
invalidator.register(null)
|
||||
}
|
||||
extraInvalidatorList.clear()
|
||||
}
|
||||
|
||||
private fun ColumnViewHolder.showAnnouncementsEmpty() {
|
||||
btnAnnouncements.vg(false)
|
||||
llAnnouncementsBox.vg(false)
|
||||
btnAnnouncementsBadge.vg(false)
|
||||
llColumnHeader.invalidate()
|
||||
}
|
||||
|
||||
private fun ColumnViewHolder.showAnnouncementColors(expand: Boolean, enablePaging: Boolean, contentColor: Int) {
|
||||
val alphaPrevNext = if (enablePaging) 1f else 0.3f
|
||||
|
||||
setIconDrawableId(
|
||||
|
@ -134,7 +150,9 @@ fun ColumnViewHolder.showAnnouncements(force: Boolean = true) {
|
|||
tvAnnouncementsCaption.textColor = contentColor
|
||||
tvAnnouncementsIndex.textColor = contentColor
|
||||
tvAnnouncementPeriod.textColor = contentColor
|
||||
}
|
||||
|
||||
private fun ColumnViewHolder.showAnnouncementFonts() {
|
||||
val f = activity.timelineFontSizeSp
|
||||
if (!f.isNaN()) {
|
||||
tvAnnouncementsCaption.textSize = f
|
||||
|
@ -152,11 +170,9 @@ fun ColumnViewHolder.showAnnouncements(force: Boolean = true) {
|
|||
tvAnnouncementsIndex.typeface = fontNormal
|
||||
tvAnnouncementPeriod.typeface = fontNormal
|
||||
tvAnnouncementContent.typeface = fontNormal
|
||||
}
|
||||
|
||||
tvAnnouncementsIndex.vg(expand)?.text =
|
||||
activity.getString(R.string.announcements_index, itemIndex + 1, listShown.size)
|
||||
llAnnouncements.vg(expand)
|
||||
|
||||
private fun ColumnViewHolder.showAnnouncementContent(item: TootAnnouncement, contentColor: Int) {
|
||||
var periods: StringBuilder? = null
|
||||
fun String.appendPeriod() {
|
||||
val sb = periods
|
||||
|
@ -168,14 +184,9 @@ fun ColumnViewHolder.showAnnouncements(force: Boolean = true) {
|
|||
}
|
||||
}
|
||||
|
||||
val (strStart, strEnd) = TootStatus.formatTimeRange(
|
||||
item.starts_at,
|
||||
item.ends_at,
|
||||
item.all_day
|
||||
)
|
||||
val (strStart, strEnd) = TootStatus.formatTimeRange(item.starts_at, item.ends_at, item.all_day)
|
||||
|
||||
when {
|
||||
|
||||
// no periods.
|
||||
strStart == "" && strEnd == "" -> {
|
||||
}
|
||||
|
@ -199,19 +210,23 @@ fun ColumnViewHolder.showAnnouncements(force: Boolean = true) {
|
|||
|
||||
val sb = periods
|
||||
tvAnnouncementPeriod.vg(sb != null)?.text = sb
|
||||
|
||||
tvAnnouncementContent.textColor = contentColor
|
||||
tvAnnouncementContent.text = item.decoded_content
|
||||
tvAnnouncementContent.tag = this@showAnnouncements
|
||||
tvAnnouncementContent.tag = this
|
||||
announcementContentInvalidator.register(item.decoded_content)
|
||||
}
|
||||
|
||||
private fun ColumnViewHolder.showReactionBox(
|
||||
column: Column,
|
||||
item: TootAnnouncement,
|
||||
contentColor: Int,
|
||||
) {
|
||||
// リアクションの表示
|
||||
|
||||
val density = activity.density
|
||||
|
||||
val buttonHeight = ActMain.boostButtonSize
|
||||
val marginBetween = (buttonHeight.toFloat() * 0.2f + 0.5f).toInt()
|
||||
val marginBottom = (buttonHeight.toFloat() * 0.2f + 0.5f).toInt()
|
||||
|
||||
val paddingH = (buttonHeight.toFloat() * 0.1f + 0.5f).toInt()
|
||||
val paddingV = (buttonHeight.toFloat() * 0.1f + 0.5f).toInt()
|
||||
|
@ -226,135 +241,155 @@ fun ColumnViewHolder.showAnnouncements(force: Boolean = true) {
|
|||
topMargin = (0.5f + density * 3f).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
// +ボタン
|
||||
run {
|
||||
val b = ImageButton(activity)
|
||||
val blp = FlexboxLayout.LayoutParams(
|
||||
buttonHeight,
|
||||
buttonHeight
|
||||
).apply {
|
||||
bottomMargin = marginBottom
|
||||
endMargin = marginBetween
|
||||
}
|
||||
b.layoutParams = blp
|
||||
b.background = ContextCompat.getDrawable(
|
||||
activity,
|
||||
R.drawable.btn_bg_transparent_round6dp
|
||||
)
|
||||
|
||||
b.contentDescription = activity.getString(R.string.reaction_add)
|
||||
b.scaleType = ImageView.ScaleType.FIT_CENTER
|
||||
b.padding = paddingV
|
||||
b.setOnClickListener {
|
||||
reactionAdd(item, null)
|
||||
}
|
||||
|
||||
setIconDrawableId(
|
||||
activity,
|
||||
b,
|
||||
R.drawable.ic_add,
|
||||
color = contentColor,
|
||||
alphaMultiplier = 1f
|
||||
)
|
||||
|
||||
box.addView(b)
|
||||
}
|
||||
val reactions = item.reactions?.filter { it.count > 0L }?.notEmpty()
|
||||
if (reactions != null) {
|
||||
|
||||
var lastButton: View? = null
|
||||
|
||||
val options = DecodeOptions(
|
||||
activity,
|
||||
column.accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
mentionDefaultHostDomain = column.accessInfo
|
||||
)
|
||||
|
||||
val actMain = activity
|
||||
val disableEmojiAnimation = Pref.bpDisableEmojiAnimation(actMain.pref)
|
||||
|
||||
for (reaction in reactions) {
|
||||
|
||||
val url = if (disableEmojiAnimation) {
|
||||
reaction.staticUrl.notEmpty() ?: reaction.url.notEmpty()
|
||||
} else {
|
||||
reaction.url.notEmpty() ?: reaction.staticUrl.notEmpty()
|
||||
}
|
||||
|
||||
val b = Button(activity).also { btn ->
|
||||
btn.layoutParams = FlexboxLayout.LayoutParams(
|
||||
FlexboxLayout.LayoutParams.WRAP_CONTENT,
|
||||
buttonHeight
|
||||
).apply {
|
||||
endMargin = marginBetween
|
||||
bottomMargin = marginBottom
|
||||
}
|
||||
btn.minWidthCompat = buttonHeight
|
||||
|
||||
btn.allCaps = false
|
||||
btn.tag = reaction
|
||||
|
||||
btn.background = if (reaction.me) {
|
||||
getAdaptiveRippleDrawableRound(
|
||||
actMain,
|
||||
actMain.attrColor(R.attr.colorButtonBgCw),
|
||||
actMain.attrColor(R.attr.colorRippleEffect)
|
||||
)
|
||||
} else {
|
||||
ContextCompat.getDrawable(actMain, R.drawable.btn_bg_transparent_round6dp)
|
||||
}
|
||||
|
||||
btn.setTextColor(contentColor)
|
||||
|
||||
btn.setPadding(paddingH, paddingV, paddingH, paddingV)
|
||||
|
||||
btn.text = if (url == null) {
|
||||
EmojiDecoder.decodeEmoji(options, "${reaction.name} ${reaction.count}")
|
||||
} else {
|
||||
SpannableStringBuilder("${reaction.name} ${reaction.count}").also { sb ->
|
||||
sb.setSpan(
|
||||
NetworkEmojiSpan(url, scale = 1.5f),
|
||||
0,
|
||||
reaction.name.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
val invalidator =
|
||||
NetworkEmojiInvalidator(actMain.handler, btn)
|
||||
invalidator.register(sb)
|
||||
extraInvalidatorList.add(invalidator)
|
||||
}
|
||||
}
|
||||
|
||||
btn.setOnClickListener {
|
||||
if (reaction.me) {
|
||||
reactionRemove(item, reaction.name)
|
||||
} else {
|
||||
reactionAdd(item, TootReaction.parseFedibird(jsonObject {
|
||||
put("name", reaction.name)
|
||||
put("count", 1)
|
||||
put("me", true)
|
||||
putNotNull("url", reaction.url)
|
||||
putNotNull("static_url", reaction.staticUrl)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
box.addView(b)
|
||||
lastButton = b
|
||||
}
|
||||
|
||||
lastButton
|
||||
?.layoutParams
|
||||
?.cast<ViewGroup.MarginLayoutParams>()
|
||||
?.endMargin = 0
|
||||
}
|
||||
|
||||
showReactionPlus(box, item, buttonHeight, marginBetween, contentColor, paddingV)
|
||||
item.reactions?.filter { it.count > 0L }
|
||||
?.notEmpty()
|
||||
?.let { showReactions(box, item, it, column, buttonHeight, marginBetween, contentColor, paddingH, paddingV) }
|
||||
llAnnouncementExtra.addView(box)
|
||||
}
|
||||
|
||||
private fun ColumnViewHolder.showReactionPlus(
|
||||
box: FlexboxLayout,
|
||||
item: TootAnnouncement,
|
||||
buttonHeight: Int,
|
||||
marginBetween: Int,
|
||||
contentColor: Int,
|
||||
paddingV: Int,
|
||||
) {
|
||||
val b = ImageButton(activity)
|
||||
val blp = FlexboxLayout.LayoutParams(
|
||||
buttonHeight,
|
||||
buttonHeight
|
||||
).apply {
|
||||
bottomMargin = marginBottom
|
||||
endMargin = marginBetween
|
||||
}
|
||||
b.layoutParams = blp
|
||||
b.background = ContextCompat.getDrawable(
|
||||
activity,
|
||||
R.drawable.btn_bg_transparent_round6dp
|
||||
)
|
||||
|
||||
b.contentDescription = activity.getString(R.string.reaction_add)
|
||||
b.scaleType = ImageView.ScaleType.FIT_CENTER
|
||||
b.padding = paddingV
|
||||
b.setOnClickListener {
|
||||
reactionAdd(item, null)
|
||||
}
|
||||
|
||||
setIconDrawableId(
|
||||
activity,
|
||||
b,
|
||||
R.drawable.ic_add,
|
||||
color = contentColor,
|
||||
alphaMultiplier = 1f
|
||||
)
|
||||
|
||||
box.addView(b)
|
||||
}
|
||||
|
||||
private fun ColumnViewHolder.showReactions(
|
||||
box: FlexboxLayout,
|
||||
item: TootAnnouncement,
|
||||
reactions: List<TootReaction>,
|
||||
column: Column,
|
||||
buttonHeight: Int,
|
||||
marginBetween: Int,
|
||||
contentColor: Int,
|
||||
paddingH: Int,
|
||||
paddingV: Int,
|
||||
) {
|
||||
|
||||
var lastButton: View? = null
|
||||
|
||||
val options = DecodeOptions(
|
||||
activity,
|
||||
column.accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
mentionDefaultHostDomain = column.accessInfo
|
||||
)
|
||||
|
||||
val actMain = activity
|
||||
val disableEmojiAnimation = PrefB.bpDisableEmojiAnimation(actMain.pref)
|
||||
|
||||
for (reaction in reactions) {
|
||||
|
||||
val url = if (disableEmojiAnimation) {
|
||||
reaction.staticUrl.notEmpty() ?: reaction.url.notEmpty()
|
||||
} else {
|
||||
reaction.url.notEmpty() ?: reaction.staticUrl.notEmpty()
|
||||
}
|
||||
|
||||
val b = Button(activity).also { btn ->
|
||||
btn.layoutParams = FlexboxLayout.LayoutParams(
|
||||
FlexboxLayout.LayoutParams.WRAP_CONTENT,
|
||||
buttonHeight
|
||||
).apply {
|
||||
endMargin = marginBetween
|
||||
bottomMargin = marginBottom
|
||||
}
|
||||
btn.minWidthCompat = buttonHeight
|
||||
|
||||
btn.allCaps = false
|
||||
btn.tag = reaction
|
||||
|
||||
btn.background = if (reaction.me) {
|
||||
getAdaptiveRippleDrawableRound(
|
||||
actMain,
|
||||
actMain.attrColor(R.attr.colorButtonBgCw),
|
||||
actMain.attrColor(R.attr.colorRippleEffect)
|
||||
)
|
||||
} else {
|
||||
ContextCompat.getDrawable(actMain, R.drawable.btn_bg_transparent_round6dp)
|
||||
}
|
||||
|
||||
btn.setTextColor(contentColor)
|
||||
|
||||
btn.setPadding(paddingH, paddingV, paddingH, paddingV)
|
||||
|
||||
btn.text = if (url == null) {
|
||||
EmojiDecoder.decodeEmoji(options, "${reaction.name} ${reaction.count}")
|
||||
} else {
|
||||
SpannableStringBuilder("${reaction.name} ${reaction.count}").also { sb ->
|
||||
sb.setSpan(
|
||||
NetworkEmojiSpan(url, scale = 1.5f),
|
||||
0,
|
||||
reaction.name.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
val invalidator =
|
||||
NetworkEmojiInvalidator(actMain.handler, btn)
|
||||
invalidator.register(sb)
|
||||
extraInvalidatorList.add(invalidator)
|
||||
}
|
||||
}
|
||||
|
||||
btn.setOnClickListener {
|
||||
if (reaction.me) {
|
||||
reactionRemove(item, reaction.name)
|
||||
} else {
|
||||
reactionAdd(item, TootReaction.parseFedibird(jsonObject {
|
||||
put("name", reaction.name)
|
||||
put("count", 1)
|
||||
put("me", true)
|
||||
putNotNull("url", reaction.url)
|
||||
putNotNull("static_url", reaction.staticUrl)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
box.addView(b)
|
||||
lastButton = b
|
||||
}
|
||||
|
||||
lastButton
|
||||
?.layoutParams
|
||||
?.cast<ViewGroup.MarginLayoutParams>()
|
||||
?.endMargin = 0
|
||||
}
|
||||
|
||||
fun ColumnViewHolder.reactionAdd(item: TootAnnouncement, sample: TootReaction?) {
|
||||
val column = column ?: return
|
||||
if (sample == null) {
|
||||
|
|
|
@ -31,7 +31,7 @@ fun ColumnViewHolder.closeBitmaps() {
|
|||
|
||||
fun ColumnViewHolder.loadBackgroundImage(iv: ImageView, url: String?) {
|
||||
try {
|
||||
if (url == null || url.isEmpty() || Pref.bpDontShowColumnBackgroundImage(activity.pref)) {
|
||||
if (url == null || url.isEmpty() || PrefB.bpDontShowColumnBackgroundImage(activity.pref)) {
|
||||
// 指定がないなら閉じる
|
||||
closeBitmaps()
|
||||
return
|
||||
|
@ -111,7 +111,7 @@ fun ColumnViewHolder.onPageCreate(column: Column, pageIdx: Int, pageCount: Int)
|
|||
ColumnViewHolder.log.d("onPageCreate [$pageIdx] ${column.getColumnName(true)}")
|
||||
|
||||
val bSimpleList =
|
||||
column.type != ColumnType.CONVERSATION && Pref.bpSimpleList(activity.pref)
|
||||
column.type != ColumnType.CONVERSATION && PrefB.bpSimpleList(activity.pref)
|
||||
|
||||
tvColumnIndex.text = activity.getString(R.string.column_index, pageIdx + 1, pageCount)
|
||||
tvColumnStatus.text = "?"
|
||||
|
@ -203,7 +203,7 @@ fun ColumnViewHolder.onPageCreate(column: Column, pageIdx: Int, pageCount: Int)
|
|||
btnEmojiAdd.vg(false)
|
||||
|
||||
etSearch.vg(true)
|
||||
btnSearchClear.vg(Pref.bpShowSearchClear(activity.pref))
|
||||
btnSearchClear.vg(PrefB.bpShowSearchClear(activity.pref))
|
||||
cbResolve.vg(column.type == ColumnType.SEARCH)
|
||||
}
|
||||
|
||||
|
@ -254,7 +254,7 @@ fun ColumnViewHolder.onPageCreate(column: Column, pageIdx: Int, pageCount: Int)
|
|||
listView.adapter = statusAdapter
|
||||
|
||||
//XXX FastScrollerのサポートを諦める。ライブラリはいくつかあるんだけど、設定でON/OFFできなかったり頭文字バブルを無効にできなかったり
|
||||
// listView.isFastScrollEnabled = ! Pref.bpDisableFastScroller(Pref.pref(activity))
|
||||
// listView.isFastScrollEnabled = ! PrefB.bpDisableFastScroller(Pref.pref(activity))
|
||||
|
||||
column.addColumnViewHolder(this)
|
||||
|
||||
|
@ -263,7 +263,7 @@ fun ColumnViewHolder.onPageCreate(column: Column, pageIdx: Int, pageCount: Int)
|
|||
fun dip(dp: Int): Int = (activity.density * dp + 0.5f).toInt()
|
||||
val context = activity
|
||||
|
||||
val announcementsBgColor = Pref.ipAnnouncementsBgColor(App1.pref).notZero()
|
||||
val announcementsBgColor = PrefI.ipAnnouncementsBgColor(App1.pref).notZero()
|
||||
?: context.attrColor(R.attr.colorSearchFormBackground)
|
||||
|
||||
btnAnnouncementsCutout.apply {
|
||||
|
@ -276,7 +276,7 @@ fun ColumnViewHolder.onPageCreate(column: Column, pageIdx: Int, pageCount: Int)
|
|||
setPadding(0, padV, 0, padV)
|
||||
}
|
||||
|
||||
val searchBgColor = Pref.ipSearchBgColor(App1.pref).notZero()
|
||||
val searchBgColor = PrefI.ipSearchBgColor(App1.pref).notZero()
|
||||
?: context.attrColor(R.attr.colorSearchFormBackground)
|
||||
|
||||
llSearch.apply {
|
||||
|
|
|
@ -27,7 +27,7 @@ fun ColumnViewHolder.showQuickFilter() {
|
|||
btnQuickFilterReaction.vg(column.isMisskey)
|
||||
btnQuickFilterFavourite.vg(!column.isMisskey)
|
||||
|
||||
val insideColumnSetting = Pref.bpMoveNotificationsQuickFilter(activity.pref)
|
||||
val insideColumnSetting = PrefB.bpMoveNotificationsQuickFilter(activity.pref)
|
||||
|
||||
val showQuickFilterButton: (btn: View, iconId: Int, selected: Boolean) -> Unit
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ internal fun ColumnViewHolder.showContent(
|
|||
refreshLayout.isRefreshing = false
|
||||
showRefreshError()
|
||||
}
|
||||
procRestorescrollposition.run()
|
||||
procRestoreScrollPosition.run()
|
||||
}
|
||||
|
||||
fun ColumnViewHolder.showColumnSetting(show: Boolean): Boolean {
|
||||
|
|
|
@ -266,7 +266,7 @@ internal class DlgContextMenu(
|
|||
} else {
|
||||
val statusByMe = accessInfo.isMe(status.account)
|
||||
|
||||
if (Pref.bpLinksInContextMenu(activity.pref) && contentTextView != null) {
|
||||
if (PrefB.bpLinksInContextMenu(activity.pref) && contentTextView != null) {
|
||||
|
||||
var insPos = 0
|
||||
|
||||
|
@ -344,11 +344,11 @@ internal class DlgContextMenu(
|
|||
llNotification.vg(notification != null)
|
||||
|
||||
val colorButtonAccent =
|
||||
Pref.ipButtonFollowingColor(activity.pref).notZero()
|
||||
PrefI.ipButtonFollowingColor(activity.pref).notZero()
|
||||
?: activity.attrColor(R.attr.colorImageButtonAccent)
|
||||
|
||||
val colorButtonError =
|
||||
Pref.ipButtonFollowRequestColor(activity.pref).notZero()
|
||||
PrefI.ipButtonFollowRequestColor(activity.pref).notZero()
|
||||
?: activity.attrColor(R.attr.colorRegexFilterError)
|
||||
|
||||
val colorButtonNormal =
|
||||
|
@ -426,7 +426,7 @@ internal class DlgContextMenu(
|
|||
)
|
||||
|
||||
btnDomainTimeline.vg(
|
||||
Pref.bpEnableDomainTimeline(activity.pref) &&
|
||||
PrefB.bpEnableDomainTimeline(activity.pref) &&
|
||||
!accessInfo.isPseudo &&
|
||||
!accessInfo.isMisskey
|
||||
)
|
||||
|
@ -563,7 +563,7 @@ internal class DlgContextMenu(
|
|||
}
|
||||
|
||||
when {
|
||||
Pref.bpAlwaysExpandContextMenuItems(activity.pref) -> {
|
||||
PrefB.bpAlwaysExpandContextMenuItems(activity.pref) -> {
|
||||
group.vg(true)
|
||||
btn.background = null
|
||||
}
|
||||
|
@ -638,7 +638,7 @@ internal class DlgContextMenu(
|
|||
private fun ActMain.onClickUser(v: View, pos: Int, who: TootAccount, whoRef: TootAccountRef): Boolean {
|
||||
when (v.id) {
|
||||
R.id.btnReportUser -> userReportForm(accessInfo, who)
|
||||
R.id.btnFollow -> clickFollow(pos, accessInfo, who, whoRef, relation)
|
||||
R.id.btnFollow -> clickFollow(pos, accessInfo, whoRef, relation)
|
||||
R.id.btnMute -> clickMute(accessInfo, who, relation)
|
||||
R.id.btnBlock -> clickBlock(accessInfo, who, relation)
|
||||
R.id.btnAccountText -> launchActText(ActText.createIntent(activity, accessInfo, who))
|
||||
|
@ -763,7 +763,7 @@ internal class DlgContextMenu(
|
|||
activity.mentionFromAnotherAccount(accessInfo, who)
|
||||
}
|
||||
|
||||
R.id.btnMute -> activity.userMuteFromAnotherAccount(who, accessInfo)
|
||||
R.id.btnMute -> activity.userMuteFromAnotherAccount(who, accessInfo)
|
||||
R.id.btnBlock -> activity.userBlockFromAnotherAccount(who, accessInfo)
|
||||
R.id.btnQuoteAnotherAccount -> activity.quoteFromAnotherAccount(accessInfo, status)
|
||||
R.id.btnQuoteTootBT -> activity.quoteFromAnotherAccount(accessInfo, status?.reblogParent)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,18 +1,15 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import jp.juggler.subwaytooter.action.*
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.ContentWarning
|
||||
import jp.juggler.subwaytooter.table.MediaShown
|
||||
import jp.juggler.subwaytooter.util.openCustomTab
|
||||
import jp.juggler.util.encodePercent
|
||||
import jp.juggler.util.cast
|
||||
import jp.juggler.util.notEmpty
|
||||
import jp.juggler.util.showToast
|
||||
import jp.juggler.util.vg
|
||||
|
||||
val defaultBoostedAction: ItemViewHolder.() -> Unit = {
|
||||
val pos = activity.nextPosition(column)
|
||||
|
@ -26,446 +23,108 @@ val defaultBoostedAction: ItemViewHolder.() -> Unit = {
|
|||
}
|
||||
}
|
||||
|
||||
fun ItemViewHolder.openConversationSummary() {
|
||||
val cs = item as? TootConversationSummary ?: return
|
||||
|
||||
if (activity.conversationUnreadClear(accessInfo, cs)) {
|
||||
listAdapter.notifyChange(
|
||||
reason = "ConversationSummary reset unread",
|
||||
reset = true
|
||||
)
|
||||
}
|
||||
activity.conversation(
|
||||
activity.nextPosition(column),
|
||||
accessInfo,
|
||||
cs.last_status
|
||||
)
|
||||
}
|
||||
|
||||
fun ItemViewHolder.openFilterMenu(item: TootFilter) {
|
||||
val ad = ActionsDialog()
|
||||
ad.addAction(activity.getString(R.string.edit)) {
|
||||
ActKeywordFilter.open(activity, accessInfo, item.id)
|
||||
}
|
||||
ad.addAction(activity.getString(R.string.delete)) {
|
||||
activity.filterDelete(accessInfo, item)
|
||||
}
|
||||
ad.show(activity, activity.getString(R.string.filter_of, item.phrase))
|
||||
}
|
||||
|
||||
fun ItemViewHolder.onClickImpl(v: View?) {
|
||||
v ?: return
|
||||
|
||||
val pos = activity.nextPosition(column)
|
||||
val item = this.item
|
||||
val notification = (item as? TootNotification)
|
||||
when (v) {
|
||||
with(activity) {
|
||||
when (v) {
|
||||
ivMedia1 -> clickMedia(0)
|
||||
ivMedia2 -> clickMedia(1)
|
||||
ivMedia3 -> clickMedia(2)
|
||||
ivMedia4 -> clickMedia(3)
|
||||
btnHideMedia, btnCardImageHide -> showHideMediaViews(false)
|
||||
btnShowMedia, btnCardImageShow -> showHideMediaViews(true)
|
||||
btnContentWarning -> toggleContentWarning()
|
||||
ivAvatar -> clickAvatar(pos)
|
||||
llBoosted -> boostedAction()
|
||||
llReply -> clickReplyInfo(pos, accessInfo, column.type, statusReply, statusShowing)
|
||||
|
||||
btnHideMedia, btnCardImageHide -> {
|
||||
fun hideViews() {
|
||||
llMedia.visibility = View.GONE
|
||||
btnShowMedia.visibility = View.VISIBLE
|
||||
llCardImage.visibility = View.GONE
|
||||
btnCardImageShow.visibility = View.VISIBLE
|
||||
llFollow -> clickFollowInfo(pos, accessInfo, followAccount) { whoRef ->
|
||||
DlgContextMenu(this, column, whoRef, null, (item as? TootNotification), tvContent).show()
|
||||
}
|
||||
statusShowing?.let { status ->
|
||||
MediaShown.save(status, false)
|
||||
hideViews()
|
||||
}
|
||||
if (item is TootScheduled) {
|
||||
MediaShown.save(item.uri, false)
|
||||
hideViews()
|
||||
btnFollow -> clickFollowInfo(pos, accessInfo, followAccount, forceMenu = true) { whoRef ->
|
||||
DlgContextMenu(this, column, whoRef, null, (item as? TootNotification), tvContent).show()
|
||||
}
|
||||
|
||||
btnGapHead -> column.startGap(item.cast(), isHead = true)
|
||||
btnGapTail -> column.startGap(item.cast(), isHead = false)
|
||||
btnSearchTag, llTrendTag -> clickTag(pos, item)
|
||||
btnListTL -> clickListTl(pos, accessInfo, item)
|
||||
btnListMore -> clickListMoreButton(pos, accessInfo, item)
|
||||
btnFollowRequestAccept -> clickFollowRequestAccept(accessInfo, followAccount, accept = true)
|
||||
btnFollowRequestDeny -> clickFollowRequestAccept(accessInfo, followAccount, accept = false)
|
||||
llFilter -> openFilterMenu(accessInfo, item.cast())
|
||||
ivCardImage -> clickCardImage(pos, accessInfo, statusShowing?.card)
|
||||
llConversationIcons -> clickConversation(pos, accessInfo, listAdapter, summary = item.cast())
|
||||
}
|
||||
|
||||
btnShowMedia, btnCardImageShow -> {
|
||||
fun showViews() {
|
||||
llMedia.visibility = View.VISIBLE
|
||||
btnShowMedia.visibility = View.GONE
|
||||
llCardImage.visibility = View.VISIBLE
|
||||
btnCardImageShow.visibility = View.GONE
|
||||
}
|
||||
statusShowing?.let { status ->
|
||||
MediaShown.save(status, true)
|
||||
showViews()
|
||||
}
|
||||
if (item is TootScheduled) {
|
||||
MediaShown.save(item.uri, true)
|
||||
showViews()
|
||||
}
|
||||
}
|
||||
|
||||
ivMedia1 -> clickMedia(0)
|
||||
ivMedia2 -> clickMedia(1)
|
||||
ivMedia3 -> clickMedia(2)
|
||||
ivMedia4 -> clickMedia(3)
|
||||
|
||||
btnContentWarning -> {
|
||||
statusShowing?.let { status ->
|
||||
val newShown = llContents.visibility == View.GONE
|
||||
ContentWarning.save(status, newShown)
|
||||
|
||||
// 1個だけ開閉するのではなく、例えば通知TLにある複数の要素をまとめて開閉するなどある
|
||||
listAdapter.notifyChange(reason = "ContentWarning onClick", reset = true)
|
||||
}
|
||||
|
||||
if (item is TootScheduled) {
|
||||
val newShown = llContents.visibility == View.GONE
|
||||
ContentWarning.save(item.uri, newShown)
|
||||
|
||||
// 1個だけ開閉するのではなく、例えば通知TLにある複数の要素をまとめて開閉するなどある
|
||||
listAdapter.notifyChange(reason = "ContentWarning onClick", reset = true)
|
||||
}
|
||||
}
|
||||
|
||||
ivThumbnail -> statusAccount?.let { whoRef ->
|
||||
when {
|
||||
accessInfo.isNA -> DlgContextMenu(
|
||||
activity,
|
||||
column,
|
||||
whoRef,
|
||||
null,
|
||||
notification,
|
||||
tvContent
|
||||
).show()
|
||||
|
||||
// 2018/12/26 疑似アカウントでもプロフカラムを表示する https://github.com/tootsuite/mastodon/commit/108b2139cd87321f6c0aec63ef93db85ce30bfec
|
||||
|
||||
else -> activity.userProfileLocal(
|
||||
|
||||
pos,
|
||||
accessInfo,
|
||||
whoRef.get()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
llBoosted -> boostedAction()
|
||||
|
||||
llReply -> {
|
||||
val s = statusReply
|
||||
|
||||
when {
|
||||
s != null -> activity.conversation(pos, accessInfo, s)
|
||||
|
||||
// tootsearchは返信元のIDを取得するのにひと手間必要
|
||||
column.type == ColumnType.SEARCH_TS ||
|
||||
column.type == ColumnType.SEARCH_NOTESTOCK ->
|
||||
activity.conversationFromTootsearch(pos, statusShowing)
|
||||
|
||||
else -> {
|
||||
val id = statusShowing?.in_reply_to_id
|
||||
if (id != null) {
|
||||
activity.conversationLocal(pos, accessInfo, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
llFollow -> followAccount?.let { whoRef ->
|
||||
if (accessInfo.isPseudo) {
|
||||
DlgContextMenu(activity, column, whoRef, null, notification, tvContent).show()
|
||||
} else {
|
||||
activity.userProfileLocal(pos, accessInfo, whoRef.get())
|
||||
}
|
||||
}
|
||||
|
||||
btnFollow -> followAccount?.let { who ->
|
||||
DlgContextMenu(activity, column, who, null, notification, tvContent).show()
|
||||
}
|
||||
|
||||
btnGapHead -> when (item) {
|
||||
is TootGap -> column.startGap(item, isHead = true)
|
||||
}
|
||||
|
||||
btnGapTail -> when (item) {
|
||||
is TootGap -> column.startGap(item, isHead = false)
|
||||
}
|
||||
|
||||
btnSearchTag, llTrendTag -> when (item) {
|
||||
|
||||
is TootConversationSummary -> openConversationSummary()
|
||||
|
||||
is TootGap -> when {
|
||||
column.type.gapDirection(column, true) ->
|
||||
column.startGap(item, isHead = true)
|
||||
|
||||
column.type.gapDirection(column, false) ->
|
||||
column.startGap(item, isHead = false)
|
||||
|
||||
else ->
|
||||
activity.showToast(true, "This column can't support gap reading.")
|
||||
}
|
||||
|
||||
is TootSearchGap -> column.startGap(item, isHead = true)
|
||||
|
||||
is TootDomainBlock -> {
|
||||
AlertDialog.Builder(activity)
|
||||
.setMessage(
|
||||
activity.getString(
|
||||
R.string.confirm_unblock_domain,
|
||||
item.domain.pretty
|
||||
)
|
||||
)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
activity.domainBlock(
|
||||
accessInfo,
|
||||
item.domain,
|
||||
bBlock = false
|
||||
)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
is TootTag -> {
|
||||
activity.tagTimeline(
|
||||
activity.nextPosition(column),
|
||||
accessInfo,
|
||||
item.name // #を含まない
|
||||
)
|
||||
}
|
||||
|
||||
is TootScheduled -> {
|
||||
ActionsDialog()
|
||||
.addAction(activity.getString(R.string.edit)) {
|
||||
activity.scheduledPostEdit(accessInfo, item)
|
||||
}
|
||||
.addAction(activity.getString(R.string.delete)) {
|
||||
activity.scheduledPostDelete(accessInfo, item) {
|
||||
column.onScheduleDeleted(item)
|
||||
activity.showToast(false, R.string.scheduled_post_deleted)
|
||||
}
|
||||
}
|
||||
.show(activity)
|
||||
}
|
||||
}
|
||||
|
||||
btnListTL -> if (item is TootList) {
|
||||
activity.addColumn(pos, accessInfo, ColumnType.LIST_TL, item.id)
|
||||
} else if (item is MisskeyAntenna) {
|
||||
// TODO
|
||||
activity.addColumn(pos, accessInfo, ColumnType.MISSKEY_ANTENNA_TL, item.id)
|
||||
}
|
||||
|
||||
btnListMore -> when (item) {
|
||||
is TootList -> {
|
||||
ActionsDialog()
|
||||
.addAction(activity.getString(R.string.list_timeline)) {
|
||||
activity.addColumn(pos, accessInfo, ColumnType.LIST_TL, item.id)
|
||||
}
|
||||
.addAction(activity.getString(R.string.list_member)) {
|
||||
activity.addColumn(
|
||||
false,
|
||||
pos,
|
||||
accessInfo,
|
||||
ColumnType.LIST_MEMBER,
|
||||
item.id
|
||||
)
|
||||
}
|
||||
.addAction(activity.getString(R.string.rename)) {
|
||||
activity.listRename(accessInfo, item)
|
||||
}
|
||||
.addAction(activity.getString(R.string.delete)) {
|
||||
activity.listDelete(accessInfo, item)
|
||||
}
|
||||
.show(activity, item.title)
|
||||
}
|
||||
|
||||
is MisskeyAntenna -> {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
btnFollowRequestAccept -> followAccount?.let { whoRef ->
|
||||
val who = whoRef.get()
|
||||
DlgConfirm.openSimple(
|
||||
activity,
|
||||
activity.getString(
|
||||
R.string.follow_accept_confirm,
|
||||
AcctColor.getNickname(accessInfo, who)
|
||||
)
|
||||
) {
|
||||
activity.followRequestAuthorize(accessInfo, whoRef, true)
|
||||
}
|
||||
}
|
||||
|
||||
btnFollowRequestDeny -> followAccount?.let { whoRef ->
|
||||
val who = whoRef.get()
|
||||
DlgConfirm.openSimple(
|
||||
activity,
|
||||
activity.getString(
|
||||
R.string.follow_deny_confirm,
|
||||
AcctColor.getNickname(accessInfo, who)
|
||||
)
|
||||
) {
|
||||
activity.followRequestAuthorize(accessInfo, whoRef, false)
|
||||
}
|
||||
}
|
||||
|
||||
llFilter -> if (item is TootFilter) {
|
||||
openFilterMenu(item)
|
||||
}
|
||||
|
||||
ivCardImage -> statusShowing?.card?.let { card ->
|
||||
val originalStatus = card.originalStatus
|
||||
if (originalStatus != null) {
|
||||
activity.conversation(
|
||||
activity.nextPosition(column),
|
||||
accessInfo,
|
||||
originalStatus
|
||||
)
|
||||
} else {
|
||||
val url = card.url
|
||||
if (url?.isNotEmpty() == true) {
|
||||
openCustomTab(
|
||||
activity,
|
||||
pos,
|
||||
url,
|
||||
accessInfo = accessInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
llConversationIcons -> openConversationSummary()
|
||||
}
|
||||
}
|
||||
|
||||
fun ItemViewHolder.onLongClickImpl(v: View?): Boolean {
|
||||
v ?: return false
|
||||
|
||||
val notification = (item as? TootNotification)
|
||||
with(activity) {
|
||||
|
||||
when (v) {
|
||||
val pos = activity.nextPosition(column)
|
||||
when (v) {
|
||||
ivAvatar ->
|
||||
clickAvatar(pos, longClick = true)
|
||||
|
||||
ivThumbnail -> {
|
||||
statusAccount?.let { who ->
|
||||
DlgContextMenu(
|
||||
activity,
|
||||
column,
|
||||
who,
|
||||
null,
|
||||
notification,
|
||||
tvContent
|
||||
).show()
|
||||
}
|
||||
return true
|
||||
}
|
||||
llBoosted ->
|
||||
longClickBoostedInfo(boostAccount)
|
||||
|
||||
llBoosted -> {
|
||||
boostAccount?.let { who ->
|
||||
DlgContextMenu(
|
||||
activity,
|
||||
column,
|
||||
who,
|
||||
null,
|
||||
notification,
|
||||
tvContent
|
||||
).show()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
llReply -> {
|
||||
val s = statusReply
|
||||
when {
|
||||
|
||||
// 返信元のstatusがあるならコンテキストメニュー
|
||||
s != null -> DlgContextMenu(
|
||||
activity,
|
||||
column,
|
||||
s.accountRef,
|
||||
s,
|
||||
notification,
|
||||
tvContent
|
||||
).show()
|
||||
|
||||
// それ以外はコンテキストメニューではなく会話を開く
|
||||
|
||||
// tootsearchは返信元のIDを取得するのにひと手間必要
|
||||
column.type == ColumnType.SEARCH_TS ||
|
||||
column.type == ColumnType.SEARCH_NOTESTOCK ->
|
||||
activity.conversationFromTootsearch(
|
||||
activity.nextPosition(column),
|
||||
statusShowing
|
||||
)
|
||||
|
||||
else -> {
|
||||
val id = statusShowing?.in_reply_to_id
|
||||
if (id != null) {
|
||||
activity.conversationLocal(
|
||||
activity.nextPosition(column),
|
||||
accessInfo,
|
||||
id
|
||||
)
|
||||
}
|
||||
llReply ->
|
||||
clickReplyInfo(pos, accessInfo, column.type, statusReply, statusShowing, longClick = true) { status ->
|
||||
DlgContextMenu(this, column, status.accountRef, status, item.cast(), tvContent).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
llFollow -> {
|
||||
followAccount?.let { whoRef ->
|
||||
DlgContextMenu(
|
||||
activity,
|
||||
column,
|
||||
whoRef,
|
||||
null,
|
||||
notification
|
||||
).show()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
btnFollow -> {
|
||||
followAccount?.let { whoRef ->
|
||||
activity.followFromAnotherAccount(
|
||||
activity.nextPosition(column),
|
||||
accessInfo,
|
||||
whoRef.get()
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
ivCardImage -> activity.conversationOtherInstance(
|
||||
activity.nextPosition(column),
|
||||
statusShowing?.card?.originalStatus
|
||||
)
|
||||
|
||||
btnSearchTag, llTrendTag -> {
|
||||
when (val item = this.item) {
|
||||
// is TootGap -> column.startGap(item)
|
||||
//
|
||||
// is TootDomainBlock -> {
|
||||
// val domain = item.domain
|
||||
// AlertDialog.Builder(activity)
|
||||
// .setMessage(activity.getString(R.string.confirm_unblock_domain, domain))
|
||||
// .setNegativeButton(R.string.cancel, null)
|
||||
// .setPositiveButton(R.string.ok) { _, _ -> Action_Instance.blockDomain(activity, access_info, domain, false) }
|
||||
// .show()
|
||||
// }
|
||||
|
||||
is TootTag -> {
|
||||
// search_tag は#を含まない
|
||||
val tagEncoded = item.name.encodePercent()
|
||||
val url = "https://${accessInfo.apiHost.ascii}/tags/$tagEncoded"
|
||||
activity.tagTimelineFromAccount(
|
||||
pos = activity.nextPosition(column),
|
||||
url = url,
|
||||
host = accessInfo.apiHost,
|
||||
tagWithoutSharp = item.name
|
||||
)
|
||||
llFollow ->
|
||||
followAccount?.let {
|
||||
DlgContextMenu(activity, column, it, null, item.cast(), tvContent).show()
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
btnFollow ->
|
||||
followAccount?.get()?.let { followFromAnotherAccount(pos, accessInfo, it) }
|
||||
|
||||
ivCardImage ->
|
||||
clickCardImage(pos, accessInfo, statusShowing?.card, longClick = true)
|
||||
|
||||
btnSearchTag, llTrendTag ->
|
||||
longClickTag(pos, item)
|
||||
|
||||
else ->
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
fun ItemViewHolder.clickMedia(i: Int) {
|
||||
private fun ItemViewHolder.longClickBoostedInfo(who: TootAccountRef?) {
|
||||
who ?: return
|
||||
DlgContextMenu(activity, column, who, null, item.cast(), tvContent).show()
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.longClickTag(pos: Int, item: TimelineItem?): Boolean {
|
||||
|
||||
when (item) {
|
||||
// is TootGap -> column.startGap(item)
|
||||
//
|
||||
// is TootDomainBlock -> {
|
||||
// val domain = item.domain
|
||||
// AlertDialog.Builder(activity)
|
||||
// .setMessage(activity.getString(R.string.confirm_unblock_domain, domain))
|
||||
// .setNegativeButton(R.string.cancel, null)
|
||||
// .setPositiveButton(R.string.ok) { _, _ -> Action_Instance.blockDomain(activity, access_info, domain, false) }
|
||||
// .show()
|
||||
// }
|
||||
is TootTag -> activity.longClickTootTag(pos, accessInfo, item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.clickMedia(i: Int) {
|
||||
try {
|
||||
val mediaAttachments =
|
||||
statusShowing?.media_attachments ?: (item as? TootScheduled)?.mediaAttachments
|
||||
|
@ -487,7 +146,7 @@ fun ItemViewHolder.clickMedia(i: Int) {
|
|||
item.type == TootAttachmentType.Unknown && mediaAttachments.size == 1 -> {
|
||||
// https://github.com/tateisu/SubwayTooter/pull/119
|
||||
// メディアタイプがunknownの場合、そのほとんどはリモートから来たURLである
|
||||
// Pref.bpPriorLocalURL の状態に関わらずリモートURLがあればそれをブラウザで開く
|
||||
// PrefB.bpPriorLocalURL の状態に関わらずリモートURLがあればそれをブラウザで開く
|
||||
when (val remoteUrl = item.remote_url.notEmpty()) {
|
||||
null -> activity.openCustomTab(item)
|
||||
else -> activity.openCustomTab(remoteUrl)
|
||||
|
@ -495,7 +154,7 @@ fun ItemViewHolder.clickMedia(i: Int) {
|
|||
}
|
||||
|
||||
// 内蔵メディアビューアを使う
|
||||
Pref.bpUseInternalMediaViewer(App1.pref) ->
|
||||
PrefB.bpUseInternalMediaViewer(App1.pref) ->
|
||||
ActMediaViewer.open(
|
||||
activity,
|
||||
when (accessInfo.isMisskey) {
|
||||
|
@ -514,3 +173,57 @@ fun ItemViewHolder.clickMedia(i: Int) {
|
|||
ItemViewHolder.log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showHideMediaViews(show: Boolean) {
|
||||
llMedia.vg(show)
|
||||
llCardImage.vg(show)
|
||||
btnShowMedia.vg(!show)
|
||||
btnCardImageShow.vg(!show)
|
||||
statusShowing?.let { MediaShown.save(it, show) }
|
||||
item.cast<TootScheduled>()?.let { MediaShown.save(it.uri, show) }
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.toggleContentWarning() {
|
||||
// トグル動作
|
||||
val show = llContents.visibility == View.GONE
|
||||
|
||||
statusShowing?.let { ContentWarning.save(it, show) }
|
||||
item.cast<TootScheduled>()?.let { ContentWarning.save(it.uri, show) }
|
||||
|
||||
// 1個だけ開閉するのではなく、例えば通知TLにある複数の要素をまとめて開閉するなどある
|
||||
listAdapter.notifyChange(reason = "ContentWarning onClick", reset = true)
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.clickAvatar(pos: Int, longClick: Boolean = false) {
|
||||
|
||||
statusAccount?.let { whoRef ->
|
||||
when {
|
||||
longClick || accessInfo.isNA ->
|
||||
DlgContextMenu(activity, column, whoRef, null, item.cast(), tvContent).show()
|
||||
|
||||
// 2018/12/26 疑似アカウントでもプロフカラムを表示する https://github.com/tootsuite/mastodon/commit/108b2139cd87321f6c0aec63ef93db85ce30bfec
|
||||
else -> activity.userProfileLocal(pos, accessInfo, whoRef.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.clickTag(pos: Int, item: TimelineItem?) {
|
||||
with(activity) {
|
||||
when (item) {
|
||||
is TootTag -> tagTimeline(pos, accessInfo, item.name)
|
||||
is TootSearchGap -> column.startGap(item, isHead = true)
|
||||
is TootConversationSummary -> clickConversation(pos, accessInfo, listAdapter, summary = item)
|
||||
is TootGap -> clickTootGap(column, item)
|
||||
is TootDomainBlock -> clickDomainBlock(accessInfo, item)
|
||||
is TootScheduled -> clickScheduledToot(accessInfo, item, column)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.clickTootGap(column: Column, item: TootGap) {
|
||||
when {
|
||||
column.type.gapDirection(column, true) -> column.startGap(item, isHead = true)
|
||||
column.type.gapDirection(column, false) -> column.startGap(item, isHead = false)
|
||||
else -> showToast(true, "This column can't support gap reading.")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ private fun addLinkAndCaption(
|
|||
|
||||
fun ItemViewHolder.showPreviewCard(status: TootStatus) {
|
||||
|
||||
if (Pref.bpDontShowPreviewCard(activity.pref)) return
|
||||
if (PrefB.bpDontShowPreviewCard(activity.pref)) return
|
||||
|
||||
val card = status.card ?: return
|
||||
|
||||
|
@ -107,7 +107,7 @@ fun ItemViewHolder.showPreviewCard(status: TootStatus) {
|
|||
if (description != null && description.isNotEmpty()) {
|
||||
if (sb.isNotEmpty()) sb.append("<br>")
|
||||
|
||||
val limit = Pref.spCardDescriptionLength.toInt(activity.pref)
|
||||
val limit = PrefS.spCardDescriptionLength.toInt(activity.pref)
|
||||
|
||||
sb.append(
|
||||
HTMLDecoder.encodeEntity(
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.jetbrains.anko.dip
|
|||
fun ItemViewHolder.makeReactionsView(status: TootStatus) {
|
||||
val reactionSet = status.reactionSet
|
||||
if (reactionSet?.hasReaction() != true) {
|
||||
if (!TootReaction.canReaction(accessInfo) || !Pref.bpKeepReactionSpace(activity.pref)) return
|
||||
if (!TootReaction.canReaction(accessInfo) || !PrefB.bpKeepReactionSpace(activity.pref)) return
|
||||
}
|
||||
|
||||
val density = activity.density
|
||||
|
@ -87,7 +87,7 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
|
|||
// 自分がリアクションしたやつは背景を変える
|
||||
getAdaptiveRippleDrawableRound(
|
||||
act,
|
||||
Pref.ipButtonReactionedColor(act.pref).notZero() ?: act.attrColor(R.attr.colorImageButtonAccent),
|
||||
PrefI.ipButtonReactionedColor(act.pref).notZero() ?: act.attrColor(R.attr.colorImageButtonAccent),
|
||||
act.attrColor(R.attr.colorRippleEffect),
|
||||
roundNormal = true
|
||||
)
|
||||
|
|
|
@ -6,10 +6,8 @@ import android.os.SystemClock
|
|||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
|
@ -19,7 +17,6 @@ import jp.juggler.subwaytooter.span.MyClickableSpan
|
|||
import jp.juggler.subwaytooter.table.*
|
||||
import jp.juggler.subwaytooter.util.Benchmark
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.OpenSticker
|
||||
import jp.juggler.subwaytooter.view.CountImageButton
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
import jp.juggler.util.*
|
||||
|
@ -197,14 +194,14 @@ fun ItemViewHolder.bind(
|
|||
|
||||
item.isQuoteToot -> {
|
||||
// 引用Renote
|
||||
val colorBg = Pref.ipEventBgColorBoost(activity.pref)
|
||||
val colorBg = PrefI.ipEventBgColorBoost(activity.pref)
|
||||
showReply(reblog, R.drawable.ic_repeat, R.string.quote_to)
|
||||
showStatus(item, colorBg)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// 引用なしブースト
|
||||
val colorBg = Pref.ipEventBgColorBoost(activity.pref)
|
||||
val colorBg = PrefI.ipEventBgColorBoost(activity.pref)
|
||||
showBoost(
|
||||
item.accountRef,
|
||||
item.time_created_at,
|
||||
|
@ -218,32 +215,20 @@ fun ItemViewHolder.bind(
|
|||
}
|
||||
|
||||
is TootAccountRef -> showAccount(item)
|
||||
|
||||
is TootNotification -> showNotification(item)
|
||||
|
||||
is TootGap -> showGap()
|
||||
is TootSearchGap -> showSearchGap(item)
|
||||
is TootDomainBlock -> showDomainBlock(item)
|
||||
is TootList -> showList(item)
|
||||
is MisskeyAntenna -> showAntenna(item)
|
||||
|
||||
is TootMessageHolder -> showMessageHolder(item)
|
||||
|
||||
is TootTag -> showSearchTag(item)
|
||||
|
||||
is TootFilter -> showFilter(item)
|
||||
|
||||
is TootConversationSummary -> {
|
||||
showStatusOrReply(item.last_status)
|
||||
showConversationIcons(item)
|
||||
}
|
||||
|
||||
is TootScheduled -> {
|
||||
showScheduled(item)
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
is TootScheduled -> showScheduled(item)
|
||||
}
|
||||
b.report()
|
||||
}
|
||||
|
@ -363,284 +348,12 @@ fun ItemViewHolder.showBoost(
|
|||
setAcct(tvBoostedAcct, accessInfo, who)
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showStatusOrReply(item: TootStatus, colorBgArg: Int = 0) {
|
||||
var colorBg = colorBgArg
|
||||
val reply = item.reply
|
||||
val inReplyToId = item.in_reply_to_id
|
||||
val inReplyToAccountId = item.in_reply_to_account_id
|
||||
when {
|
||||
reply != null -> {
|
||||
showReply(reply, R.drawable.ic_reply, R.string.reply_to)
|
||||
if (colorBgArg == 0) colorBg = Pref.ipEventBgColorMention(activity.pref)
|
||||
}
|
||||
|
||||
inReplyToId != null && inReplyToAccountId != null -> {
|
||||
showReply(item, inReplyToAccountId)
|
||||
if (colorBgArg == 0) colorBg = Pref.ipEventBgColorMention(activity.pref)
|
||||
}
|
||||
}
|
||||
showStatus(item, colorBg)
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showMessageHolder(item: TootMessageHolder) {
|
||||
tvMessageHolder.visibility = View.VISIBLE
|
||||
tvMessageHolder.text = item.text
|
||||
tvMessageHolder.gravity = item.gravity
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showNotification(n: TootNotification) {
|
||||
val nStatus = n.status
|
||||
val nAccountRef = n.accountRef
|
||||
val nAccount = nAccountRef?.get()
|
||||
|
||||
fun showNotificationStatus(item: TootStatus, colorBgDefault: Int) {
|
||||
val reblog = item.reblog
|
||||
when {
|
||||
reblog == null -> showStatusOrReply(item, colorBgDefault)
|
||||
|
||||
item.isQuoteToot -> {
|
||||
// 引用Renote
|
||||
showReply(reblog, R.drawable.ic_repeat, R.string.quote_to)
|
||||
showStatus(item, Pref.ipEventBgColorQuote(activity.pref))
|
||||
}
|
||||
|
||||
else -> {
|
||||
// 通常のブースト。引用なしブースト。
|
||||
// ブースト表示は通知イベントと被るのでしない
|
||||
showStatusOrReply(reblog, Pref.ipEventBgColorBoost(activity.pref))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (n.type) {
|
||||
|
||||
TootNotification.TYPE_FAVOURITE -> {
|
||||
val colorBg = Pref.ipEventBgColorFavourite(activity.pref)
|
||||
if (nAccount != null) showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
if (accessInfo.isNicoru(nAccount)) R.drawable.ic_nicoru else R.drawable.ic_star,
|
||||
R.string.display_name_favourited_by
|
||||
)
|
||||
if (nStatus != null) {
|
||||
showNotificationStatus(nStatus, colorBg)
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_REBLOG -> {
|
||||
val colorBg = Pref.ipEventBgColorBoost(activity.pref)
|
||||
if (nAccount != null) showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_repeat,
|
||||
R.string.display_name_boosted_by,
|
||||
boostStatus = nStatus
|
||||
)
|
||||
if (nStatus != null) {
|
||||
showNotificationStatus(nStatus, colorBg)
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_RENOTE -> {
|
||||
// 引用のないreblog
|
||||
val colorBg = Pref.ipEventBgColorBoost(activity.pref)
|
||||
if (nAccount != null) showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_repeat,
|
||||
R.string.display_name_boosted_by,
|
||||
boostStatus = nStatus
|
||||
)
|
||||
if (nStatus != null) {
|
||||
showNotificationStatus(nStatus, colorBg)
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_FOLLOW -> {
|
||||
val colorBg = Pref.ipEventBgColorFollow(activity.pref)
|
||||
if (nAccount != null) {
|
||||
showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_follow_plus,
|
||||
R.string.display_name_followed_by
|
||||
)
|
||||
showAccount(nAccountRef)
|
||||
if (colorBg != 0) this.viewRoot.backgroundColor = colorBg
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_UNFOLLOW -> {
|
||||
val colorBg = Pref.ipEventBgColorUnfollow(activity.pref)
|
||||
if (nAccount != null) {
|
||||
showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_follow_cross,
|
||||
R.string.display_name_unfollowed_by
|
||||
)
|
||||
showAccount(nAccountRef)
|
||||
if (colorBg != 0) this.viewRoot.backgroundColor = colorBg
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_MENTION,
|
||||
TootNotification.TYPE_REPLY,
|
||||
-> {
|
||||
val colorBg = Pref.ipEventBgColorMention(activity.pref)
|
||||
if (!bSimpleList && !accessInfo.isMisskey) {
|
||||
when {
|
||||
nAccount == null -> {
|
||||
//
|
||||
}
|
||||
|
||||
nStatus?.in_reply_to_id != null || nStatus?.reply != null -> {
|
||||
// トゥート内部に「~への返信」を表示するので、
|
||||
// 通知イベントの「~からの返信」は表示しない
|
||||
}
|
||||
|
||||
else -> // 返信ではなくメンションの場合は「~からの返信」を表示する
|
||||
showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_reply,
|
||||
R.string.display_name_mentioned_by
|
||||
)
|
||||
}
|
||||
}
|
||||
if (nStatus != null) {
|
||||
showNotificationStatus(nStatus, colorBg)
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
|
||||
TootNotification.TYPE_EMOJI_REACTION,
|
||||
TootNotification.TYPE_REACTION,
|
||||
-> {
|
||||
val colorBg = Pref.ipEventBgColorReaction(activity.pref)
|
||||
if (nAccount != null) showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_face,
|
||||
R.string.display_name_reaction_by,
|
||||
reaction = n.reaction ?: TootReaction.UNKNOWN,
|
||||
boostStatus = nStatus
|
||||
)
|
||||
if (nStatus != null) {
|
||||
showNotificationStatus(nStatus, colorBg)
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_QUOTE -> {
|
||||
val colorBg = Pref.ipEventBgColorQuote(activity.pref)
|
||||
if (nAccount != null) showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_repeat,
|
||||
R.string.display_name_quoted_by
|
||||
)
|
||||
if (nStatus != null) {
|
||||
showNotificationStatus(nStatus, colorBg)
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_STATUS -> {
|
||||
val colorBg = Pref.ipEventBgColorStatus(activity.pref)
|
||||
if (nAccount != null) showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
if (nStatus == null) {
|
||||
R.drawable.ic_question
|
||||
} else {
|
||||
Styler.getVisibilityIconId(accessInfo.isMisskey, nStatus.visibility)
|
||||
},
|
||||
R.string.display_name_posted_by
|
||||
)
|
||||
if (nStatus != null) {
|
||||
showNotificationStatus(nStatus, colorBg)
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_FOLLOW_REQUEST,
|
||||
TootNotification.TYPE_FOLLOW_REQUEST_MISSKEY,
|
||||
-> {
|
||||
val colorBg = Pref.ipEventBgColorFollowRequest(activity.pref)
|
||||
if (nAccount != null) {
|
||||
showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_follow_wait,
|
||||
R.string.display_name_follow_request_by
|
||||
)
|
||||
if (colorBg != 0) this.viewRoot.backgroundColor = colorBg
|
||||
boostedAction = {
|
||||
activity.addColumn(
|
||||
activity.nextPosition(column), accessInfo, ColumnType.FOLLOW_REQUESTS
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_FOLLOW_REQUEST_ACCEPTED_MISSKEY -> {
|
||||
val colorBg = Pref.ipEventBgColorFollow(activity.pref)
|
||||
if (nAccount != null) {
|
||||
showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_follow_plus,
|
||||
R.string.display_name_follow_request_accepted_by
|
||||
)
|
||||
showAccount(nAccountRef)
|
||||
if (colorBg != 0) this.viewRoot.backgroundColor = colorBg
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_VOTE,
|
||||
TootNotification.TYPE_POLL_VOTE_MISSKEY,
|
||||
-> {
|
||||
val colorBg = Pref.ipEventBgColorVote(activity.pref)
|
||||
if (nAccount != null) showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_vote,
|
||||
R.string.display_name_voted_by
|
||||
)
|
||||
if (nStatus != null) {
|
||||
showNotificationStatus(nStatus, colorBg)
|
||||
}
|
||||
}
|
||||
|
||||
TootNotification.TYPE_POLL -> {
|
||||
val colorBg = 0
|
||||
if (nAccount != null) showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_vote,
|
||||
R.string.end_of_polling_from
|
||||
)
|
||||
if (nStatus != null) {
|
||||
showNotificationStatus(nStatus, colorBg)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
val colorBg = 0
|
||||
if (nAccount != null) showBoost(
|
||||
nAccountRef,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_question,
|
||||
R.string.unknown_notification_from
|
||||
)
|
||||
if (nStatus != null) {
|
||||
showNotificationStatus(nStatus, colorBg)
|
||||
}
|
||||
tvMessageHolder.visibility = View.VISIBLE
|
||||
tvMessageHolder.text = "notification type is ${n.type}"
|
||||
tvMessageHolder.gravity = Gravity.CENTER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showList(list: TootList) {
|
||||
llList.visibility = View.VISIBLE
|
||||
btnListTL.text = list.title
|
||||
|
@ -705,7 +418,7 @@ fun ItemViewHolder.showGap() {
|
|||
btnGapTail.vg(column.type.gapDirection(column, false))
|
||||
?.imageTintList = contentColorCsl
|
||||
|
||||
val c = Pref.ipEventBgColorGap(App1.pref)
|
||||
val c = PrefI.ipEventBgColorGap(App1.pref)
|
||||
if (c != 0) this.viewRoot.backgroundColor = c
|
||||
}
|
||||
|
||||
|
@ -764,281 +477,6 @@ fun ItemViewHolder.showReply(reply: TootStatus, accountId: EntityId) {
|
|||
// tootsearchではどのタンスから読んだか分からないのでin_reply_toのIDも信用できない
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showStatus(status: TootStatus, colorBg: Int = 0) {
|
||||
|
||||
val filteredWord = status.filteredWord
|
||||
if (filteredWord != null) {
|
||||
showMessageHolder(
|
||||
TootMessageHolder(
|
||||
if (Pref.bpShowFilteredWord(activity.pref)) {
|
||||
"${activity.getString(R.string.filtered)} / $filteredWord"
|
||||
} else {
|
||||
activity.getString(R.string.filtered)
|
||||
}
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
this.statusShowing = status
|
||||
llStatus.visibility = View.VISIBLE
|
||||
|
||||
if (status.conversation_main) {
|
||||
|
||||
val conversationMainBgColor =
|
||||
Pref.ipConversationMainTootBgColor(activity.pref).notZero()
|
||||
?: (activity.attrColor(R.attr.colorImageButtonAccent) and 0xffffff) or 0x20000000
|
||||
|
||||
this.viewRoot.setBackgroundColor(conversationMainBgColor)
|
||||
} else {
|
||||
val c = colorBg.notZero()
|
||||
|
||||
?: when (status.bookmarked) {
|
||||
true -> Pref.ipEventBgColorBookmark(App1.pref)
|
||||
false -> 0
|
||||
}.notZero()
|
||||
|
||||
?: when (status.getBackgroundColorType(accessInfo)) {
|
||||
TootVisibility.UnlistedHome -> ItemViewHolder.toot_color_unlisted
|
||||
TootVisibility.PrivateFollowers -> ItemViewHolder.toot_color_follower
|
||||
TootVisibility.DirectSpecified -> ItemViewHolder.toot_color_direct_user
|
||||
TootVisibility.DirectPrivate -> ItemViewHolder.toot_color_direct_me
|
||||
// TODO add color setting for limited?
|
||||
TootVisibility.Limited -> ItemViewHolder.toot_color_follower
|
||||
else -> 0
|
||||
}
|
||||
|
||||
if (c != 0) {
|
||||
this.viewRoot.backgroundColor = c
|
||||
}
|
||||
}
|
||||
|
||||
showStatusTime(activity, tvTime, who = status.account, status = status)
|
||||
|
||||
val whoRef = status.accountRef
|
||||
val who = whoRef.get()
|
||||
this.statusAccount = whoRef
|
||||
|
||||
setAcct(tvAcct, accessInfo, who)
|
||||
|
||||
// if(who == null) {
|
||||
// tvName.text = "?"
|
||||
// name_invalidator.register(null)
|
||||
// ivThumbnail.setImageUrl(activity.pref, 16f, null, null)
|
||||
// } else {
|
||||
tvName.text = whoRef.decoded_display_name
|
||||
nameInvalidator.register(whoRef.decoded_display_name)
|
||||
ivThumbnail.setImageUrl(
|
||||
activity.pref,
|
||||
Styler.calcIconRound(ivThumbnail.layoutParams),
|
||||
accessInfo.supplyBaseUrl(who.avatar_static),
|
||||
accessInfo.supplyBaseUrl(who.avatar)
|
||||
)
|
||||
// }
|
||||
|
||||
showOpenSticker(who)
|
||||
|
||||
var content = status.decoded_content
|
||||
|
||||
// ニコフレのアンケートの表示
|
||||
val enquete = status.enquete
|
||||
when {
|
||||
enquete == null -> {
|
||||
}
|
||||
|
||||
enquete.pollType == TootPollsType.FriendsNico && enquete.type != TootPolls.TYPE_ENQUETE -> {
|
||||
// フレニコの投票の結果表示は普通にテキストを表示するだけでよい
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
// アンケートの本文を上書きする
|
||||
val question = enquete.decoded_question
|
||||
if (question.isNotBlank()) content = question
|
||||
|
||||
showEnqueteItems(status, enquete)
|
||||
}
|
||||
}
|
||||
|
||||
showPreviewCard(status)
|
||||
|
||||
// if( status.decoded_tags == null ){
|
||||
// tvTags.setVisibility( View.GONE );
|
||||
// }else{
|
||||
// tvTags.setVisibility( View.VISIBLE );
|
||||
// tvTags.setText( status.decoded_tags );
|
||||
// }
|
||||
|
||||
if (status.decoded_mentions.isEmpty()) {
|
||||
tvMentions.visibility = View.GONE
|
||||
} else {
|
||||
tvMentions.visibility = View.VISIBLE
|
||||
tvMentions.text = status.decoded_mentions
|
||||
}
|
||||
|
||||
if (status.time_deleted_at > 0L) {
|
||||
val s = SpannableStringBuilder()
|
||||
.append('(')
|
||||
.append(
|
||||
activity.getString(
|
||||
R.string.deleted_at,
|
||||
TootStatus.formatTime(activity, status.time_deleted_at, true)
|
||||
)
|
||||
)
|
||||
.append(')')
|
||||
content = s
|
||||
}
|
||||
|
||||
tvContent.text = content
|
||||
contentInvalidator.register(content)
|
||||
|
||||
activity.checkAutoCW(status, content)
|
||||
val r = status.auto_cw
|
||||
|
||||
tvContent.minLines = r?.originalLineCount ?: -1
|
||||
|
||||
val decodedSpoilerText = status.decoded_spoiler_text
|
||||
when {
|
||||
decodedSpoilerText.isNotEmpty() -> {
|
||||
// 元データに含まれるContent Warning を使う
|
||||
llContentWarning.visibility = View.VISIBLE
|
||||
tvContentWarning.text = status.decoded_spoiler_text
|
||||
spoilerInvalidator.register(status.decoded_spoiler_text)
|
||||
val cwShown = ContentWarning.isShown(status, accessInfo.expand_cw)
|
||||
showContent(cwShown)
|
||||
}
|
||||
|
||||
r?.decodedSpoilerText != null -> {
|
||||
// 自動CW
|
||||
llContentWarning.visibility = View.VISIBLE
|
||||
tvContentWarning.text = r.decodedSpoilerText
|
||||
spoilerInvalidator.register(r.decodedSpoilerText)
|
||||
val cwShown = ContentWarning.isShown(status, accessInfo.expand_cw)
|
||||
showContent(cwShown)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// CWしない
|
||||
llContentWarning.visibility = View.GONE
|
||||
llContents.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
val mediaAttachments = status.media_attachments
|
||||
if (mediaAttachments == null || mediaAttachments.isEmpty()) {
|
||||
flMedia.visibility = View.GONE
|
||||
llMedia.visibility = View.GONE
|
||||
btnShowMedia.visibility = View.GONE
|
||||
} else {
|
||||
flMedia.visibility = View.VISIBLE
|
||||
|
||||
// hide sensitive media
|
||||
val defaultShown = when {
|
||||
column.hideMediaDefault -> false
|
||||
accessInfo.dont_hide_nsfw -> true
|
||||
else -> !status.sensitive
|
||||
}
|
||||
val isShown = MediaShown.isShown(status, defaultShown)
|
||||
|
||||
btnShowMedia.visibility = if (!isShown) View.VISIBLE else View.GONE
|
||||
llMedia.visibility = if (!isShown) View.GONE else View.VISIBLE
|
||||
val sb = StringBuilder()
|
||||
setMedia(mediaAttachments, sb, ivMedia1, 0)
|
||||
setMedia(mediaAttachments, sb, ivMedia2, 1)
|
||||
setMedia(mediaAttachments, sb, ivMedia3, 2)
|
||||
setMedia(mediaAttachments, sb, ivMedia4, 3)
|
||||
|
||||
val m0 =
|
||||
if (mediaAttachments.isEmpty()) null else mediaAttachments[0] as? TootAttachment
|
||||
btnShowMedia.blurhash = m0?.blurhash
|
||||
|
||||
if (sb.isNotEmpty()) {
|
||||
tvMediaDescription.visibility = View.VISIBLE
|
||||
tvMediaDescription.text = sb
|
||||
}
|
||||
|
||||
setIconDrawableId(
|
||||
activity,
|
||||
btnHideMedia,
|
||||
R.drawable.ic_close,
|
||||
color = contentColor,
|
||||
alphaMultiplier = Styler.boostAlpha
|
||||
)
|
||||
}
|
||||
|
||||
makeReactionsView(status)
|
||||
|
||||
buttonsForStatus?.bind(status, (item as? TootNotification))
|
||||
|
||||
var sb: StringBuilder? = null
|
||||
|
||||
fun prepareSb(): StringBuilder =
|
||||
sb?.append(", ") ?: StringBuilder().also { sb = it }
|
||||
|
||||
val application = status.application
|
||||
if (application != null &&
|
||||
(column.type == ColumnType.CONVERSATION || Pref.bpShowAppName(activity.pref))
|
||||
) {
|
||||
prepareSb().append(activity.getString(R.string.application_is, application.name ?: ""))
|
||||
}
|
||||
|
||||
val language = status.language
|
||||
if (language != null &&
|
||||
(column.type == ColumnType.CONVERSATION || Pref.bpShowLanguage(activity.pref))
|
||||
) {
|
||||
prepareSb().append(activity.getString(R.string.language_is, language))
|
||||
}
|
||||
|
||||
tvApplication.vg(sb != null)?.text = sb
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showOpenSticker(who: TootAccount) {
|
||||
try {
|
||||
if (!Column.showOpenSticker) return
|
||||
|
||||
val host = who.apDomain
|
||||
|
||||
// LTLでホスト名が同じならTickerを表示しない
|
||||
@Suppress("NON_EXHAUSTIVE_WHEN")
|
||||
when (column.type) {
|
||||
ColumnType.LOCAL, ColumnType.LOCAL_AROUND -> {
|
||||
if (host == accessInfo.apDomain) return
|
||||
}
|
||||
}
|
||||
|
||||
val item = OpenSticker.lastList[host.ascii] ?: return
|
||||
|
||||
tvOpenSticker.text = item.name
|
||||
tvOpenSticker.textColor = item.fontColor
|
||||
|
||||
val density = activity.density
|
||||
|
||||
val lp = ivOpenSticker.layoutParams
|
||||
lp.height = (density * 16f + 0.5f).toInt()
|
||||
lp.width = (density * item.imageWidth + 0.5f).toInt()
|
||||
|
||||
ivOpenSticker.layoutParams = lp
|
||||
ivOpenSticker.setImageUrl(activity.pref, 0f, item.favicon)
|
||||
val colorBg = item.bgColor
|
||||
when (colorBg.size) {
|
||||
1 -> {
|
||||
val c = colorBg.first()
|
||||
tvOpenSticker.setBackgroundColor(c)
|
||||
ivOpenSticker.setBackgroundColor(c)
|
||||
}
|
||||
|
||||
else -> {
|
||||
ivOpenSticker.setBackgroundColor(colorBg.last())
|
||||
tvOpenSticker.background = colorBg.getGradation()
|
||||
}
|
||||
}
|
||||
llOpenSticker.visibility = View.VISIBLE
|
||||
llOpenSticker.requestLayout()
|
||||
} catch (ex: Throwable) {
|
||||
ItemViewHolder.log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showStatusTime(
|
||||
activity: ActMain,
|
||||
tv: TextView,
|
||||
|
@ -1238,9 +676,9 @@ fun ItemViewHolder.showScheduled(item: TootScheduled) {
|
|||
|
||||
tvName.text = whoRef.decoded_display_name
|
||||
nameInvalidator.register(whoRef.decoded_display_name)
|
||||
ivThumbnail.setImageUrl(
|
||||
ivAvatar.setImageUrl(
|
||||
activity.pref,
|
||||
Styler.calcIconRound(ivThumbnail.layoutParams),
|
||||
Styler.calcIconRound(ivAvatar.layoutParams),
|
||||
accessInfo.supplyBaseUrl(who.avatar_static),
|
||||
accessInfo.supplyBaseUrl(who.avatar)
|
||||
)
|
||||
|
@ -1320,20 +758,6 @@ fun ItemViewHolder.showScheduled(item: TootScheduled) {
|
|||
TootStatus.formatTime(activity, item.timeScheduledAt, true)
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showContent(shown: Boolean) {
|
||||
llContents.visibility = if (shown) View.VISIBLE else View.GONE
|
||||
btnContentWarning.setText(if (shown) R.string.hide else R.string.show)
|
||||
statusShowing?.let { status ->
|
||||
val r = status.auto_cw
|
||||
tvContent.minLines = r?.originalLineCount ?: -1
|
||||
if (r?.decodedSpoilerText != null) {
|
||||
// 自動CWの場合はContentWarningのテキストを切り替える
|
||||
tvContentWarning.text =
|
||||
if (shown) activity.getString(R.string.auto_cw_prefix) else r.decodedSpoilerText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showConversationIcons(cs: TootConversationSummary) {
|
||||
|
||||
val lastAccountId = cs.last_status.account.id
|
||||
|
@ -1384,7 +808,7 @@ fun ItemViewHolder.setAcct(tv: TextView, accessInfo: SavedAccount, who: TootAcco
|
|||
val ac = AcctColor.load(accessInfo, who)
|
||||
tv.text = when {
|
||||
AcctColor.hasNickname(ac) -> ac.nickname
|
||||
Pref.bpShortAcctLocalUser(App1.pref) -> "@${who.acct.pretty}"
|
||||
PrefB.bpShortAcctLocalUser(App1.pref) -> "@${who.acct.pretty}"
|
||||
else -> "@${ac.nickname}"
|
||||
}
|
||||
tv.textColor = ac.color_fg.notZero() ?: this.acctColor
|
||||
|
@ -1392,97 +816,3 @@ fun ItemViewHolder.setAcct(tv: TextView, accessInfo: SavedAccount, who: TootAcco
|
|||
tv.setBackgroundColor(ac.color_bg) // may 0
|
||||
tv.setPaddingRelative(activity.acctPadLr, 0, activity.acctPadLr, 0)
|
||||
}
|
||||
|
||||
fun ItemViewHolder.setMedia(
|
||||
mediaAttachments: ArrayList<TootAttachmentLike>,
|
||||
sbDesc: StringBuilder,
|
||||
iv: MyNetworkImageView,
|
||||
idx: Int,
|
||||
) {
|
||||
val ta = if (idx < mediaAttachments.size) mediaAttachments[idx] else null
|
||||
if (ta == null) {
|
||||
iv.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
|
||||
iv.visibility = View.VISIBLE
|
||||
|
||||
iv.setFocusPoint(ta.focusX, ta.focusY)
|
||||
|
||||
if (Pref.bpDontCropMediaThumb(App1.pref)) {
|
||||
iv.scaleType = ImageView.ScaleType.FIT_CENTER
|
||||
} else {
|
||||
iv.setScaleTypeForMedia()
|
||||
}
|
||||
|
||||
val showUrl: Boolean
|
||||
|
||||
when (ta.type) {
|
||||
TootAttachmentType.Audio -> {
|
||||
iv.setMediaType(0)
|
||||
iv.setDefaultImage(Styler.defaultColorIcon(activity, R.drawable.wide_music))
|
||||
iv.setImageUrl(activity.pref, 0f, ta.urlForThumbnail(activity.pref))
|
||||
showUrl = true
|
||||
}
|
||||
|
||||
TootAttachmentType.Unknown -> {
|
||||
iv.setMediaType(0)
|
||||
iv.setDefaultImage(Styler.defaultColorIcon(activity, R.drawable.wide_question))
|
||||
iv.setImageUrl(activity.pref, 0f, null)
|
||||
showUrl = true
|
||||
}
|
||||
|
||||
else -> when (val urlThumbnail = ta.urlForThumbnail(activity.pref)) {
|
||||
null, "" -> {
|
||||
iv.setMediaType(0)
|
||||
iv.setDefaultImage(Styler.defaultColorIcon(activity, R.drawable.wide_question))
|
||||
iv.setImageUrl(activity.pref, 0f, null)
|
||||
showUrl = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
iv.setMediaType(
|
||||
when (ta.type) {
|
||||
TootAttachmentType.Video -> R.drawable.media_type_video
|
||||
TootAttachmentType.GIFV -> R.drawable.media_type_gifv
|
||||
else -> 0
|
||||
}
|
||||
)
|
||||
iv.setDefaultImage(null)
|
||||
iv.setImageUrl(
|
||||
activity.pref,
|
||||
0f,
|
||||
accessInfo.supplyBaseUrl(urlThumbnail),
|
||||
accessInfo.supplyBaseUrl(urlThumbnail)
|
||||
)
|
||||
showUrl = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun appendDescription(s: String) {
|
||||
// val lp = LinearLayout.LayoutParams(
|
||||
// LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
// LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
// )
|
||||
// lp.topMargin = (0.5f + activity.density * 3f).toInt()
|
||||
//
|
||||
// val tv = MyTextView(activity)
|
||||
// tv.layoutParams = lp
|
||||
// //
|
||||
// tv.movementMethod = MyLinkMovementMethod
|
||||
// if(! activity.timeline_font_size_sp.isNaN()) {
|
||||
// tv.textSize = activity.timeline_font_size_sp
|
||||
// }
|
||||
// tv.setTextColor(content_color)
|
||||
|
||||
if (sbDesc.isNotEmpty()) sbDesc.append("\n")
|
||||
val desc = activity.getString(R.string.media_description, idx + 1, s)
|
||||
sbDesc.append(desc)
|
||||
}
|
||||
|
||||
when (val description = ta.description.notEmpty()) {
|
||||
null -> if (showUrl) ta.urlForDescription.notEmpty()?.let { appendDescription(it) }
|
||||
else -> appendDescription(description)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import jp.juggler.subwaytooter.api.entity.TootAccountRef
|
||||
import jp.juggler.subwaytooter.api.entity.TootNotification
|
||||
import jp.juggler.subwaytooter.api.entity.TootReaction
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.util.notZero
|
||||
import org.jetbrains.anko.backgroundColor
|
||||
|
||||
fun ItemViewHolder.showNotification(n: TootNotification) {
|
||||
val nStatus = n.status
|
||||
val nAccountRef = n.accountRef
|
||||
when (n.type) {
|
||||
TootNotification.TYPE_FAVOURITE ->
|
||||
showNotificationFavourite(n, nAccountRef, nStatus)
|
||||
|
||||
TootNotification.TYPE_REBLOG ->
|
||||
showNotificationReblog(n, nAccountRef, nStatus)
|
||||
|
||||
TootNotification.TYPE_RENOTE ->
|
||||
showNotificationRenote(n, nAccountRef, nStatus)
|
||||
|
||||
TootNotification.TYPE_FOLLOW ->
|
||||
showNotificationFollow(n, nAccountRef)
|
||||
|
||||
TootNotification.TYPE_UNFOLLOW ->
|
||||
showNotificationUnfollow(n, nAccountRef)
|
||||
|
||||
TootNotification.TYPE_MENTION,
|
||||
TootNotification.TYPE_REPLY,
|
||||
-> showNotificationMention(n, nAccountRef, nStatus)
|
||||
|
||||
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
|
||||
TootNotification.TYPE_EMOJI_REACTION,
|
||||
TootNotification.TYPE_REACTION,
|
||||
-> showNotificationReaction(n, nAccountRef, nStatus)
|
||||
|
||||
TootNotification.TYPE_QUOTE ->
|
||||
showNotificationQuote(n, nAccountRef, nStatus)
|
||||
|
||||
TootNotification.TYPE_STATUS ->
|
||||
showNotificationPost(n, nAccountRef, nStatus)
|
||||
|
||||
TootNotification.TYPE_FOLLOW_REQUEST,
|
||||
TootNotification.TYPE_FOLLOW_REQUEST_MISSKEY,
|
||||
-> showNotificationFollowRequest(n, nAccountRef)
|
||||
|
||||
TootNotification.TYPE_FOLLOW_REQUEST_ACCEPTED_MISSKEY ->
|
||||
showNotificationFollowRequestAccepted(n, nAccountRef)
|
||||
|
||||
TootNotification.TYPE_VOTE,
|
||||
TootNotification.TYPE_POLL_VOTE_MISSKEY,
|
||||
-> showNotificationVote(n, nAccountRef, nStatus)
|
||||
|
||||
TootNotification.TYPE_POLL ->
|
||||
showNotificationPoll(n, nAccountRef, nStatus)
|
||||
|
||||
else ->
|
||||
showNotificationUnknown(n, nAccountRef, nStatus)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationFollow(n: TootNotification, nAccountRef: TootAccountRef?) {
|
||||
val colorBg = PrefI.ipEventBgColorFollow(activity.pref)
|
||||
colorBg.notZero()?.let { viewRoot.backgroundColor = it }
|
||||
nAccountRef?.let {
|
||||
showBoost(it, n.time_created_at, R.drawable.ic_follow_plus, R.string.display_name_followed_by)
|
||||
showAccount(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationUnfollow(n: TootNotification, nAccountRef: TootAccountRef?) {
|
||||
val colorBg = PrefI.ipEventBgColorUnfollow(activity.pref)
|
||||
colorBg.notZero()?.let { viewRoot.backgroundColor = it }
|
||||
nAccountRef?.let {
|
||||
showBoost(it, n.time_created_at, R.drawable.ic_follow_cross, R.string.display_name_unfollowed_by)
|
||||
showAccount(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationFollowRequest(n: TootNotification, nAccountRef: TootAccountRef?) {
|
||||
val colorBg = PrefI.ipEventBgColorFollowRequest(activity.pref)
|
||||
colorBg.notZero()?.let { viewRoot.backgroundColor = it }
|
||||
nAccountRef?.let {
|
||||
showBoost(it, n.time_created_at, R.drawable.ic_follow_wait, R.string.display_name_follow_request_by)
|
||||
showAccount(it)
|
||||
}
|
||||
boostedAction = {
|
||||
activity.addColumn(activity.nextPosition(column), accessInfo, ColumnType.FOLLOW_REQUESTS)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationFollowRequestAccepted(n: TootNotification, nAccountRef: TootAccountRef?) {
|
||||
val colorBg = PrefI.ipEventBgColorFollow(activity.pref)
|
||||
colorBg.notZero()?.let { viewRoot.backgroundColor = it }
|
||||
nAccountRef?.let {
|
||||
showBoost(it, n.time_created_at, R.drawable.ic_follow_plus, R.string.display_name_follow_request_accepted_by)
|
||||
showAccount(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationPost(n: TootNotification, nAccountRef: TootAccountRef?, nStatus: TootStatus?) {
|
||||
val colorBg = PrefI.ipEventBgColorStatus(activity.pref)
|
||||
val iconId = when (nStatus) {
|
||||
null -> R.drawable.ic_question
|
||||
else -> Styler.getVisibilityIconId(accessInfo.isMisskey, nStatus.visibility)
|
||||
}
|
||||
nAccountRef?.let { showBoost(it, n.time_created_at, iconId, R.string.display_name_posted_by) }
|
||||
nStatus?.let { showNotificationStatus(it, colorBg) }
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationReaction(n: TootNotification, nAccountRef: TootAccountRef?, nStatus: TootStatus?) {
|
||||
val colorBg = PrefI.ipEventBgColorReaction(activity.pref)
|
||||
nAccountRef?.let {
|
||||
showBoost(
|
||||
it, n.time_created_at,
|
||||
R.drawable.ic_face,
|
||||
R.string.display_name_reaction_by,
|
||||
reaction = n.reaction ?: TootReaction.UNKNOWN,
|
||||
boostStatus = nStatus
|
||||
)
|
||||
}
|
||||
nStatus?.let { showNotificationStatus(it, colorBg) }
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationFavourite(n: TootNotification, nAccountRef: TootAccountRef?, nStatus: TootStatus?) {
|
||||
nAccountRef?.let {
|
||||
val iconId = if (accessInfo.isNicoru(it.get())) R.drawable.ic_nicoru else R.drawable.ic_star
|
||||
showBoost(it, n.time_created_at, iconId, R.string.display_name_favourited_by)
|
||||
}
|
||||
val colorBg = PrefI.ipEventBgColorFavourite(activity.pref)
|
||||
nStatus?.let { showNotificationStatus(it, colorBg) }
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationReblog(n: TootNotification, nAccountRef: TootAccountRef?, nStatus: TootStatus?) {
|
||||
nAccountRef?.let {
|
||||
showBoost(
|
||||
it,
|
||||
n.time_created_at,
|
||||
R.drawable.ic_repeat,
|
||||
R.string.display_name_boosted_by,
|
||||
boostStatus = nStatus
|
||||
)
|
||||
}
|
||||
val colorBg = PrefI.ipEventBgColorBoost(activity.pref)
|
||||
nStatus?.let { showNotificationStatus(it, colorBg) }
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationRenote(n: TootNotification, nAccountRef: TootAccountRef?, nStatus: TootStatus?) {
|
||||
// 引用のないreblog
|
||||
nAccountRef?.let {
|
||||
showBoost(it, n.time_created_at, R.drawable.ic_repeat, R.string.display_name_boosted_by, boostStatus = nStatus)
|
||||
}
|
||||
val colorBg = PrefI.ipEventBgColorBoost(activity.pref)
|
||||
nStatus?.let { showNotificationStatus(it, colorBg) }
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationMention(n: TootNotification, nAccountRef: TootAccountRef?, nStatus: TootStatus?) {
|
||||
// メンション通知に「~~からの返信」を表示するカラムなのかどうか
|
||||
fun willShowReplyInfo(status: TootStatus?): Boolean = when {
|
||||
// メンションではなく返信の場合、トゥート内部に「~への返信」を表示するので
|
||||
// 通知イベントの「~からの返信」を表示しない
|
||||
status.let { it?.in_reply_to_id != null && it.reply != null } -> false
|
||||
|
||||
// XXX: 簡略表示だったりMisskeyだったりが影響してた時期もあったが、今後どうしようか…
|
||||
else -> true
|
||||
}
|
||||
|
||||
if (willShowReplyInfo(nStatus)) {
|
||||
nAccountRef?.let { showBoost(it, n.time_created_at, R.drawable.ic_reply, R.string.display_name_mentioned_by) }
|
||||
}
|
||||
|
||||
val colorBg = PrefI.ipEventBgColorMention(activity.pref)
|
||||
nStatus?.let { showNotificationStatus(it, colorBg) }
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationQuote(n: TootNotification, nAccountRef: TootAccountRef?, nStatus: TootStatus?) {
|
||||
nAccountRef?.let { showBoost(it, n.time_created_at, R.drawable.ic_repeat, R.string.display_name_quoted_by) }
|
||||
|
||||
val colorBg = PrefI.ipEventBgColorQuote(activity.pref)
|
||||
nStatus?.let { showNotificationStatus(it, colorBg) }
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationVote(n: TootNotification, nAccountRef: TootAccountRef?, nStatus: TootStatus?) {
|
||||
nAccountRef?.let { showBoost(it, n.time_created_at, R.drawable.ic_vote, R.string.display_name_voted_by) }
|
||||
val colorBg = PrefI.ipEventBgColorVote(activity.pref)
|
||||
nStatus?.let { showNotificationStatus(it, colorBg) }
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationPoll(n: TootNotification, nAccountRef: TootAccountRef?, nStatus: TootStatus?) {
|
||||
nAccountRef?.let { showBoost(it, n.time_created_at, R.drawable.ic_vote, R.string.end_of_polling_from) }
|
||||
val colorBg = 0
|
||||
nStatus?.let { showNotificationStatus(it, colorBg) }
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationUnknown(n: TootNotification, nAccountRef: TootAccountRef?, nStatus: TootStatus?) {
|
||||
nAccountRef?.let { showBoost(it, n.time_created_at, R.drawable.ic_question, R.string.unknown_notification_from) }
|
||||
val colorBg = 0
|
||||
nStatus?.let { showNotificationStatus(it, colorBg) }
|
||||
|
||||
tvMessageHolder.visibility = View.VISIBLE
|
||||
tvMessageHolder.text = "notification type is ${n.type}"
|
||||
tvMessageHolder.gravity = Gravity.CENTER
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.showNotificationStatus(item: TootStatus, colorBgDefault: Int) {
|
||||
val reblog = item.reblog
|
||||
when {
|
||||
reblog == null -> showStatusOrReply(item, colorBgDefault)
|
||||
|
||||
item.isQuoteToot -> {
|
||||
// 引用Renote
|
||||
showReply(reblog, R.drawable.ic_repeat, R.string.quote_to)
|
||||
showStatus(item, PrefI.ipEventBgColorQuote(activity.pref))
|
||||
}
|
||||
|
||||
else -> {
|
||||
// 通常のブースト。引用なしブースト。
|
||||
// ブースト表示は通知イベントと被るのでしない
|
||||
showStatusOrReply(reblog, PrefI.ipEventBgColorBoost(activity.pref))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,415 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.table.ContentWarning
|
||||
import jp.juggler.subwaytooter.table.MediaShown
|
||||
import jp.juggler.subwaytooter.util.OpenSticker
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
import jp.juggler.util.*
|
||||
import org.jetbrains.anko.backgroundColor
|
||||
import org.jetbrains.anko.textColor
|
||||
|
||||
fun ItemViewHolder.showStatusOrReply(item: TootStatus, colorBgArg: Int = 0) {
|
||||
var colorBg = colorBgArg
|
||||
val reply = item.reply
|
||||
val inReplyToId = item.in_reply_to_id
|
||||
val inReplyToAccountId = item.in_reply_to_account_id
|
||||
when {
|
||||
reply != null -> {
|
||||
showReply(reply, R.drawable.ic_reply, R.string.reply_to)
|
||||
if (colorBgArg == 0) colorBg = PrefI.ipEventBgColorMention(activity.pref)
|
||||
}
|
||||
|
||||
inReplyToId != null && inReplyToAccountId != null -> {
|
||||
showReply(item, inReplyToAccountId)
|
||||
if (colorBgArg == 0) colorBg = PrefI.ipEventBgColorMention(activity.pref)
|
||||
}
|
||||
}
|
||||
showStatus(item, colorBg)
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showStatus(status: TootStatus, colorBg: Int = 0) {
|
||||
|
||||
val filteredWord = status.filteredWord
|
||||
if (filteredWord != null) {
|
||||
showMessageHolder(
|
||||
TootMessageHolder(
|
||||
if (PrefB.bpShowFilteredWord(activity.pref)) {
|
||||
"${activity.getString(R.string.filtered)} / $filteredWord"
|
||||
} else {
|
||||
activity.getString(R.string.filtered)
|
||||
}
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
this.statusShowing = status
|
||||
llStatus.visibility = View.VISIBLE
|
||||
|
||||
if (status.conversation_main) {
|
||||
|
||||
val conversationMainBgColor =
|
||||
PrefI.ipConversationMainTootBgColor(activity.pref).notZero()
|
||||
?: (activity.attrColor(R.attr.colorImageButtonAccent) and 0xffffff) or 0x20000000
|
||||
|
||||
this.viewRoot.setBackgroundColor(conversationMainBgColor)
|
||||
} else {
|
||||
val c = colorBg.notZero()
|
||||
|
||||
?: when (status.bookmarked) {
|
||||
true -> PrefI.ipEventBgColorBookmark(App1.pref)
|
||||
false -> 0
|
||||
}.notZero()
|
||||
|
||||
?: when (status.getBackgroundColorType(accessInfo)) {
|
||||
TootVisibility.UnlistedHome -> ItemViewHolder.toot_color_unlisted
|
||||
TootVisibility.PrivateFollowers -> ItemViewHolder.toot_color_follower
|
||||
TootVisibility.DirectSpecified -> ItemViewHolder.toot_color_direct_user
|
||||
TootVisibility.DirectPrivate -> ItemViewHolder.toot_color_direct_me
|
||||
// TODO add color setting for limited?
|
||||
TootVisibility.Limited -> ItemViewHolder.toot_color_follower
|
||||
else -> 0
|
||||
}
|
||||
|
||||
if (c != 0) {
|
||||
this.viewRoot.backgroundColor = c
|
||||
}
|
||||
}
|
||||
|
||||
showStatusTime(activity, tvTime, who = status.account, status = status)
|
||||
|
||||
val whoRef = status.accountRef
|
||||
val who = whoRef.get()
|
||||
this.statusAccount = whoRef
|
||||
|
||||
setAcct(tvAcct, accessInfo, who)
|
||||
|
||||
// if(who == null) {
|
||||
// tvName.text = "?"
|
||||
// name_invalidator.register(null)
|
||||
// ivThumbnail.setImageUrl(activity.pref, 16f, null, null)
|
||||
// } else {
|
||||
tvName.text = whoRef.decoded_display_name
|
||||
nameInvalidator.register(whoRef.decoded_display_name)
|
||||
ivAvatar.setImageUrl(
|
||||
activity.pref,
|
||||
Styler.calcIconRound(ivAvatar.layoutParams),
|
||||
accessInfo.supplyBaseUrl(who.avatar_static),
|
||||
accessInfo.supplyBaseUrl(who.avatar)
|
||||
)
|
||||
// }
|
||||
|
||||
showOpenSticker(who)
|
||||
|
||||
var content = status.decoded_content
|
||||
|
||||
// ニコフレのアンケートの表示
|
||||
val enquete = status.enquete
|
||||
when {
|
||||
enquete == null -> {
|
||||
}
|
||||
|
||||
enquete.pollType == TootPollsType.FriendsNico && enquete.type != TootPolls.TYPE_ENQUETE -> {
|
||||
// フレニコの投票の結果表示は普通にテキストを表示するだけでよい
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
// アンケートの本文を上書きする
|
||||
val question = enquete.decoded_question
|
||||
if (question.isNotBlank()) content = question
|
||||
|
||||
showEnqueteItems(status, enquete)
|
||||
}
|
||||
}
|
||||
|
||||
showPreviewCard(status)
|
||||
|
||||
// if( status.decoded_tags == null ){
|
||||
// tvTags.setVisibility( View.GONE );
|
||||
// }else{
|
||||
// tvTags.setVisibility( View.VISIBLE );
|
||||
// tvTags.setText( status.decoded_tags );
|
||||
// }
|
||||
|
||||
if (status.decoded_mentions.isEmpty()) {
|
||||
tvMentions.visibility = View.GONE
|
||||
} else {
|
||||
tvMentions.visibility = View.VISIBLE
|
||||
tvMentions.text = status.decoded_mentions
|
||||
}
|
||||
|
||||
if (status.time_deleted_at > 0L) {
|
||||
val s = SpannableStringBuilder()
|
||||
.append('(')
|
||||
.append(
|
||||
activity.getString(
|
||||
R.string.deleted_at,
|
||||
TootStatus.formatTime(activity, status.time_deleted_at, true)
|
||||
)
|
||||
)
|
||||
.append(')')
|
||||
content = s
|
||||
}
|
||||
|
||||
tvContent.text = content
|
||||
contentInvalidator.register(content)
|
||||
|
||||
activity.checkAutoCW(status, content)
|
||||
val r = status.auto_cw
|
||||
|
||||
tvContent.minLines = r?.originalLineCount ?: -1
|
||||
|
||||
val decodedSpoilerText = status.decoded_spoiler_text
|
||||
when {
|
||||
decodedSpoilerText.isNotEmpty() -> {
|
||||
// 元データに含まれるContent Warning を使う
|
||||
llContentWarning.visibility = View.VISIBLE
|
||||
tvContentWarning.text = status.decoded_spoiler_text
|
||||
spoilerInvalidator.register(status.decoded_spoiler_text)
|
||||
val cwShown = ContentWarning.isShown(status, accessInfo.expand_cw)
|
||||
showContent(cwShown)
|
||||
}
|
||||
|
||||
r?.decodedSpoilerText != null -> {
|
||||
// 自動CW
|
||||
llContentWarning.visibility = View.VISIBLE
|
||||
tvContentWarning.text = r.decodedSpoilerText
|
||||
spoilerInvalidator.register(r.decodedSpoilerText)
|
||||
val cwShown = ContentWarning.isShown(status, accessInfo.expand_cw)
|
||||
showContent(cwShown)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// CWしない
|
||||
llContentWarning.visibility = View.GONE
|
||||
llContents.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
val mediaAttachments = status.media_attachments
|
||||
if (mediaAttachments == null || mediaAttachments.isEmpty()) {
|
||||
flMedia.visibility = View.GONE
|
||||
llMedia.visibility = View.GONE
|
||||
btnShowMedia.visibility = View.GONE
|
||||
} else {
|
||||
flMedia.visibility = View.VISIBLE
|
||||
|
||||
// hide sensitive media
|
||||
val defaultShown = when {
|
||||
column.hideMediaDefault -> false
|
||||
accessInfo.dont_hide_nsfw -> true
|
||||
else -> !status.sensitive
|
||||
}
|
||||
val isShown = MediaShown.isShown(status, defaultShown)
|
||||
|
||||
btnShowMedia.visibility = if (!isShown) View.VISIBLE else View.GONE
|
||||
llMedia.visibility = if (!isShown) View.GONE else View.VISIBLE
|
||||
val sb = StringBuilder()
|
||||
setMedia(mediaAttachments, sb, ivMedia1, 0)
|
||||
setMedia(mediaAttachments, sb, ivMedia2, 1)
|
||||
setMedia(mediaAttachments, sb, ivMedia3, 2)
|
||||
setMedia(mediaAttachments, sb, ivMedia4, 3)
|
||||
|
||||
val m0 =
|
||||
if (mediaAttachments.isEmpty()) null else mediaAttachments[0] as? TootAttachment
|
||||
btnShowMedia.blurhash = m0?.blurhash
|
||||
|
||||
if (sb.isNotEmpty()) {
|
||||
tvMediaDescription.visibility = View.VISIBLE
|
||||
tvMediaDescription.text = sb
|
||||
}
|
||||
|
||||
setIconDrawableId(
|
||||
activity,
|
||||
btnHideMedia,
|
||||
R.drawable.ic_close,
|
||||
color = contentColor,
|
||||
alphaMultiplier = Styler.boostAlpha
|
||||
)
|
||||
}
|
||||
|
||||
makeReactionsView(status)
|
||||
|
||||
buttonsForStatus?.bind(status, (item as? TootNotification))
|
||||
|
||||
var sb: StringBuilder? = null
|
||||
|
||||
fun prepareSb(): StringBuilder =
|
||||
sb?.append(", ") ?: StringBuilder().also { sb = it }
|
||||
|
||||
val application = status.application
|
||||
if (application != null &&
|
||||
(column.type == ColumnType.CONVERSATION || PrefB.bpShowAppName(activity.pref))
|
||||
) {
|
||||
prepareSb().append(activity.getString(R.string.application_is, application.name ?: ""))
|
||||
}
|
||||
|
||||
val language = status.language
|
||||
if (language != null &&
|
||||
(column.type == ColumnType.CONVERSATION || PrefB.bpShowLanguage(activity.pref))
|
||||
) {
|
||||
prepareSb().append(activity.getString(R.string.language_is, language))
|
||||
}
|
||||
|
||||
tvApplication.vg(sb != null)?.text = sb
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showOpenSticker(who: TootAccount) {
|
||||
try {
|
||||
if (!Column.showOpenSticker) return
|
||||
|
||||
val host = who.apDomain
|
||||
|
||||
// LTLでホスト名が同じならTickerを表示しない
|
||||
@Suppress("NON_EXHAUSTIVE_WHEN")
|
||||
when (column.type) {
|
||||
ColumnType.LOCAL, ColumnType.LOCAL_AROUND -> {
|
||||
if (host == accessInfo.apDomain) return
|
||||
}
|
||||
}
|
||||
|
||||
val item = OpenSticker.lastList[host.ascii] ?: return
|
||||
|
||||
tvOpenSticker.text = item.name
|
||||
tvOpenSticker.textColor = item.fontColor
|
||||
|
||||
val density = activity.density
|
||||
|
||||
val lp = ivOpenSticker.layoutParams
|
||||
lp.height = (density * 16f + 0.5f).toInt()
|
||||
lp.width = (density * item.imageWidth + 0.5f).toInt()
|
||||
|
||||
ivOpenSticker.layoutParams = lp
|
||||
ivOpenSticker.setImageUrl(activity.pref, 0f, item.favicon)
|
||||
val colorBg = item.bgColor
|
||||
when (colorBg.size) {
|
||||
1 -> {
|
||||
val c = colorBg.first()
|
||||
tvOpenSticker.setBackgroundColor(c)
|
||||
ivOpenSticker.setBackgroundColor(c)
|
||||
}
|
||||
|
||||
else -> {
|
||||
ivOpenSticker.setBackgroundColor(colorBg.last())
|
||||
tvOpenSticker.background = colorBg.getGradation()
|
||||
}
|
||||
}
|
||||
llOpenSticker.visibility = View.VISIBLE
|
||||
llOpenSticker.requestLayout()
|
||||
} catch (ex: Throwable) {
|
||||
ItemViewHolder.log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showContent(shown: Boolean) {
|
||||
llContents.visibility = if (shown) View.VISIBLE else View.GONE
|
||||
btnContentWarning.setText(if (shown) R.string.hide else R.string.show)
|
||||
statusShowing?.let { status ->
|
||||
val r = status.auto_cw
|
||||
tvContent.minLines = r?.originalLineCount ?: -1
|
||||
if (r?.decodedSpoilerText != null) {
|
||||
// 自動CWの場合はContentWarningのテキストを切り替える
|
||||
tvContentWarning.text =
|
||||
if (shown) activity.getString(R.string.auto_cw_prefix) else r.decodedSpoilerText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ItemViewHolder.setMedia(
|
||||
mediaAttachments: ArrayList<TootAttachmentLike>,
|
||||
sbDesc: StringBuilder,
|
||||
iv: MyNetworkImageView,
|
||||
idx: Int,
|
||||
) {
|
||||
val ta = if (idx < mediaAttachments.size) mediaAttachments[idx] else null
|
||||
if (ta == null) {
|
||||
iv.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
|
||||
iv.visibility = View.VISIBLE
|
||||
|
||||
iv.setFocusPoint(ta.focusX, ta.focusY)
|
||||
|
||||
if (PrefB.bpDontCropMediaThumb(App1.pref)) {
|
||||
iv.scaleType = ImageView.ScaleType.FIT_CENTER
|
||||
} else {
|
||||
iv.setScaleTypeForMedia()
|
||||
}
|
||||
|
||||
val showUrl: Boolean
|
||||
|
||||
when (ta.type) {
|
||||
TootAttachmentType.Audio -> {
|
||||
iv.setMediaType(0)
|
||||
iv.setDefaultImage(Styler.defaultColorIcon(activity, R.drawable.wide_music))
|
||||
iv.setImageUrl(activity.pref, 0f, ta.urlForThumbnail(activity.pref))
|
||||
showUrl = true
|
||||
}
|
||||
|
||||
TootAttachmentType.Unknown -> {
|
||||
iv.setMediaType(0)
|
||||
iv.setDefaultImage(Styler.defaultColorIcon(activity, R.drawable.wide_question))
|
||||
iv.setImageUrl(activity.pref, 0f, null)
|
||||
showUrl = true
|
||||
}
|
||||
|
||||
else -> when (val urlThumbnail = ta.urlForThumbnail(activity.pref)) {
|
||||
null, "" -> {
|
||||
iv.setMediaType(0)
|
||||
iv.setDefaultImage(Styler.defaultColorIcon(activity, R.drawable.wide_question))
|
||||
iv.setImageUrl(activity.pref, 0f, null)
|
||||
showUrl = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
iv.setMediaType(
|
||||
when (ta.type) {
|
||||
TootAttachmentType.Video -> R.drawable.media_type_video
|
||||
TootAttachmentType.GIFV -> R.drawable.media_type_gifv
|
||||
else -> 0
|
||||
}
|
||||
)
|
||||
iv.setDefaultImage(null)
|
||||
iv.setImageUrl(
|
||||
activity.pref,
|
||||
0f,
|
||||
accessInfo.supplyBaseUrl(urlThumbnail),
|
||||
accessInfo.supplyBaseUrl(urlThumbnail)
|
||||
)
|
||||
showUrl = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun appendDescription(s: String) {
|
||||
// val lp = LinearLayout.LayoutParams(
|
||||
// LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
// LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
// )
|
||||
// lp.topMargin = (0.5f + activity.density * 3f).toInt()
|
||||
//
|
||||
// val tv = MyTextView(activity)
|
||||
// tv.layoutParams = lp
|
||||
// //
|
||||
// tv.movementMethod = MyLinkMovementMethod
|
||||
// if(! activity.timeline_font_size_sp.isNaN()) {
|
||||
// tv.textSize = activity.timeline_font_size_sp
|
||||
// }
|
||||
// tv.setTextColor(content_color)
|
||||
|
||||
if (sbDesc.isNotEmpty()) sbDesc.append("\n")
|
||||
val desc = activity.getString(R.string.media_description, idx + 1, s)
|
||||
sbDesc.append(desc)
|
||||
}
|
||||
|
||||
when (val description = ta.description.notEmpty()) {
|
||||
null -> if (showUrl) ta.urlForDescription.notEmpty()?.let { appendDescription(it) }
|
||||
else -> appendDescription(description)
|
||||
}
|
||||
}
|
|
@ -12,12 +12,17 @@ fun Context.pref(): SharedPreferences =
|
|||
@Suppress("EqualsOrHashCode")
|
||||
abstract class BasePref<T>(val key: String, val defVal: T) {
|
||||
|
||||
companion object {
|
||||
// キー名と設定項目のマップ。インポートやアプリ設定で使う
|
||||
val allPref = HashMap<String, BasePref<*>>()
|
||||
}
|
||||
|
||||
init {
|
||||
when {
|
||||
Pref.map[key] != null -> error("Preference key duplicate: $key")
|
||||
allPref[key] != null -> error("Preference key duplicate: $key")
|
||||
else -> {
|
||||
@Suppress("LeakingThis")
|
||||
Pref.map[key] = this
|
||||
allPref[key] = this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,11 +127,7 @@ fun SharedPreferences.Editor.put(item: LongPref, v: Long) =
|
|||
fun SharedPreferences.Editor.put(item: FloatPref, v: Float) =
|
||||
this.apply { item.put(this, v) }
|
||||
|
||||
object Pref {
|
||||
|
||||
// キー名と設定項目のマップ。インポートやアプリ設定で使う
|
||||
val map = HashMap<String, BasePref<*>>()
|
||||
|
||||
object PrefB {
|
||||
// boolean
|
||||
|
||||
val bpDisableEmojiAnimation = BooleanPref(
|
||||
|
@ -461,7 +462,9 @@ object Pref {
|
|||
"ManyWindowPost",
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
object PrefI {
|
||||
// int
|
||||
|
||||
val ipBackButtonAction = IntPref("back_button_action", 0)
|
||||
|
@ -481,6 +484,7 @@ object Pref {
|
|||
|
||||
@Suppress("unused")
|
||||
const val RC_NONE = 2
|
||||
|
||||
val ipRepliesCount = IntPref("RepliesCount", RC_SIMPLE)
|
||||
val ipBoostsCount = IntPref("BoostsCount", RC_ACTUAL)
|
||||
val ipFavouritesCount = IntPref("FavouritesCount", RC_ACTUAL)
|
||||
|
@ -498,13 +502,7 @@ object Pref {
|
|||
const val VS_MISSKEY = 2
|
||||
val ipVisibilityStyle = IntPref("ipVisibilityStyle", VS_BY_ACCOUNT)
|
||||
|
||||
const val ABP_TOP = 0
|
||||
|
||||
@Suppress("unused")
|
||||
const val ABP_BOTTOM = 1
|
||||
const val ABP_START = 2
|
||||
const val ABP_END = 3
|
||||
val ipAdditionalButtonsPosition = IntPref("AdditionalButtonsPosition", ABP_END)
|
||||
val ipAdditionalButtonsPosition = IntPref("AdditionalButtonsPosition", AdditionalButtonsPosition.End.idx)
|
||||
|
||||
val ipFooterButtonBgColor = IntPref("footer_button_bg_color", 0)
|
||||
val ipFooterButtonFgColor = IntPref("footer_button_fg_color", 0)
|
||||
|
@ -574,6 +572,9 @@ object Pref {
|
|||
// val ipTrendTagCountShowing = IntPref("TrendTagCountShowing", 0)
|
||||
// const val TTCS_WEEKLY = 0
|
||||
// const val TTCS_DAILY = 1
|
||||
}
|
||||
|
||||
object PrefS {
|
||||
|
||||
// string
|
||||
val spColumnWidth = StringPref("ColumnWidth", "")
|
||||
|
@ -620,17 +621,21 @@ object Pref {
|
|||
val spWebBrowser = StringPref("WebBrowser", "")
|
||||
|
||||
val spTimelineSpacing = StringPref("TimelineSpacing", "")
|
||||
}
|
||||
|
||||
object PrefL {
|
||||
|
||||
// long
|
||||
val lpTabletTootDefaultAccount = LongPref("tablet_toot_default_account", -1L)
|
||||
}
|
||||
|
||||
object PrefF {
|
||||
// float
|
||||
|
||||
val fpTimelineFontSize = FloatPref("timeline_font_size", Float.NaN)
|
||||
val fpAcctFontSize = FloatPref("acct_font_size", Float.NaN)
|
||||
val fpNotificationTlFontSize = FloatPref("notification_tl_font_size", Float.NaN)
|
||||
val fpHeaderTextSize = FloatPref("HeaderTextSize", Float.NaN)
|
||||
|
||||
internal const val default_timeline_font_size = 14f
|
||||
internal const val default_acct_font_size = 12f
|
||||
internal const val default_notification_tl_font_size = 14f
|
||||
|
|
|
@ -72,7 +72,7 @@ class SideMenuAdapter(
|
|||
)
|
||||
)
|
||||
val newRelease = releaseInfo?.jsonObject(
|
||||
if (Pref.bpCheckBetaVersion(App1.pref)) "beta" else "stable"
|
||||
if (PrefB.bpCheckBetaVersion(App1.pref)) "beta" else "stable"
|
||||
)
|
||||
|
||||
val newVersion =
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -36,9 +36,9 @@ object Styler {
|
|||
}
|
||||
|
||||
fun getVisibilityIconId(isMisskeyData: Boolean, visibility: TootVisibility): Int {
|
||||
val isMisskey = when (Pref.ipVisibilityStyle(App1.pref)) {
|
||||
Pref.VS_MASTODON -> false
|
||||
Pref.VS_MISSKEY -> true
|
||||
val isMisskey = when (PrefI.ipVisibilityStyle(App1.pref)) {
|
||||
PrefI.VS_MASTODON -> false
|
||||
PrefI.VS_MISSKEY -> true
|
||||
else -> isMisskeyData
|
||||
}
|
||||
return when {
|
||||
|
@ -84,9 +84,9 @@ object Styler {
|
|||
isMisskeyData: Boolean,
|
||||
visibility: TootVisibility
|
||||
): String {
|
||||
val isMisskey = when (Pref.ipVisibilityStyle(App1.pref)) {
|
||||
Pref.VS_MASTODON -> false
|
||||
Pref.VS_MISSKEY -> true
|
||||
val isMisskey = when (PrefI.ipVisibilityStyle(App1.pref)) {
|
||||
PrefI.VS_MASTODON -> false
|
||||
PrefI.VS_MISSKEY -> true
|
||||
else -> isMisskeyData
|
||||
}
|
||||
return context.getString(
|
||||
|
@ -174,11 +174,11 @@ object Styler {
|
|||
alphaMultiplier: Float
|
||||
) {
|
||||
fun colorAccent() =
|
||||
Pref.ipButtonFollowingColor(context.pref()).notZero()
|
||||
PrefI.ipButtonFollowingColor(context.pref()).notZero()
|
||||
?: context.attrColor(R.attr.colorImageButtonAccent)
|
||||
|
||||
fun colorError() =
|
||||
Pref.ipButtonFollowRequestColor(context.pref()).notZero()
|
||||
PrefI.ipButtonFollowRequestColor(context.pref()).notZero()
|
||||
?: context.attrColor(R.attr.colorRegexFilterError)
|
||||
|
||||
// 被フォロー状態
|
||||
|
@ -299,13 +299,13 @@ object Styler {
|
|||
val widthDp = dm.widthPixels / dm.density
|
||||
if (widthDp >= 640f && v.resources?.configuration?.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
val pad_lr = (0.5f + dpDelta * dm.density).toInt()
|
||||
when (Pref.ipJustifyWindowContentPortrait(App1.pref)) {
|
||||
Pref.JWCP_START -> {
|
||||
when (PrefI.ipJustifyWindowContentPortrait(App1.pref)) {
|
||||
PrefI.JWCP_START -> {
|
||||
v.setPaddingRelative(pad_lr, pad_t, pad_lr + dm.widthPixels / 2, pad_b)
|
||||
return
|
||||
}
|
||||
|
||||
Pref.JWCP_END -> {
|
||||
PrefI.JWCP_END -> {
|
||||
v.setPaddingRelative(pad_lr + dm.widthPixels / 2, pad_t, pad_lr, pad_b)
|
||||
return
|
||||
}
|
||||
|
@ -328,14 +328,14 @@ object Styler {
|
|||
log.d("fixHorizontalMargin: orientation=$orientationString, w=${widthDp}dp, h=${dm.heightPixels / dm.density}")
|
||||
|
||||
if (widthDp >= 640f && v.resources?.configuration?.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
when (Pref.ipJustifyWindowContentPortrait(App1.pref)) {
|
||||
Pref.JWCP_START -> {
|
||||
when (PrefI.ipJustifyWindowContentPortrait(App1.pref)) {
|
||||
PrefI.JWCP_START -> {
|
||||
lp.marginStart = 0
|
||||
lp.marginEnd = dm.widthPixels / 2
|
||||
return
|
||||
}
|
||||
|
||||
Pref.JWCP_END -> {
|
||||
PrefI.JWCP_END -> {
|
||||
lp.marginStart = dm.widthPixels / 2
|
||||
lp.marginEnd = 0
|
||||
return
|
||||
|
|
|
@ -29,9 +29,22 @@ import org.jetbrains.anko.textColor
|
|||
|
||||
internal class ViewHolderHeaderProfile(
|
||||
activity: ActMain,
|
||||
viewRoot: View
|
||||
viewRoot: View,
|
||||
) : ViewHolderHeaderBase(activity, viewRoot), View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
companion object {
|
||||
private fun SpannableStringBuilder.appendSpan(text: String, span: Any) {
|
||||
val start = length
|
||||
append(text)
|
||||
setSpan(
|
||||
span,
|
||||
start,
|
||||
length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val ivBackground: MyNetworkImageView
|
||||
private val tvCreated: TextView
|
||||
private val tvLastStatusAt: TextView
|
||||
|
@ -73,6 +86,9 @@ internal class ViewHolderHeaderProfile(
|
|||
private val tvPersonalNotes: TextView
|
||||
private val btnPersonalNotesEdit: ImageButton
|
||||
|
||||
private var contentColor = 0
|
||||
private var relation: UserRelation? = null
|
||||
|
||||
init {
|
||||
ivBackground = viewRoot.findViewById(R.id.ivBackground)
|
||||
llProfile = viewRoot.findViewById(R.id.llProfile)
|
||||
|
@ -136,6 +152,38 @@ internal class ViewHolderHeaderProfile(
|
|||
ivBackground.measureProfileBg = true
|
||||
}
|
||||
|
||||
override fun getAccount(): TootAccountRef? = whoRef
|
||||
|
||||
override fun onViewRecycled() {
|
||||
}
|
||||
|
||||
// fun updateRelativeTime() {
|
||||
// val who = whoRef?.get()
|
||||
// if(who != null) {
|
||||
// tvCreated.text = TootStatus.formatTime(tvCreated.context, who.time_created_at, true)
|
||||
// }
|
||||
// }
|
||||
|
||||
override fun bindData(column: Column) {
|
||||
super.bindData(column)
|
||||
|
||||
bindFonts()
|
||||
bindColors()
|
||||
|
||||
llMoved.visibility = View.GONE
|
||||
tvMoved.visibility = View.GONE
|
||||
llFields.visibility = View.GONE
|
||||
llFields.removeAllViews()
|
||||
|
||||
val whoRef = column.whoAccount
|
||||
this.whoRef = whoRef
|
||||
when (val who = whoRef?.get()) {
|
||||
null -> bindAccountNull()
|
||||
else -> bindAccount(who, whoRef)
|
||||
}
|
||||
}
|
||||
|
||||
// カラム設定から戻った際に呼ばれる
|
||||
override fun showColor() {
|
||||
llProfile.setBackgroundColor(
|
||||
when (val c = column.columnBgColor) {
|
||||
|
@ -145,36 +193,8 @@ internal class ViewHolderHeaderProfile(
|
|||
)
|
||||
}
|
||||
|
||||
private var contentColor = 0
|
||||
|
||||
private var relation: UserRelation? = null
|
||||
|
||||
override fun bindData(column: Column) {
|
||||
super.bindData(column)
|
||||
|
||||
var f: Float
|
||||
|
||||
f = activity.timelineFontSizeSp
|
||||
if (!f.isNaN()) {
|
||||
tvMovedName.textSize = f
|
||||
tvMoved.textSize = f
|
||||
tvPersonalNotes.textSize = f
|
||||
tvFeaturedTags.textSize = f
|
||||
}
|
||||
|
||||
f = activity.acctFontSizeSp
|
||||
if (!f.isNaN()) {
|
||||
tvMovedAcct.textSize = f
|
||||
tvCreated.textSize = f
|
||||
tvLastStatusAt.textSize = f
|
||||
}
|
||||
|
||||
val spacing = activity.timelineSpacing
|
||||
if (spacing != null) {
|
||||
tvMovedName.setLineSpacing(0f, spacing)
|
||||
tvMoved.setLineSpacing(0f, spacing)
|
||||
}
|
||||
|
||||
// bind時に呼ばれる
|
||||
private fun bindColors() {
|
||||
val contentColor = column.getContentColor()
|
||||
this.contentColor = contentColor
|
||||
|
||||
|
@ -210,313 +230,140 @@ internal class ViewHolderHeaderProfile(
|
|||
tvMovedAcct.textColor = acctColor
|
||||
tvLastStatusAt.textColor = acctColor
|
||||
|
||||
val whoRef = column.whoAccount
|
||||
this.whoRef = whoRef
|
||||
val who = whoRef?.get()
|
||||
showColor()
|
||||
}
|
||||
|
||||
private fun bindFonts() {
|
||||
var f: Float
|
||||
|
||||
f = activity.timelineFontSizeSp
|
||||
if (!f.isNaN()) {
|
||||
tvMovedName.textSize = f
|
||||
tvMoved.textSize = f
|
||||
tvPersonalNotes.textSize = f
|
||||
tvFeaturedTags.textSize = f
|
||||
}
|
||||
|
||||
f = activity.acctFontSizeSp
|
||||
if (!f.isNaN()) {
|
||||
tvMovedAcct.textSize = f
|
||||
tvCreated.textSize = f
|
||||
tvLastStatusAt.textSize = f
|
||||
}
|
||||
|
||||
val spacing = activity.timelineSpacing
|
||||
if (spacing != null) {
|
||||
tvMovedName.setLineSpacing(0f, spacing)
|
||||
tvMoved.setLineSpacing(0f, spacing)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindAccountNull() {
|
||||
relation = null
|
||||
tvCreated.text = ""
|
||||
tvLastStatusAt.vg(false)
|
||||
tvFeaturedTags.vg(false)
|
||||
ivBackground.setImageDrawable(null)
|
||||
ivAvatar.setImageDrawable(null)
|
||||
|
||||
tvAcct.text = "@"
|
||||
|
||||
tvDisplayName.text = ""
|
||||
nameInvalidator1.register(null)
|
||||
|
||||
tvNote.text = ""
|
||||
tvMisskeyExtra.text = ""
|
||||
noteInvalidator.register(null)
|
||||
|
||||
btnStatusCount.text = activity.getString(R.string.statuses) + "\n" + "?"
|
||||
btnFollowing.text = activity.getString(R.string.following) + "\n" + "?"
|
||||
btnFollowers.text = activity.getString(R.string.followers) + "\n" + "?"
|
||||
|
||||
btnFollow.setImageDrawable(null)
|
||||
tvRemoteProfileWarning.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun bindAccount(who: TootAccount, whoRef: TootAccountRef) {
|
||||
|
||||
// Misskeyの場合はNote中のUserエンティティと /api/users/show の情報量がかなり異なる
|
||||
val whoDetail = if (who == null) {
|
||||
null
|
||||
} else {
|
||||
MisskeyAccountDetailMap.get(accessInfo, who.id)
|
||||
val whoDetail = MisskeyAccountDetailMap.get(accessInfo, who.id)
|
||||
|
||||
tvCreated.text =
|
||||
TootStatus.formatTime(tvCreated.context, (whoDetail ?: who).time_created_at, true)
|
||||
|
||||
who.setAccountExtra(
|
||||
accessInfo,
|
||||
tvLastStatusAt,
|
||||
invalidator = null,
|
||||
fromProfileHeader = true
|
||||
)
|
||||
|
||||
val featuredTagsText = formatFeaturedTags()
|
||||
tvFeaturedTags.vg(featuredTagsText != null)?.let {
|
||||
it.text = featuredTagsText!!
|
||||
it.movementMethod = MyLinkMovementMethod
|
||||
}
|
||||
|
||||
showColor()
|
||||
ivBackground.setImageUrl(activity.pref, 0f, accessInfo.supplyBaseUrl(who.header_static))
|
||||
|
||||
llMoved.visibility = View.GONE
|
||||
tvMoved.visibility = View.GONE
|
||||
llFields.visibility = View.GONE
|
||||
llFields.removeAllViews()
|
||||
ivAvatar.setImageUrl(
|
||||
activity.pref,
|
||||
Styler.calcIconRound(ivAvatar.layoutParams),
|
||||
accessInfo.supplyBaseUrl(who.avatar_static),
|
||||
accessInfo.supplyBaseUrl(who.avatar)
|
||||
)
|
||||
|
||||
if (who == null) {
|
||||
relation = null
|
||||
tvCreated.text = ""
|
||||
tvLastStatusAt.vg(false)
|
||||
tvFeaturedTags.vg(false)
|
||||
ivBackground.setImageDrawable(null)
|
||||
ivAvatar.setImageDrawable(null)
|
||||
val name = whoDetail?.decodeDisplayName(activity) ?: whoRef.decoded_display_name
|
||||
tvDisplayName.text = name
|
||||
nameInvalidator1.register(name)
|
||||
|
||||
tvAcct.text = "@"
|
||||
tvRemoteProfileWarning.vg(column.accessInfo.isRemoteUser(who))
|
||||
|
||||
tvDisplayName.text = ""
|
||||
nameInvalidator1.register(null)
|
||||
tvAcct.text = encodeAcctText(who, whoDetail)
|
||||
|
||||
tvNote.text = ""
|
||||
tvMisskeyExtra.text = ""
|
||||
noteInvalidator.register(null)
|
||||
val note = whoRef.decoded_note
|
||||
tvNote.text = note
|
||||
noteInvalidator.register(note)
|
||||
|
||||
btnStatusCount.text = activity.getString(R.string.statuses) + "\n" + "?"
|
||||
btnFollowing.text = activity.getString(R.string.following) + "\n" + "?"
|
||||
btnFollowers.text = activity.getString(R.string.followers) + "\n" + "?"
|
||||
tvMisskeyExtra.text = encodeMisskeyExtra(whoDetail)
|
||||
tvMisskeyExtra.vg(tvMisskeyExtra.text.isNotEmpty())
|
||||
|
||||
btnFollow.setImageDrawable(null)
|
||||
tvRemoteProfileWarning.visibility = View.GONE
|
||||
} else {
|
||||
tvCreated.text =
|
||||
TootStatus.formatTime(tvCreated.context, (whoDetail ?: who).time_created_at, true)
|
||||
btnStatusCount.text =
|
||||
"${activity.getString(R.string.statuses)}\n${
|
||||
whoDetail?.statuses_count ?: who.statuses_count
|
||||
}"
|
||||
|
||||
who.setAccountExtra(
|
||||
accessInfo,
|
||||
tvLastStatusAt,
|
||||
invalidator = null,
|
||||
fromProfileHeader = true
|
||||
)
|
||||
val hideFollowCount = PrefB.bpHideFollowCount(activity.pref)
|
||||
|
||||
val featuredTagsText = column.whoFeaturedTags?.notEmpty()?.let { tagList ->
|
||||
SpannableStringBuilder().apply {
|
||||
append(activity.getString(R.string.featured_hashtags))
|
||||
append(":")
|
||||
tagList.forEach { tag ->
|
||||
append(" ")
|
||||
val tagWithSharp = "#" + tag.name
|
||||
val start = length
|
||||
append(tagWithSharp)
|
||||
val end = length
|
||||
tag.url?.notEmpty()?.let { url ->
|
||||
val span = MyClickableSpan(
|
||||
LinkInfo(url = url, tag = tag.name, caption = tagWithSharp)
|
||||
)
|
||||
setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tvFeaturedTags.vg(featuredTagsText != null)?.let {
|
||||
it.text = featuredTagsText!!
|
||||
it.movementMethod = MyLinkMovementMethod
|
||||
}
|
||||
|
||||
ivBackground.setImageUrl(
|
||||
activity.pref,
|
||||
0f,
|
||||
accessInfo.supplyBaseUrl(who.header_static)
|
||||
)
|
||||
|
||||
ivAvatar.setImageUrl(
|
||||
activity.pref,
|
||||
Styler.calcIconRound(ivAvatar.layoutParams),
|
||||
accessInfo.supplyBaseUrl(who.avatar_static),
|
||||
accessInfo.supplyBaseUrl(who.avatar)
|
||||
)
|
||||
|
||||
val name = whoDetail?.decodeDisplayName(activity) ?: whoRef.decoded_display_name
|
||||
tvDisplayName.text = name
|
||||
nameInvalidator1.register(name)
|
||||
|
||||
tvRemoteProfileWarning.visibility =
|
||||
if (column.accessInfo.isRemoteUser(who)) View.VISIBLE else View.GONE
|
||||
|
||||
fun SpannableStringBuilder.appendSpan(text: String, span: Any) {
|
||||
val start = length
|
||||
append(text)
|
||||
setSpan(
|
||||
span,
|
||||
start,
|
||||
length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
tvAcct.text = SpannableStringBuilder().apply {
|
||||
|
||||
append("@")
|
||||
|
||||
append(accessInfo.getFullAcct(who).pretty)
|
||||
|
||||
if (whoDetail?.locked ?: who.locked) {
|
||||
append(" ")
|
||||
val emoji = EmojiMap.shortNameMap["lock"]
|
||||
if (emoji != null) {
|
||||
appendSpan("locked", emoji.createSpan(activity))
|
||||
} else {
|
||||
append("locked")
|
||||
}
|
||||
}
|
||||
|
||||
if (who.bot) {
|
||||
append(" ")
|
||||
val emoji = EmojiMap.shortNameMap["robot_face"]
|
||||
if (emoji != null) {
|
||||
appendSpan("bot", emoji.createSpan(activity))
|
||||
} else {
|
||||
append("bot")
|
||||
}
|
||||
}
|
||||
|
||||
if (who.suspended) {
|
||||
append(" ")
|
||||
val emoji = EmojiMap.shortNameMap["cross_mark"]
|
||||
if (emoji != null) {
|
||||
appendSpan("suspended", emoji.createSpan(activity))
|
||||
} else {
|
||||
append("suspended")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val note = whoRef.decoded_note
|
||||
tvNote.text = note
|
||||
noteInvalidator.register(note)
|
||||
|
||||
tvMisskeyExtra.text = SpannableStringBuilder().apply {
|
||||
var s = whoDetail?.location
|
||||
if (s?.isNotEmpty() == true) {
|
||||
if (isNotEmpty()) append('\n')
|
||||
appendSpan(
|
||||
activity.getString(R.string.location),
|
||||
EmojiImageSpan(
|
||||
activity,
|
||||
R.drawable.ic_location,
|
||||
useColorShader = true
|
||||
)
|
||||
)
|
||||
append(' ')
|
||||
append(s)
|
||||
}
|
||||
s = whoDetail?.birthday
|
||||
if (s?.isNotEmpty() == true) {
|
||||
if (isNotEmpty()) append('\n')
|
||||
appendSpan(
|
||||
activity.getString(R.string.birthday),
|
||||
EmojiImageSpan(
|
||||
activity,
|
||||
R.drawable.ic_cake,
|
||||
useColorShader = true
|
||||
)
|
||||
)
|
||||
append(' ')
|
||||
append(s)
|
||||
}
|
||||
}
|
||||
tvMisskeyExtra.vg(tvMisskeyExtra.text.isNotEmpty())
|
||||
|
||||
btnStatusCount.text =
|
||||
"${activity.getString(R.string.statuses)}\n${
|
||||
whoDetail?.statuses_count
|
||||
?: who.statuses_count
|
||||
}"
|
||||
|
||||
if (Pref.bpHideFollowCount(activity.pref)) {
|
||||
btnFollowing.text = activity.getString(R.string.following)
|
||||
btnFollowers.text = activity.getString(R.string.followers)
|
||||
} else {
|
||||
btnFollowing.text =
|
||||
"${activity.getString(R.string.following)}\n${
|
||||
whoDetail?.following_count ?: who.following_count
|
||||
}"
|
||||
btnFollowers.text =
|
||||
"${activity.getString(R.string.followers)}\n${
|
||||
whoDetail?.followers_count ?: who.followers_count
|
||||
}"
|
||||
}
|
||||
|
||||
val relation = UserRelation.load(accessInfo.db_id, who.id)
|
||||
this.relation = relation
|
||||
|
||||
Styler.setFollowIcon(
|
||||
activity,
|
||||
btnFollow,
|
||||
ivFollowedBy,
|
||||
relation,
|
||||
who,
|
||||
contentColor,
|
||||
alphaMultiplier = Styler.boostAlpha
|
||||
)
|
||||
|
||||
tvPersonalNotes.text = relation.note ?: ""
|
||||
|
||||
showMoved(who, who.movedRef)
|
||||
|
||||
val fields = whoDetail?.fields ?: who.fields
|
||||
if (fields != null) {
|
||||
|
||||
llFields.visibility = View.VISIBLE
|
||||
|
||||
// fieldsのnameにはカスタム絵文字が適用されるようになった
|
||||
// https://github.com/tootsuite/mastodon/pull/11350
|
||||
// fieldsのvalueはMisskeyならMFM、MastodonならHTML
|
||||
val fieldDecodeOptions = DecodeOptions(
|
||||
context = activity,
|
||||
decodeEmoji = true,
|
||||
linkHelper = accessInfo,
|
||||
short = true,
|
||||
emojiMapCustom = who.custom_emojis,
|
||||
emojiMapProfile = who.profile_emojis,
|
||||
mentionDefaultHostDomain = who
|
||||
)
|
||||
|
||||
val nameTypeface = ActMain.timeline_font_bold
|
||||
val valueTypeface = ActMain.timelineFont
|
||||
|
||||
for (item in fields) {
|
||||
|
||||
//
|
||||
val nameView = MyTextView(activity)
|
||||
val nameLp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
val nameText = fieldDecodeOptions.decodeEmoji(item.name)
|
||||
val nameInvalidator = NetworkEmojiInvalidator(activity.handler, nameView)
|
||||
nameInvalidator.register(nameText)
|
||||
|
||||
nameLp.topMargin = (density * 6f).toInt()
|
||||
nameView.layoutParams = nameLp
|
||||
nameView.text = nameText
|
||||
nameView.setTextColor(contentColor)
|
||||
nameView.typeface = nameTypeface
|
||||
nameView.movementMethod = MyLinkMovementMethod
|
||||
llFields.addView(nameView)
|
||||
|
||||
// 値の方はHTMLエンコードされている
|
||||
val valueView = MyTextView(activity)
|
||||
val valueLp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
|
||||
val valueText = fieldDecodeOptions.decodeHTML(item.value)
|
||||
if (item.verified_at > 0L) {
|
||||
valueText.append('\n')
|
||||
|
||||
val start = valueText.length
|
||||
valueText.append(activity.getString(R.string.verified_at))
|
||||
valueText.append(": ")
|
||||
valueText.append(TootStatus.formatTime(activity, item.verified_at, false))
|
||||
val end = valueText.length
|
||||
|
||||
val linkFgColor = Pref.ipVerifiedLinkFgColor(activity.pref).notZero()
|
||||
?: (Color.BLACK or 0x7fbc99)
|
||||
|
||||
valueText.setSpan(
|
||||
ForegroundColorSpan(linkFgColor),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
val valueInvalidator = NetworkEmojiInvalidator(activity.handler, valueView)
|
||||
valueInvalidator.register(valueText)
|
||||
|
||||
valueLp.startMargin = (density * 32f).toInt()
|
||||
valueView.layoutParams = valueLp
|
||||
valueView.text = valueText
|
||||
valueView.setTextColor(contentColor)
|
||||
valueView.typeface = valueTypeface
|
||||
valueView.movementMethod = MyLinkMovementMethod
|
||||
|
||||
if (item.verified_at > 0L) {
|
||||
val linkBgColor = Pref.ipVerifiedLinkBgColor(activity.pref).notZero()
|
||||
?: (0x337fbc99)
|
||||
|
||||
valueView.setBackgroundColor(linkBgColor)
|
||||
}
|
||||
|
||||
llFields.addView(valueView)
|
||||
}
|
||||
}
|
||||
var caption = activity.getString(R.string.following)
|
||||
btnFollowing.text = when {
|
||||
hideFollowCount -> caption
|
||||
else -> "${caption}\n${whoDetail?.following_count ?: who.following_count}"
|
||||
}
|
||||
|
||||
caption = activity.getString(R.string.followers)
|
||||
btnFollowers.text = when {
|
||||
hideFollowCount -> caption
|
||||
else -> "${caption}\n${whoDetail?.followers_count ?: who.followers_count}"
|
||||
}
|
||||
|
||||
val relation = UserRelation.load(accessInfo.db_id, who.id)
|
||||
this.relation = relation
|
||||
Styler.setFollowIcon(
|
||||
activity,
|
||||
btnFollow,
|
||||
ivFollowedBy,
|
||||
relation,
|
||||
who,
|
||||
contentColor,
|
||||
alphaMultiplier = Styler.boostAlpha
|
||||
)
|
||||
|
||||
tvPersonalNotes.text = relation.note ?: ""
|
||||
|
||||
showMoved(who, who.movedRef)
|
||||
|
||||
(whoDetail?.fields ?: who.fields)?.notEmpty()?.let { showFields(who, it) }
|
||||
}
|
||||
|
||||
private fun showMoved(who: TootAccount, movedRef: TootAccountRef?) {
|
||||
|
@ -557,20 +404,6 @@ internal class ViewHolderHeaderProfile(
|
|||
)
|
||||
}
|
||||
|
||||
private fun setAcct(tv: TextView, accessInfo: SavedAccount, who: TootAccount) {
|
||||
val ac = AcctColor.load(accessInfo, who)
|
||||
tv.text = when {
|
||||
AcctColor.hasNickname(ac) -> ac.nickname
|
||||
Pref.bpShortAcctLocalUser(App1.pref) -> "@${who.acct.pretty}"
|
||||
else -> "@${ac.nickname}"
|
||||
}
|
||||
|
||||
tv.textColor = ac.color_fg.notZero() ?: column.getAcctColor()
|
||||
|
||||
tv.setBackgroundColor(ac.color_bg) // may 0
|
||||
tv.setPaddingRelative(activity.acctPadLr, 0, activity.acctPadLr, 0)
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
|
||||
when (v.id) {
|
||||
|
@ -695,15 +528,190 @@ internal class ViewHolderHeaderProfile(
|
|||
return false
|
||||
}
|
||||
|
||||
override fun onViewRecycled() {
|
||||
private fun setAcct(tv: TextView, accessInfo: SavedAccount, who: TootAccount) {
|
||||
val ac = AcctColor.load(accessInfo, who)
|
||||
tv.text = when {
|
||||
AcctColor.hasNickname(ac) -> ac.nickname
|
||||
PrefB.bpShortAcctLocalUser(App1.pref) -> "@${who.acct.pretty}"
|
||||
else -> "@${ac.nickname}"
|
||||
}
|
||||
|
||||
tv.textColor = ac.color_fg.notZero() ?: column.getAcctColor()
|
||||
|
||||
tv.setBackgroundColor(ac.color_bg) // may 0
|
||||
tv.setPaddingRelative(activity.acctPadLr, 0, activity.acctPadLr, 0)
|
||||
}
|
||||
|
||||
// fun updateRelativeTime() {
|
||||
// val who = whoRef?.get()
|
||||
// if(who != null) {
|
||||
// tvCreated.text = TootStatus.formatTime(tvCreated.context, who.time_created_at, true)
|
||||
// }
|
||||
// }
|
||||
private fun formatFeaturedTags() = column.whoFeaturedTags?.notEmpty()?.let { tagList ->
|
||||
SpannableStringBuilder().apply {
|
||||
append(activity.getString(R.string.featured_hashtags))
|
||||
append(":")
|
||||
tagList.forEach { tag ->
|
||||
append(" ")
|
||||
val tagWithSharp = "#" + tag.name
|
||||
val start = length
|
||||
append(tagWithSharp)
|
||||
val end = length
|
||||
tag.url?.notEmpty()?.let { url ->
|
||||
val span = MyClickableSpan(
|
||||
LinkInfo(url = url, tag = tag.name, caption = tagWithSharp)
|
||||
)
|
||||
setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAccount(): TootAccountRef? = whoRef
|
||||
private fun encodeAcctText(who: TootAccount, whoDetail: TootAccount?) = SpannableStringBuilder().apply {
|
||||
append("@")
|
||||
append(accessInfo.getFullAcct(who).pretty)
|
||||
if (whoDetail?.locked ?: who.locked) {
|
||||
append(" ")
|
||||
val emoji = EmojiMap.shortNameMap["lock"]
|
||||
if (emoji != null) {
|
||||
appendSpan("locked", emoji.createSpan(activity))
|
||||
} else {
|
||||
append("locked")
|
||||
}
|
||||
}
|
||||
|
||||
if (who.bot) {
|
||||
append(" ")
|
||||
val emoji = EmojiMap.shortNameMap["robot_face"]
|
||||
if (emoji != null) {
|
||||
appendSpan("bot", emoji.createSpan(activity))
|
||||
} else {
|
||||
append("bot")
|
||||
}
|
||||
}
|
||||
|
||||
if (who.suspended) {
|
||||
append(" ")
|
||||
val emoji = EmojiMap.shortNameMap["cross_mark"]
|
||||
if (emoji != null) {
|
||||
appendSpan("suspended", emoji.createSpan(activity))
|
||||
} else {
|
||||
append("suspended")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun encodeMisskeyExtra(whoDetail: TootAccount?) = SpannableStringBuilder().apply {
|
||||
var s = whoDetail?.location
|
||||
if (s?.isNotEmpty() == true) {
|
||||
if (isNotEmpty()) append('\n')
|
||||
appendSpan(
|
||||
activity.getString(R.string.location),
|
||||
EmojiImageSpan(
|
||||
activity,
|
||||
R.drawable.ic_location,
|
||||
useColorShader = true
|
||||
)
|
||||
)
|
||||
append(' ')
|
||||
append(s)
|
||||
}
|
||||
s = whoDetail?.birthday
|
||||
if (s?.isNotEmpty() == true) {
|
||||
if (isNotEmpty()) append('\n')
|
||||
appendSpan(
|
||||
activity.getString(R.string.birthday),
|
||||
EmojiImageSpan(
|
||||
activity,
|
||||
R.drawable.ic_cake,
|
||||
useColorShader = true
|
||||
)
|
||||
)
|
||||
append(' ')
|
||||
append(s)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFields(who: TootAccount, fields: List<TootAccount.Field>) {
|
||||
llFields.visibility = View.VISIBLE
|
||||
|
||||
// fieldsのnameにはカスタム絵文字が適用されるようになった
|
||||
// https://github.com/tootsuite/mastodon/pull/11350
|
||||
// fieldsのvalueはMisskeyならMFM、MastodonならHTML
|
||||
val fieldDecodeOptions = DecodeOptions(
|
||||
context = activity,
|
||||
decodeEmoji = true,
|
||||
linkHelper = accessInfo,
|
||||
short = true,
|
||||
emojiMapCustom = who.custom_emojis,
|
||||
emojiMapProfile = who.profile_emojis,
|
||||
mentionDefaultHostDomain = who
|
||||
)
|
||||
|
||||
val nameTypeface = ActMain.timeline_font_bold
|
||||
val valueTypeface = ActMain.timelineFont
|
||||
|
||||
for (item in fields) {
|
||||
|
||||
//
|
||||
val nameView = MyTextView(activity)
|
||||
val nameLp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
val nameText = fieldDecodeOptions.decodeEmoji(item.name)
|
||||
val nameInvalidator = NetworkEmojiInvalidator(activity.handler, nameView)
|
||||
nameInvalidator.register(nameText)
|
||||
|
||||
nameLp.topMargin = (density * 6f).toInt()
|
||||
nameView.layoutParams = nameLp
|
||||
nameView.text = nameText
|
||||
nameView.setTextColor(contentColor)
|
||||
nameView.typeface = nameTypeface
|
||||
nameView.movementMethod = MyLinkMovementMethod
|
||||
llFields.addView(nameView)
|
||||
|
||||
// 値の方はHTMLエンコードされている
|
||||
val valueView = MyTextView(activity)
|
||||
val valueLp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
|
||||
val valueText = fieldDecodeOptions.decodeHTML(item.value)
|
||||
if (item.verified_at > 0L) {
|
||||
valueText.append('\n')
|
||||
|
||||
val start = valueText.length
|
||||
valueText.append(activity.getString(R.string.verified_at))
|
||||
valueText.append(": ")
|
||||
valueText.append(TootStatus.formatTime(activity, item.verified_at, false))
|
||||
val end = valueText.length
|
||||
|
||||
val linkFgColor = PrefI.ipVerifiedLinkFgColor(activity.pref).notZero()
|
||||
?: (Color.BLACK or 0x7fbc99)
|
||||
|
||||
valueText.setSpan(
|
||||
ForegroundColorSpan(linkFgColor),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
val valueInvalidator = NetworkEmojiInvalidator(activity.handler, valueView)
|
||||
valueInvalidator.register(valueText)
|
||||
|
||||
valueLp.startMargin = (density * 32f).toInt()
|
||||
valueView.layoutParams = valueLp
|
||||
valueView.text = valueText
|
||||
valueView.setTextColor(contentColor)
|
||||
valueView.typeface = valueTypeface
|
||||
valueView.movementMethod = MyLinkMovementMethod
|
||||
|
||||
if (item.verified_at > 0L) {
|
||||
val linkBgColor = PrefI.ipVerifiedLinkBgColor(activity.pref).notZero()
|
||||
?: (0x337fbc99)
|
||||
|
||||
valueView.setBackgroundColor(linkBgColor)
|
||||
}
|
||||
|
||||
llFields.addView(valueView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,10 +116,10 @@ fun ActMain.accountAdd() {
|
|||
when (action) {
|
||||
|
||||
LoginForm.Action.Existing ->
|
||||
client.authentication1(Pref.spClientName(pref))
|
||||
client.authentication1(PrefS.spClientName(pref))
|
||||
|
||||
LoginForm.Action.Create ->
|
||||
client.createUser1(Pref.spClientName(pref))
|
||||
client.createUser1(PrefS.spClientName(pref))
|
||||
|
||||
LoginForm.Action.Pseudo, LoginForm.Action.Token -> {
|
||||
val (ti, ri) = TootInstance.get(client)
|
||||
|
@ -204,8 +204,8 @@ fun AppCompatActivity.accountRemove(account: SavedAccount) {
|
|||
// if account is default account of tablet mode,
|
||||
// reset default.
|
||||
val pref = pref()
|
||||
if (account.db_id == Pref.lpTabletTootDefaultAccount(pref)) {
|
||||
pref.edit().put(Pref.lpTabletTootDefaultAccount, -1L).apply()
|
||||
if (account.db_id == PrefL.lpTabletTootDefaultAccount(pref)) {
|
||||
pref.edit().put(PrefL.lpTabletTootDefaultAccount, -1L).apply()
|
||||
}
|
||||
|
||||
account.delete()
|
||||
|
|
|
@ -0,0 +1,419 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import jp.juggler.subwaytooter.*
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.api.entity.Acct
|
||||
import jp.juggler.subwaytooter.api.entity.EntityId
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.api.entity.TootVisibility
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
||||
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.emptyCallback
|
||||
import jp.juggler.util.JsonObject
|
||||
import jp.juggler.util.launchMain
|
||||
import jp.juggler.util.showToast
|
||||
import jp.juggler.util.toPostRequestBuilder
|
||||
import java.util.ArrayList
|
||||
import kotlin.math.max
|
||||
|
||||
private class BoostImpl(
|
||||
val activity: ActMain,
|
||||
val accessInfo: SavedAccount,
|
||||
val statusArg: TootStatus,
|
||||
val statusOwner: Acct,
|
||||
val crossAccountMode: CrossAccountMode,
|
||||
val bSet: Boolean = true,
|
||||
val visibility: TootVisibility? = null,
|
||||
val callback: () -> Unit,
|
||||
) {
|
||||
// Mastodonは非公開トゥートをブーストできるのは本人だけ
|
||||
val isPrivateToot = accessInfo.isMastodon &&
|
||||
statusArg.visibility == TootVisibility.PrivateFollowers
|
||||
|
||||
var bConfirmed = false
|
||||
|
||||
fun preCheck(): Boolean {
|
||||
|
||||
// アカウントからステータスにブースト操作を行っているなら、何もしない
|
||||
if (activity.appState.isBusyBoost(accessInfo, statusArg)) {
|
||||
activity.showToast(false, R.string.wait_previous_operation)
|
||||
return false
|
||||
}
|
||||
|
||||
if (isPrivateToot && accessInfo.acct != statusOwner) {
|
||||
activity.showToast(false, R.string.boost_private_toot_not_allowed)
|
||||
return false
|
||||
}
|
||||
// DMとかのブーストはAPI側がエラーを出すだろう?
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun confirm(): Boolean {
|
||||
if (bConfirmed) return true
|
||||
DlgConfirm.open(
|
||||
activity,
|
||||
activity.getString(
|
||||
when {
|
||||
!bSet -> R.string.confirm_unboost_from
|
||||
isPrivateToot -> R.string.confirm_boost_private_from
|
||||
visibility == TootVisibility.PrivateFollowers -> R.string.confirm_private_boost_from
|
||||
else -> R.string.confirm_boost_from
|
||||
},
|
||||
AcctColor.getNickname(accessInfo)
|
||||
),
|
||||
object : DlgConfirm.Callback {
|
||||
override fun onOK() {
|
||||
bConfirmed = true
|
||||
run()
|
||||
}
|
||||
|
||||
override var isConfirmEnabled: Boolean
|
||||
get() = when (bSet) {
|
||||
true -> accessInfo.confirm_boost
|
||||
else -> accessInfo.confirm_unboost
|
||||
}
|
||||
set(value) {
|
||||
when (bSet) {
|
||||
true -> accessInfo.confirm_boost = value
|
||||
else -> accessInfo.confirm_unboost = value
|
||||
}
|
||||
accessInfo.saveSetting()
|
||||
activity.reloadAccountSetting(accessInfo)
|
||||
}
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
fun run() {
|
||||
if (!preCheck()) return
|
||||
if (!confirm()) return
|
||||
|
||||
activity.appState.setBusyBoost(accessInfo, statusArg)
|
||||
// ブースト表示を更新中にする
|
||||
activity.showColumnMatchAccount(accessInfo)
|
||||
// misskeyは非公開トゥートをブーストできないっぽい
|
||||
|
||||
launchMain {
|
||||
var resultStatus: TootStatus? = null
|
||||
var resultUnrenoteId: EntityId? = null
|
||||
val result = activity.runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
||||
|
||||
val parser = TootParser(this, accessInfo)
|
||||
|
||||
val targetStatus = if (crossAccountMode.isRemote) {
|
||||
val (result, status) = client.syncStatus(accessInfo, statusArg)
|
||||
if (status == null) return@runApiTask result
|
||||
if (status.reblogged) {
|
||||
return@runApiTask TootApiResult(getString(R.string.already_boosted))
|
||||
}
|
||||
status
|
||||
} else {
|
||||
// 既に自タンスのステータスがある
|
||||
statusArg
|
||||
}
|
||||
|
||||
if (accessInfo.isMisskey) {
|
||||
if (!bSet) {
|
||||
val myRenoteId = targetStatus.myRenoteId
|
||||
?: return@runApiTask TootApiResult("missing renote id.")
|
||||
|
||||
client.request(
|
||||
"/api/notes/delete",
|
||||
accessInfo.putMisskeyApiToken().apply {
|
||||
put("noteId", myRenoteId.toString())
|
||||
put("renoteId", targetStatus.id.toString())
|
||||
}
|
||||
.toPostRequestBuilder()
|
||||
)
|
||||
?.also {
|
||||
if (it.response?.code == 204) {
|
||||
resultUnrenoteId = myRenoteId
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client.request(
|
||||
"/api/notes/create",
|
||||
accessInfo.putMisskeyApiToken().apply {
|
||||
put("renoteId", targetStatus.id.toString())
|
||||
}
|
||||
.toPostRequestBuilder()
|
||||
)
|
||||
?.also { result ->
|
||||
val jsonObject = result.jsonObject
|
||||
if (jsonObject != null) {
|
||||
val outerStatus = parser.status(
|
||||
jsonObject.jsonObject("createdNote")
|
||||
?: jsonObject
|
||||
)
|
||||
val innerStatus = outerStatus?.reblog ?: outerStatus
|
||||
if (outerStatus != null && innerStatus != null && outerStatus != innerStatus) {
|
||||
innerStatus.myRenoteId = outerStatus.id
|
||||
innerStatus.reblogged = true
|
||||
}
|
||||
// renoteそのものではなくrenoteされた元noteが欲しい
|
||||
resultStatus = innerStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val b = JsonObject().apply {
|
||||
if (visibility != null) put("visibility", visibility.strMastodon)
|
||||
}.toPostRequestBuilder()
|
||||
|
||||
client.request(
|
||||
"/api/v1/statuses/${targetStatus.id}/${if (bSet) "reblog" else "unreblog"}",
|
||||
b
|
||||
)?.also { result ->
|
||||
// reblogはreblogを表すStatusを返す
|
||||
// unreblogはreblogしたStatusを返す
|
||||
val s = parser.status(result.jsonObject)
|
||||
resultStatus = s?.reblog ?: s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activity.appState.resetBusyBoost(accessInfo, statusArg)
|
||||
|
||||
if (result != null) {
|
||||
val unrenoteId = resultUnrenoteId
|
||||
val newStatus = resultStatus
|
||||
when {
|
||||
// Misskeyでunrenoteに成功した
|
||||
unrenoteId != null -> {
|
||||
|
||||
// 星を外したのにカウントが下がらないのは違和感あるので、表示をいじる
|
||||
// 0未満にはならない
|
||||
val count = max(0, (statusArg.reblogs_count ?: 1) - 1)
|
||||
|
||||
for (column in activity.appState.columnList) {
|
||||
column.findStatus(
|
||||
accessInfo.apDomain,
|
||||
statusArg.id
|
||||
) { account, status ->
|
||||
|
||||
// 同タンス別アカウントでもカウントは変化する
|
||||
status.reblogs_count = count
|
||||
|
||||
// 同アカウントならreblogged状態を変化させる
|
||||
if (accessInfo == account && status.myRenoteId == unrenoteId) {
|
||||
status.myRenoteId = null
|
||||
status.reblogged = false
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
// 処理に成功した
|
||||
newStatus != null -> {
|
||||
// カウント数は遅延があるみたいなので、恣意的に表示を変更する
|
||||
// ブーストカウント数を加工する
|
||||
val oldCount = statusArg.reblogs_count
|
||||
val newCount = newStatus.reblogs_count
|
||||
if (oldCount != null && newCount != null) {
|
||||
if (bSet && newStatus.reblogged && newCount <= oldCount) {
|
||||
// 星をつけたのにカウントが上がらないのは違和感あるので、表示をいじる
|
||||
newStatus.reblogs_count = oldCount + 1
|
||||
} else if (!bSet && !newStatus.reblogged && newCount >= oldCount) {
|
||||
// 星を外したのにカウントが下がらないのは違和感あるので、表示をいじる
|
||||
// 0未満にはならない
|
||||
newStatus.reblogs_count = if (oldCount < 1) 0 else oldCount - 1
|
||||
}
|
||||
}
|
||||
|
||||
for (column in activity.appState.columnList) {
|
||||
column.findStatus(
|
||||
accessInfo.apDomain,
|
||||
newStatus.id
|
||||
) { account, status ->
|
||||
|
||||
// 同タンス別アカウントでもカウントは変化する
|
||||
status.reblogs_count = newStatus.reblogs_count
|
||||
|
||||
if (accessInfo == account) {
|
||||
|
||||
// 同アカウントならreblog状態を変化させる
|
||||
when {
|
||||
accessInfo.isMastodon ->
|
||||
status.reblogged = newStatus.reblogged
|
||||
|
||||
bSet && status.myRenoteId == null -> {
|
||||
status.myRenoteId = newStatus.myRenoteId
|
||||
status.reblogged = true
|
||||
}
|
||||
}
|
||||
// Misskey のunrenote時はここを通らない
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
else -> activity.showToast(true, result.error)
|
||||
}
|
||||
}
|
||||
|
||||
// 結果に関わらず、更新中状態から復帰させる
|
||||
activity.showColumnMatchAccount(accessInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.boost(
|
||||
accessInfo: SavedAccount,
|
||||
statusArg: TootStatus,
|
||||
statusOwner: Acct,
|
||||
crossAccountMode: CrossAccountMode,
|
||||
bSet: Boolean = true,
|
||||
visibility: TootVisibility? = null,
|
||||
callback: () -> Unit,
|
||||
) {
|
||||
BoostImpl(
|
||||
activity = this,
|
||||
accessInfo = accessInfo,
|
||||
statusArg = statusArg,
|
||||
statusOwner = statusOwner,
|
||||
crossAccountMode = crossAccountMode,
|
||||
bSet = bSet,
|
||||
visibility = visibility,
|
||||
callback = callback,
|
||||
).run()
|
||||
}
|
||||
|
||||
fun ActMain.boostFromAnotherAccount(
|
||||
timelineAccount: SavedAccount,
|
||||
status: TootStatus?,
|
||||
) {
|
||||
status ?: return
|
||||
launchMain {
|
||||
val statusOwner = timelineAccount.getFullAcct(status.account)
|
||||
|
||||
val isPrivateToot = timelineAccount.isMastodon &&
|
||||
status.visibility == TootVisibility.PrivateFollowers
|
||||
|
||||
if (isPrivateToot) {
|
||||
val list = ArrayList<SavedAccount>()
|
||||
for (a in SavedAccount.loadAccountList(applicationContext)) {
|
||||
if (a.acct == statusOwner) list.add(a)
|
||||
}
|
||||
if (list.isEmpty()) {
|
||||
showToast(false, R.string.boost_private_toot_not_allowed)
|
||||
return@launchMain
|
||||
}
|
||||
|
||||
pickAccount(
|
||||
bAllowPseudo = false,
|
||||
bAuto = false,
|
||||
message = getString(R.string.account_picker_boost),
|
||||
accountListArg = list
|
||||
)?.let { action_account ->
|
||||
boost(
|
||||
action_account,
|
||||
status,
|
||||
statusOwner,
|
||||
calcCrossAccountMode(timelineAccount, action_account),
|
||||
callback = boostCompleteCallback
|
||||
)
|
||||
}
|
||||
} else {
|
||||
pickAccount(
|
||||
bAllowPseudo = false,
|
||||
bAuto = false,
|
||||
message = getString(R.string.account_picker_boost),
|
||||
accountListArg = accountListNonPseudo(timelineAccount.apDomain)
|
||||
)?.let { action_account ->
|
||||
boost(
|
||||
action_account,
|
||||
status,
|
||||
statusOwner,
|
||||
calcCrossAccountMode(timelineAccount, action_account),
|
||||
callback = boostCompleteCallback
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.clickBoostWithVisibility(
|
||||
accessInfo: SavedAccount,
|
||||
status: TootStatus?,
|
||||
) {
|
||||
status ?: return
|
||||
val list = if (accessInfo.isMisskey) {
|
||||
arrayOf(
|
||||
TootVisibility.Public,
|
||||
TootVisibility.UnlistedHome,
|
||||
TootVisibility.PrivateFollowers,
|
||||
TootVisibility.LocalPublic,
|
||||
TootVisibility.LocalHome,
|
||||
TootVisibility.LocalFollowers,
|
||||
TootVisibility.DirectSpecified,
|
||||
TootVisibility.DirectPrivate
|
||||
)
|
||||
} else {
|
||||
arrayOf(
|
||||
TootVisibility.Public,
|
||||
TootVisibility.UnlistedHome,
|
||||
TootVisibility.PrivateFollowers
|
||||
)
|
||||
}
|
||||
val captionList = list
|
||||
.map { Styler.getVisibilityCaption(this, accessInfo.isMisskey, it) }
|
||||
.toTypedArray()
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.choose_visibility)
|
||||
.setItems(captionList) { _, which ->
|
||||
if (which in list.indices) {
|
||||
boost(
|
||||
accessInfo,
|
||||
status,
|
||||
accessInfo.getFullAcct(status.account),
|
||||
CrossAccountMode.SameAccount,
|
||||
visibility = list[which],
|
||||
callback = boostCompleteCallback,
|
||||
)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
fun ActMain.clickBoostBy(
|
||||
pos: Int,
|
||||
accessInfo: SavedAccount,
|
||||
status: TootStatus?,
|
||||
columnType: ColumnType = ColumnType.BOOSTED_BY,
|
||||
) {
|
||||
status ?: return
|
||||
addColumn(false, pos, accessInfo, columnType, status.id)
|
||||
}
|
||||
|
||||
fun ActMain.clickBoost(accessInfo: SavedAccount, status: TootStatus, willToast: Boolean) {
|
||||
if (accessInfo.isPseudo) {
|
||||
boostFromAnotherAccount(accessInfo, status)
|
||||
} else {
|
||||
// トグル動作
|
||||
val bSet = !status.reblogged
|
||||
|
||||
boost(
|
||||
accessInfo,
|
||||
status,
|
||||
accessInfo.getFullAcct(status.account),
|
||||
CrossAccountMode.SameAccount,
|
||||
bSet = bSet,
|
||||
callback = when {
|
||||
!willToast -> emptyCallback
|
||||
// 簡略表示なら結果をトースト表示
|
||||
bSet -> boostCompleteCallback
|
||||
else -> unboostCompleteCallback
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,16 +1,10 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import android.content.Context
|
||||
import jp.juggler.subwaytooter.ActMain
|
||||
import jp.juggler.subwaytooter.ColumnType
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.*
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.api.entity.EntityId
|
||||
import jp.juggler.subwaytooter.api.entity.Host
|
||||
import jp.juggler.subwaytooter.api.entity.TootConversationSummary
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.findStatus
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.matchHost
|
||||
|
@ -20,6 +14,77 @@ import java.util.*
|
|||
|
||||
private val log = LogCategory("Action_Conversation")
|
||||
|
||||
fun ActMain.clickConversation(
|
||||
pos: Int,
|
||||
accessInfo: SavedAccount,
|
||||
|
||||
// optional. 未読表示のクリアに使う
|
||||
listAdapter: ItemListAdapter? = null,
|
||||
|
||||
// どちらか非nullであること
|
||||
status: TootStatus? = null,
|
||||
summary: TootConversationSummary? = null,
|
||||
) {
|
||||
// 未読クリアと表示の更新
|
||||
(summary ?: status?.conversationSummary)?.let {
|
||||
if (conversationUnreadClear(accessInfo, it)) {
|
||||
listAdapter?.notifyChange(
|
||||
reason = "ConversationSummary reset unread",
|
||||
reset = true
|
||||
)
|
||||
}
|
||||
}
|
||||
// 会話カラムを開く
|
||||
(status ?: summary?.last_status)?.let {
|
||||
conversation(pos, accessInfo, it)
|
||||
}
|
||||
}
|
||||
|
||||
// プレビューカードのイメージは返信かもしれない
|
||||
fun ActMain.clickCardImage(pos: Int, accessInfo: SavedAccount, card: TootCard?, longClick: Boolean = false) {
|
||||
card ?: return
|
||||
card.originalStatus?.let {
|
||||
if (longClick) {
|
||||
conversationOtherInstance(pos, it)
|
||||
} else {
|
||||
conversation(pos, accessInfo, it)
|
||||
}
|
||||
return
|
||||
}
|
||||
card.url?.notEmpty()?.let {
|
||||
openCustomTab(this, pos, it, accessInfo = accessInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// 「~からの返信」の表記を押した
|
||||
fun ActMain.clickReplyInfo(
|
||||
pos: Int,
|
||||
accessInfo: SavedAccount,
|
||||
columnType: ColumnType,
|
||||
statusReply: TootStatus?,
|
||||
statusShowing: TootStatus?,
|
||||
longClick: Boolean = false,
|
||||
contextMenuOpener: ActMain.(status: TootStatus) -> Unit = {},
|
||||
) {
|
||||
when {
|
||||
statusReply != null ->
|
||||
if (longClick) {
|
||||
contextMenuOpener(this, statusReply)
|
||||
} else {
|
||||
conversation(pos, accessInfo, statusReply)
|
||||
}
|
||||
|
||||
// tootsearchは返信元のIDを取得するのにひと手間必要
|
||||
columnType == ColumnType.SEARCH_TS ||
|
||||
columnType == ColumnType.SEARCH_NOTESTOCK ->
|
||||
conversationFromTootsearch(pos, statusShowing)
|
||||
|
||||
else ->
|
||||
statusShowing?.in_reply_to_id
|
||||
?.let { conversationLocal(pos, accessInfo, it) }
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// open conversation
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import jp.juggler.subwaytooter.ActKeywordFilter
|
||||
import jp.juggler.subwaytooter.ActMain
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.TootFilter
|
||||
import jp.juggler.subwaytooter.api.runApiTask
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
||||
import jp.juggler.subwaytooter.onFilterDeleted
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
|
@ -13,6 +15,19 @@ import okhttp3.Request
|
|||
|
||||
// private val log = LogCategory("Action_Filter")
|
||||
|
||||
fun ActMain.openFilterMenu(accessInfo: SavedAccount, item: TootFilter?) {
|
||||
item ?: return
|
||||
|
||||
val ad = ActionsDialog()
|
||||
ad.addAction(getString(R.string.edit)) {
|
||||
ActKeywordFilter.open(this, accessInfo, item.id)
|
||||
}
|
||||
ad.addAction(getString(R.string.delete)) {
|
||||
filterDelete(accessInfo, item)
|
||||
}
|
||||
ad.show(this, getString(R.string.filter_of, item.phrase))
|
||||
}
|
||||
|
||||
fun ActMain.filterDelete(
|
||||
accessInfo: SavedAccount,
|
||||
filter: TootFilter,
|
||||
|
|
|
@ -11,41 +11,58 @@ import jp.juggler.subwaytooter.table.SavedAccount
|
|||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.util.*
|
||||
|
||||
fun ActMain.clickFollowInfo(
|
||||
pos: Int,
|
||||
accessInfo: SavedAccount,
|
||||
whoRef: TootAccountRef?,
|
||||
forceMenu: Boolean = false,
|
||||
contextMenuOpener: ActMain.(whoRef: TootAccountRef) -> Unit,
|
||||
) {
|
||||
whoRef ?: return
|
||||
if (forceMenu || accessInfo.isPseudo) {
|
||||
contextMenuOpener(this, whoRef)
|
||||
} else {
|
||||
userProfileLocal(pos, accessInfo, whoRef.get())
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.clickFollow(
|
||||
pos: Int,
|
||||
accessInfo: SavedAccount,
|
||||
who: TootAccount,
|
||||
whoRef: TootAccountRef,
|
||||
relation: UserRelation,
|
||||
relation: UserRelation?,
|
||||
) {
|
||||
relation ?: return
|
||||
val who = whoRef.get()
|
||||
when {
|
||||
accessInfo.isPseudo ->
|
||||
followFromAnotherAccount(pos, accessInfo, who)
|
||||
|
||||
accessInfo.isMisskey &&
|
||||
relation.getRequested(who) &&
|
||||
!relation.getFollowing(who) ->
|
||||
followRequestDelete(
|
||||
pos, accessInfo, whoRef,
|
||||
callback = cancelFollowRequestCompleteCallback
|
||||
)
|
||||
|
||||
else -> {
|
||||
val bSet = !(relation.getRequested(who) || relation.getFollowing(who))
|
||||
follow(
|
||||
pos,
|
||||
accessInfo,
|
||||
whoRef,
|
||||
bFollow = bSet,
|
||||
callback = when (bSet) {
|
||||
true -> followCompleteCallback
|
||||
else -> unfollowCompleteCallback
|
||||
}
|
||||
)
|
||||
}
|
||||
relation.blocking || relation.muting ->
|
||||
Unit // 何もしない
|
||||
accessInfo.isMisskey && relation.getRequested(who) && !relation.getFollowing(who) ->
|
||||
followRequestDelete(pos, accessInfo, whoRef, callback = cancelFollowRequestCompleteCallback)
|
||||
relation.getFollowing(who) || relation.getRequested(who) ->
|
||||
follow(pos, accessInfo, whoRef, bFollow = false, callback = unfollowCompleteCallback)
|
||||
else ->
|
||||
follow(pos, accessInfo, whoRef, bFollow = true, callback = followCompleteCallback)
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.clickFollowRequestAccept(accessInfo: SavedAccount, whoRef: TootAccountRef?, accept: Boolean) {
|
||||
val who = whoRef?.get() ?: return
|
||||
DlgConfirm.openSimple(
|
||||
this,
|
||||
getString(
|
||||
if (accept) R.string.follow_accept_confirm else R.string.follow_deny_confirm,
|
||||
AcctColor.getNickname(accessInfo, who)
|
||||
)
|
||||
) {
|
||||
followRequestAuthorize(accessInfo, whoRef, accept)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fun ActMain.follow(
|
||||
pos: Int,
|
||||
accessInfo: SavedAccount,
|
||||
|
|
|
@ -1,19 +1,57 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import android.app.Dialog
|
||||
import jp.juggler.subwaytooter.ActMain
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.*
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.api.entity.MisskeyAntenna
|
||||
import jp.juggler.subwaytooter.api.entity.TimelineItem
|
||||
import jp.juggler.subwaytooter.api.entity.TootList
|
||||
import jp.juggler.subwaytooter.api.entity.parseItem
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
||||
import jp.juggler.subwaytooter.dialog.DlgTextInput
|
||||
import jp.juggler.subwaytooter.onListListUpdated
|
||||
import jp.juggler.subwaytooter.onListNameUpdated
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.util.*
|
||||
import okhttp3.Request
|
||||
|
||||
fun ActMain.clickListTl(pos: Int, accessInfo: SavedAccount, item: TimelineItem?) {
|
||||
when (item) {
|
||||
is TootList -> addColumn(pos, accessInfo, ColumnType.LIST_TL, item.id)
|
||||
is MisskeyAntenna -> addColumn(pos, accessInfo, ColumnType.MISSKEY_ANTENNA_TL, item.id)
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.clickListMoreButton(pos: Int, accessInfo: SavedAccount, item: TimelineItem?) {
|
||||
when (item) {
|
||||
is TootList -> {
|
||||
ActionsDialog()
|
||||
.addAction(getString(R.string.list_timeline)) {
|
||||
addColumn(pos, accessInfo, ColumnType.LIST_TL, item.id)
|
||||
}
|
||||
.addAction(getString(R.string.list_member)) {
|
||||
addColumn(
|
||||
false,
|
||||
pos,
|
||||
accessInfo,
|
||||
ColumnType.LIST_MEMBER,
|
||||
item.id
|
||||
)
|
||||
}
|
||||
.addAction(getString(R.string.rename)) {
|
||||
listRename(accessInfo, item)
|
||||
}
|
||||
.addAction(getString(R.string.delete)) {
|
||||
listDelete(accessInfo, item)
|
||||
}
|
||||
.show(this, item.title)
|
||||
}
|
||||
|
||||
is MisskeyAntenna -> {
|
||||
// XXX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interface ListOnCreatedCallback {
|
||||
fun onCreated(list: TootList)
|
||||
}
|
||||
|
|
|
@ -12,10 +12,6 @@ import jp.juggler.util.*
|
|||
import okhttp3.Request
|
||||
import java.util.regex.Pattern
|
||||
|
||||
fun interface ListOnListMemberUpdatedCallback {
|
||||
fun onListMemberUpdated(willRegistered: Boolean, bSuccess: Boolean)
|
||||
}
|
||||
|
||||
private val reFollowError422 = "follow".asciiPattern(Pattern.CASE_INSENSITIVE)
|
||||
private val reFollowError404 = "Record not found".asciiPattern(Pattern.CASE_INSENSITIVE)
|
||||
|
||||
|
@ -24,7 +20,7 @@ fun ActMain.listMemberAdd(
|
|||
listId: EntityId,
|
||||
localWho: TootAccount,
|
||||
bFollow: Boolean = false,
|
||||
callback: ListOnListMemberUpdatedCallback?,
|
||||
onListMemberUpdated: (willRegistered: Boolean, bSuccess: Boolean) -> Unit = { _, _ -> },
|
||||
) {
|
||||
launchMain {
|
||||
runApiTask(accessInfo) { client ->
|
||||
|
@ -143,7 +139,7 @@ fun ActMain.listMemberAdd(
|
|||
listId,
|
||||
localWho,
|
||||
bFollow = true,
|
||||
callback = callback
|
||||
onListMemberUpdated = onListMemberUpdated
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
@ -158,7 +154,7 @@ fun ActMain.listMemberAdd(
|
|||
else -> showToast(true, error)
|
||||
}
|
||||
} finally {
|
||||
callback?.onListMemberUpdated(true, bSuccess)
|
||||
onListMemberUpdated(true, bSuccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +164,7 @@ fun ActMain.listMemberDelete(
|
|||
accessInfo: SavedAccount,
|
||||
listId: EntityId,
|
||||
localWho: TootAccount,
|
||||
callback: ListOnListMemberUpdatedCallback?,
|
||||
onListMemberDeleted: (willRegistered: Boolean, bSuccess: Boolean) -> Unit = { _, _ -> },
|
||||
) {
|
||||
launchMain {
|
||||
runApiTask(accessInfo) { client ->
|
||||
|
@ -208,7 +204,7 @@ fun ActMain.listMemberDelete(
|
|||
}
|
||||
}
|
||||
} finally {
|
||||
callback?.onListMemberUpdated(false, bSuccess)
|
||||
onListMemberDeleted(false, bSuccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,8 +68,8 @@ fun ActMain.openActPostImpl(
|
|||
scheduledStatus: TootScheduled? = null,
|
||||
) {
|
||||
|
||||
val useManyWindow = Pref.bpManyWindowPost(pref)
|
||||
val useMultiWindow = useManyWindow || Pref.bpMultiWindowPost(pref)
|
||||
val useManyWindow = PrefB.bpManyWindowPost(pref)
|
||||
val useMultiWindow = useManyWindow || PrefB.bpMultiWindowPost(pref)
|
||||
|
||||
val intent = ActPost.createIntent(
|
||||
activity = this,
|
||||
|
@ -246,7 +246,7 @@ fun ActMain.quoteFromAnotherAccount(
|
|||
fun ActMain.quoteName(who: TootAccount) {
|
||||
var sv = who.display_name
|
||||
try {
|
||||
val fmt = Pref.spQuoteNameFormat(pref)
|
||||
val fmt = PrefS.spQuoteNameFormat(pref)
|
||||
if (fmt.contains("%1\$s")) {
|
||||
sv = String.format(Locale.getDefault(), fmt, sv)
|
||||
}
|
||||
|
@ -263,3 +263,19 @@ fun ActMain.shareText(text: String?) {
|
|||
.setType("text/plain")
|
||||
.startChooser()
|
||||
}
|
||||
|
||||
fun ActMain.clickReply(accessInfo: SavedAccount, status: TootStatus) {
|
||||
if (!accessInfo.isPseudo) {
|
||||
reply(accessInfo, status)
|
||||
} else {
|
||||
replyFromAnotherAccount(accessInfo, status)
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.clickQuote(accessInfo: SavedAccount, status: TootStatus) {
|
||||
if (!accessInfo.isPseudo) {
|
||||
reply(accessInfo, status, quote = true)
|
||||
} else {
|
||||
quoteFromAnotherAccount(accessInfo, status)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -523,3 +523,20 @@ fun ActMain.reactionFromAnotherAccount(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.clickReaction(accessInfo: SavedAccount, column: Column, status: TootStatus) {
|
||||
val canMultipleReaction = InstanceCapability.canMultipleReaction(accessInfo)
|
||||
val hasMyReaction = status.reactionSet?.hasMyReaction() == true
|
||||
val bRemoveButton = hasMyReaction && !canMultipleReaction
|
||||
when {
|
||||
!TootReaction.canReaction(accessInfo) ->
|
||||
reactionFromAnotherAccount(
|
||||
accessInfo,
|
||||
status
|
||||
)
|
||||
bRemoveButton ->
|
||||
reactionRemove(column, status)
|
||||
else ->
|
||||
reactionAdd(column, status)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,7 @@ package jp.juggler.subwaytooter.action
|
|||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import jp.juggler.subwaytooter.*
|
||||
import jp.juggler.subwaytooter.api.entity.Host
|
||||
import jp.juggler.subwaytooter.api.entity.InstanceType
|
||||
import jp.juggler.subwaytooter.api.entity.TootAccount
|
||||
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.api.runApiTask
|
||||
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
|
@ -111,6 +108,18 @@ fun ActMain.serverInformation(
|
|||
host
|
||||
)
|
||||
|
||||
// ドメインブロック一覧から解除
|
||||
fun ActMain.clickDomainBlock(accessInfo: SavedAccount, item: TootDomainBlock) {
|
||||
AlertDialog.Builder(this)
|
||||
.setMessage(getString(R.string.confirm_unblock_domain, item.domain.pretty))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
domainBlock(accessInfo, item.domain, bBlock = false)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
// ContextMenuからドメインブロックを追加
|
||||
fun ActMain.clickDomainBlock(
|
||||
accessInfo: SavedAccount,
|
||||
who: TootAccount,
|
||||
|
|
|
@ -5,69 +5,15 @@ import androidx.appcompat.app.AlertDialog
|
|||
import jp.juggler.subwaytooter.*
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
||||
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.emptyCallback
|
||||
import jp.juggler.util.*
|
||||
import okhttp3.Request
|
||||
import java.util.*
|
||||
import kotlin.math.max
|
||||
|
||||
fun ActMain.clickBoostWithVisibility(
|
||||
accessInfo: SavedAccount,
|
||||
status: TootStatus?,
|
||||
) {
|
||||
status ?: return
|
||||
val list = if (accessInfo.isMisskey) {
|
||||
arrayOf(
|
||||
TootVisibility.Public,
|
||||
TootVisibility.UnlistedHome,
|
||||
TootVisibility.PrivateFollowers,
|
||||
TootVisibility.LocalPublic,
|
||||
TootVisibility.LocalHome,
|
||||
TootVisibility.LocalFollowers,
|
||||
TootVisibility.DirectSpecified,
|
||||
TootVisibility.DirectPrivate
|
||||
)
|
||||
} else {
|
||||
arrayOf(
|
||||
TootVisibility.Public,
|
||||
TootVisibility.UnlistedHome,
|
||||
TootVisibility.PrivateFollowers
|
||||
)
|
||||
}
|
||||
val captionList = list
|
||||
.map { Styler.getVisibilityCaption(this, accessInfo.isMisskey, it) }
|
||||
.toTypedArray()
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.choose_visibility)
|
||||
.setItems(captionList) { _, which ->
|
||||
if (which in list.indices) {
|
||||
boost(
|
||||
accessInfo,
|
||||
status,
|
||||
accessInfo.getFullAcct(status.account),
|
||||
CrossAccountMode.SameAccount,
|
||||
visibility = list[which],
|
||||
callback = boostCompleteCallback,
|
||||
)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
fun ActMain.clickBoostBy(
|
||||
pos: Int,
|
||||
accessInfo: SavedAccount,
|
||||
status: TootStatus?,
|
||||
columnType: ColumnType = ColumnType.BOOSTED_BY,
|
||||
) {
|
||||
status ?: return
|
||||
addColumn(false, pos, accessInfo, columnType, status.id)
|
||||
}
|
||||
|
||||
fun ActMain.clickStatusDelete(
|
||||
accessInfo: SavedAccount,
|
||||
|
@ -81,6 +27,64 @@ fun ActMain.clickStatusDelete(
|
|||
.show()
|
||||
}
|
||||
|
||||
fun ActMain.clickBookmark(accessInfo: SavedAccount, status: TootStatus, willToast: Boolean) {
|
||||
if (accessInfo.isPseudo) {
|
||||
bookmarkFromAnotherAccount(accessInfo, status)
|
||||
} else {
|
||||
// トグル動作
|
||||
val bSet = !status.bookmarked
|
||||
|
||||
bookmark(
|
||||
accessInfo,
|
||||
status,
|
||||
CrossAccountMode.SameAccount,
|
||||
bSet = bSet,
|
||||
callback = when {
|
||||
!willToast -> emptyCallback
|
||||
// 簡略表示なら結果をトースト表示
|
||||
bSet -> bookmarkCompleteCallback
|
||||
else -> unbookmarkCompleteCallback
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.clickFavourite(accessInfo: SavedAccount, status: TootStatus, willToast: Boolean) {
|
||||
if (accessInfo.isPseudo) {
|
||||
favouriteFromAnotherAccount(accessInfo, status)
|
||||
} else {
|
||||
// トグル動作
|
||||
val bSet = !status.favourited
|
||||
|
||||
favourite(
|
||||
accessInfo,
|
||||
status,
|
||||
CrossAccountMode.SameAccount,
|
||||
bSet = bSet,
|
||||
callback = when {
|
||||
!willToast -> emptyCallback
|
||||
// 簡略表示なら結果をトースト表示
|
||||
bSet -> favouriteCompleteCallback
|
||||
else -> unfavouriteCompleteCallback
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.clickScheduledToot(accessInfo: SavedAccount, item: TootScheduled, column: Column) {
|
||||
ActionsDialog()
|
||||
.addAction(getString(R.string.edit)) {
|
||||
scheduledPostEdit(accessInfo, item)
|
||||
}
|
||||
.addAction(getString(R.string.delete)) {
|
||||
scheduledPostDelete(accessInfo, item) {
|
||||
column.onScheduleDeleted(item)
|
||||
showToast(false, R.string.scheduled_post_deleted)
|
||||
}
|
||||
}
|
||||
.show(this)
|
||||
}
|
||||
|
||||
fun ActMain.launchActText(intent: Intent) = arActText.launch(intent)
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
@ -394,304 +398,6 @@ fun ActMain.bookmarkFromAnotherAccount(
|
|||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
fun ActMain.boost(
|
||||
accessInfo: SavedAccount,
|
||||
statusArg: TootStatus,
|
||||
statusOwner: Acct,
|
||||
crossAccountMode: CrossAccountMode,
|
||||
bSet: Boolean = true,
|
||||
bConfirmed: Boolean = false,
|
||||
visibility: TootVisibility? = null,
|
||||
callback: () -> Unit,
|
||||
) {
|
||||
|
||||
// アカウントからステータスにブースト操作を行っているなら、何もしない
|
||||
if (appState.isBusyBoost(accessInfo, statusArg)) {
|
||||
showToast(false, R.string.wait_previous_operation)
|
||||
return
|
||||
}
|
||||
|
||||
// Mastodonは非公開トゥートをブーストできるのは本人だけ
|
||||
val isPrivateToot = accessInfo.isMastodon &&
|
||||
statusArg.visibility == TootVisibility.PrivateFollowers
|
||||
|
||||
if (isPrivateToot && accessInfo.acct != statusOwner) {
|
||||
showToast(false, R.string.boost_private_toot_not_allowed)
|
||||
return
|
||||
}
|
||||
// DMとかのブーストはAPI側がエラーを出すだろう?
|
||||
|
||||
// 必要なら確認を出す
|
||||
if (!bConfirmed) {
|
||||
DlgConfirm.open(
|
||||
this,
|
||||
getString(
|
||||
when {
|
||||
!bSet -> R.string.confirm_unboost_from
|
||||
isPrivateToot -> R.string.confirm_boost_private_from
|
||||
visibility == TootVisibility.PrivateFollowers -> R.string.confirm_private_boost_from
|
||||
else -> R.string.confirm_boost_from
|
||||
},
|
||||
AcctColor.getNickname(accessInfo)
|
||||
),
|
||||
object : DlgConfirm.Callback {
|
||||
override fun onOK() {
|
||||
boost(
|
||||
|
||||
accessInfo,
|
||||
statusArg,
|
||||
statusOwner,
|
||||
crossAccountMode,
|
||||
bSet = bSet,
|
||||
bConfirmed = true,
|
||||
visibility = visibility,
|
||||
callback = callback,
|
||||
)
|
||||
}
|
||||
|
||||
override var isConfirmEnabled: Boolean
|
||||
get() = when (bSet) {
|
||||
true -> accessInfo.confirm_boost
|
||||
else -> accessInfo.confirm_unboost
|
||||
}
|
||||
set(value) {
|
||||
when (bSet) {
|
||||
true -> accessInfo.confirm_boost = value
|
||||
else -> accessInfo.confirm_unboost = value
|
||||
}
|
||||
accessInfo.saveSetting()
|
||||
reloadAccountSetting(accessInfo)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
appState.setBusyBoost(accessInfo, statusArg)
|
||||
// ブースト表示を更新中にする
|
||||
showColumnMatchAccount(accessInfo)
|
||||
// misskeyは非公開トゥートをブーストできないっぽい
|
||||
|
||||
launchMain {
|
||||
var resultStatus: TootStatus? = null
|
||||
var resultUnrenoteId: EntityId? = null
|
||||
val result = runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
||||
|
||||
val parser = TootParser(this, accessInfo)
|
||||
|
||||
val targetStatus = if (crossAccountMode.isRemote) {
|
||||
val (result, status) = client.syncStatus(accessInfo, statusArg)
|
||||
if (status == null) return@runApiTask result
|
||||
if (status.reblogged) {
|
||||
return@runApiTask TootApiResult(getString(R.string.already_boosted))
|
||||
}
|
||||
status
|
||||
} else {
|
||||
// 既に自タンスのステータスがある
|
||||
statusArg
|
||||
}
|
||||
|
||||
if (accessInfo.isMisskey) {
|
||||
if (!bSet) {
|
||||
val myRenoteId = targetStatus.myRenoteId
|
||||
?: return@runApiTask TootApiResult("missing renote id.")
|
||||
|
||||
client.request(
|
||||
"/api/notes/delete",
|
||||
accessInfo.putMisskeyApiToken().apply {
|
||||
put("noteId", myRenoteId.toString())
|
||||
put("renoteId", targetStatus.id.toString())
|
||||
}
|
||||
.toPostRequestBuilder()
|
||||
)
|
||||
?.also {
|
||||
if (it.response?.code == 204) {
|
||||
resultUnrenoteId = myRenoteId
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client.request(
|
||||
"/api/notes/create",
|
||||
accessInfo.putMisskeyApiToken().apply {
|
||||
put("renoteId", targetStatus.id.toString())
|
||||
}
|
||||
.toPostRequestBuilder()
|
||||
)
|
||||
?.also { result ->
|
||||
val jsonObject = result.jsonObject
|
||||
if (jsonObject != null) {
|
||||
val outerStatus = parser.status(
|
||||
jsonObject.jsonObject("createdNote")
|
||||
?: jsonObject
|
||||
)
|
||||
val innerStatus = outerStatus?.reblog ?: outerStatus
|
||||
if (outerStatus != null && innerStatus != null && outerStatus != innerStatus) {
|
||||
innerStatus.myRenoteId = outerStatus.id
|
||||
innerStatus.reblogged = true
|
||||
}
|
||||
// renoteそのものではなくrenoteされた元noteが欲しい
|
||||
resultStatus = innerStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val b = JsonObject().apply {
|
||||
if (visibility != null) put("visibility", visibility.strMastodon)
|
||||
}.toPostRequestBuilder()
|
||||
|
||||
client.request(
|
||||
"/api/v1/statuses/${targetStatus.id}/${if (bSet) "reblog" else "unreblog"}",
|
||||
b
|
||||
)?.also { result ->
|
||||
// reblogはreblogを表すStatusを返す
|
||||
// unreblogはreblogしたStatusを返す
|
||||
val s = parser.status(result.jsonObject)
|
||||
resultStatus = s?.reblog ?: s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appState.resetBusyBoost(accessInfo, statusArg)
|
||||
|
||||
if (result != null) {
|
||||
val unrenoteId = resultUnrenoteId
|
||||
val newStatus = resultStatus
|
||||
when {
|
||||
// Misskeyでunrenoteに成功した
|
||||
unrenoteId != null -> {
|
||||
|
||||
// 星を外したのにカウントが下がらないのは違和感あるので、表示をいじる
|
||||
// 0未満にはならない
|
||||
val count = max(0, (statusArg.reblogs_count ?: 1) - 1)
|
||||
|
||||
for (column in appState.columnList) {
|
||||
column.findStatus(
|
||||
accessInfo.apDomain,
|
||||
statusArg.id
|
||||
) { account, status ->
|
||||
|
||||
// 同タンス別アカウントでもカウントは変化する
|
||||
status.reblogs_count = count
|
||||
|
||||
// 同アカウントならreblogged状態を変化させる
|
||||
if (accessInfo == account && status.myRenoteId == unrenoteId) {
|
||||
status.myRenoteId = null
|
||||
status.reblogged = false
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
// 処理に成功した
|
||||
newStatus != null -> {
|
||||
// カウント数は遅延があるみたいなので、恣意的に表示を変更する
|
||||
// ブーストカウント数を加工する
|
||||
val oldCount = statusArg.reblogs_count
|
||||
val newCount = newStatus.reblogs_count
|
||||
if (oldCount != null && newCount != null) {
|
||||
if (bSet && newStatus.reblogged && newCount <= oldCount) {
|
||||
// 星をつけたのにカウントが上がらないのは違和感あるので、表示をいじる
|
||||
newStatus.reblogs_count = oldCount + 1
|
||||
} else if (!bSet && !newStatus.reblogged && newCount >= oldCount) {
|
||||
// 星を外したのにカウントが下がらないのは違和感あるので、表示をいじる
|
||||
// 0未満にはならない
|
||||
newStatus.reblogs_count = if (oldCount < 1) 0 else oldCount - 1
|
||||
}
|
||||
}
|
||||
|
||||
for (column in appState.columnList) {
|
||||
column.findStatus(
|
||||
accessInfo.apDomain,
|
||||
newStatus.id
|
||||
) { account, status ->
|
||||
|
||||
// 同タンス別アカウントでもカウントは変化する
|
||||
status.reblogs_count = newStatus.reblogs_count
|
||||
|
||||
if (accessInfo == account) {
|
||||
|
||||
// 同アカウントならreblog状態を変化させる
|
||||
when {
|
||||
accessInfo.isMastodon ->
|
||||
status.reblogged = newStatus.reblogged
|
||||
|
||||
bSet && status.myRenoteId == null -> {
|
||||
status.myRenoteId = newStatus.myRenoteId
|
||||
status.reblogged = true
|
||||
}
|
||||
}
|
||||
// Misskey のunrenote時はここを通らない
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
else -> showToast(true, result.error)
|
||||
}
|
||||
}
|
||||
|
||||
// 結果に関わらず、更新中状態から復帰させる
|
||||
showColumnMatchAccount(accessInfo)
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.boostFromAnotherAccount(
|
||||
timelineAccount: SavedAccount,
|
||||
status: TootStatus?,
|
||||
) {
|
||||
status ?: return
|
||||
launchMain {
|
||||
val statusOwner = timelineAccount.getFullAcct(status.account)
|
||||
|
||||
val isPrivateToot = timelineAccount.isMastodon &&
|
||||
status.visibility == TootVisibility.PrivateFollowers
|
||||
|
||||
if (isPrivateToot) {
|
||||
val list = ArrayList<SavedAccount>()
|
||||
for (a in SavedAccount.loadAccountList(applicationContext)) {
|
||||
if (a.acct == statusOwner) list.add(a)
|
||||
}
|
||||
if (list.isEmpty()) {
|
||||
showToast(false, R.string.boost_private_toot_not_allowed)
|
||||
return@launchMain
|
||||
}
|
||||
|
||||
pickAccount(
|
||||
bAllowPseudo = false,
|
||||
bAuto = false,
|
||||
message = getString(R.string.account_picker_boost),
|
||||
accountListArg = list
|
||||
)?.let { action_account ->
|
||||
boost(
|
||||
action_account,
|
||||
status,
|
||||
statusOwner,
|
||||
calcCrossAccountMode(timelineAccount, action_account),
|
||||
callback = boostCompleteCallback
|
||||
)
|
||||
}
|
||||
} else {
|
||||
pickAccount(
|
||||
bAllowPseudo = false,
|
||||
bAuto = false,
|
||||
message = getString(R.string.account_picker_boost),
|
||||
accountListArg = accountListNonPseudo(timelineAccount.apDomain)
|
||||
)?.let { action_account ->
|
||||
boost(
|
||||
action_account,
|
||||
status,
|
||||
statusOwner,
|
||||
calcCrossAccountMode(timelineAccount, action_account),
|
||||
callback = boostCompleteCallback
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ActMain.statusDelete(
|
||||
accessInfo: SavedAccount,
|
||||
statusId: EntityId,
|
||||
|
|
|
@ -5,6 +5,7 @@ import jp.juggler.subwaytooter.ColumnType
|
|||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.Acct
|
||||
import jp.juggler.subwaytooter.api.entity.Host
|
||||
import jp.juggler.subwaytooter.api.entity.TootTag
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
|
@ -14,6 +15,15 @@ import jp.juggler.util.encodePercent
|
|||
import jp.juggler.util.launchMain
|
||||
import java.util.*
|
||||
|
||||
fun ActMain.longClickTootTag(pos: Int, accessInfo: SavedAccount, item: TootTag) {
|
||||
tagTimelineFromAccount(
|
||||
pos = pos,
|
||||
url = "https://${accessInfo.apiHost.ascii}/tags/${item.name.encodePercent()}",
|
||||
host = accessInfo.apiHost,
|
||||
tagWithoutSharp = item.name
|
||||
)
|
||||
}
|
||||
|
||||
// ハッシュタグへの操作を選択する
|
||||
fun ActMain.tagDialog(
|
||||
pos: Int,
|
||||
|
|
|
@ -5,7 +5,7 @@ import android.text.Spannable
|
|||
import android.text.SpannableStringBuilder
|
||||
import android.widget.TextView
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.MisskeyAccountDetailMap
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
|
@ -491,7 +491,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
|||
.append(suggestionSource)
|
||||
}
|
||||
|
||||
if (Pref.bpDirectoryLastActive(pref) && last_status_at > 0L) {
|
||||
if (PrefB.bpDirectoryLastActive(pref) && last_status_at > 0L) {
|
||||
prepareSb()
|
||||
.append(context.getString(R.string.last_active))
|
||||
.append(delm)
|
||||
|
@ -499,7 +499,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
|||
}
|
||||
|
||||
if (!fromProfileHeader) {
|
||||
if (Pref.bpDirectoryTootCount(pref) &&
|
||||
if (PrefB.bpDirectoryTootCount(pref) &&
|
||||
(statuses_count ?: 0L) > 0L
|
||||
) {
|
||||
prepareSb()
|
||||
|
@ -508,8 +508,8 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
|||
.append(statuses_count.toString())
|
||||
}
|
||||
|
||||
if (Pref.bpDirectoryFollowers(pref) &&
|
||||
!Pref.bpHideFollowCount(pref) &&
|
||||
if (PrefB.bpDirectoryFollowers(pref) &&
|
||||
!PrefB.bpHideFollowCount(pref) &&
|
||||
(followers_count ?: 0L) > 0L
|
||||
) {
|
||||
prepareSb()
|
||||
|
@ -518,7 +518,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
|||
.append(followers_count.toString())
|
||||
}
|
||||
|
||||
if (Pref.bpDirectoryNote(pref) && note?.isNotEmpty() == true) {
|
||||
if (PrefB.bpDirectoryNote(pref) && note?.isNotEmpty() == true) {
|
||||
val decodedNote = DecodeOptions(
|
||||
context,
|
||||
accessInfo,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package jp.juggler.subwaytooter.api.entity
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.util.*
|
||||
|
||||
|
@ -195,7 +195,7 @@ class TootAttachment : TootAttachmentLike {
|
|||
TootAttachmentType.values().find { it.id == src }
|
||||
|
||||
override fun urlForThumbnail(pref: SharedPreferences) =
|
||||
if (Pref.bpPriorLocalURL(pref)) {
|
||||
if (PrefB.bpPriorLocalURL(pref)) {
|
||||
preview_url.notEmpty() ?: preview_remote_url.notEmpty()
|
||||
} else {
|
||||
preview_remote_url.notEmpty() ?: preview_url.notEmpty()
|
||||
|
@ -205,7 +205,7 @@ class TootAttachment : TootAttachmentLike {
|
|||
}
|
||||
|
||||
fun getLargeUrl(pref: SharedPreferences) =
|
||||
if (Pref.bpPriorLocalURL(pref)) {
|
||||
if (PrefB.bpPriorLocalURL(pref)) {
|
||||
url.notEmpty() ?: remote_url
|
||||
} else {
|
||||
remote_url.notEmpty() ?: url
|
||||
|
@ -213,7 +213,7 @@ class TootAttachment : TootAttachmentLike {
|
|||
|
||||
fun getLargeUrlList(pref: SharedPreferences) =
|
||||
ArrayList<String>().apply {
|
||||
if (Pref.bpPriorLocalURL(pref)) {
|
||||
if (PrefB.bpPriorLocalURL(pref)) {
|
||||
url.notEmpty()?.addTo(this)
|
||||
remote_url.notEmpty()?.addTo(this)
|
||||
} else {
|
||||
|
|
|
@ -2,7 +2,7 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import android.os.SystemClock
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
import jp.juggler.subwaytooter.api.TootApiResult
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
|
@ -399,7 +399,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
|
|||
|
||||
when {
|
||||
pair.first?.instanceType == InstanceType.Pixelfed &&
|
||||
!Pref.bpEnablePixelfed(App1.pref) &&
|
||||
!PrefB.bpEnablePixelfed(App1.pref) &&
|
||||
!req.allowPixelfed ->
|
||||
Pair(
|
||||
null, TootApiResult("currently Pixelfed instance is not supported.")
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.text.Spannable
|
|||
import android.text.SpannableStringBuilder
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.ColumnViewHolder
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
|
@ -165,7 +165,7 @@ class TootReaction(
|
|||
}
|
||||
|
||||
fun chooseUrl() = when {
|
||||
Pref.bpDisableEmojiAnimation(App1.pref) -> staticUrl
|
||||
PrefB.bpDisableEmojiAnimation(App1.pref) -> staticUrl
|
||||
else -> url
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import androidx.annotation.StringRes
|
|||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||
|
@ -498,7 +498,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
TootCard(parser, quote)
|
||||
// content中のQTの表現が四角括弧の有無とか色々あるみたいだし
|
||||
// 選択してコピーのことを考えたらむしろ削らない方が良い気がしてきた
|
||||
// removeQt = ! Pref.bpDontShowPreviewCard(Pref.pref(parser.context))
|
||||
// removeQt = ! PrefB.bpDontShowPreviewCard(Pref.pref(parser.context))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -712,7 +712,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
card = TootCard(parser, quote)
|
||||
// content中のQTの表現が四角括弧の有無とか色々あるみたいだし
|
||||
// 選択してコピーのことを考えたらむしろ削らない方が良い気がしてきた
|
||||
// removeQt = ! Pref.bpDontShowPreviewCard(Pref.pref(parser.context))
|
||||
// removeQt = ! PrefB.bpDontShowPreviewCard(Pref.pref(parser.context))
|
||||
}
|
||||
|
||||
// content
|
||||
|
@ -1050,7 +1050,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
|
||||
fun markDeleted(context: Context, deletedAt: Long?): Boolean {
|
||||
|
||||
if (Pref.bpDontRemoveDeletedToot(App1.getAppState(context).pref)) return false
|
||||
if (PrefB.bpDontRemoveDeletedToot(App1.getAppState(context).pref)) return false
|
||||
|
||||
var sv = if (deletedAt != null) {
|
||||
context.getString(R.string.status_deleted_at, formatTime(context, deletedAt, false))
|
||||
|
@ -1281,7 +1281,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
formatDate(t, date_format2, omitZeroSecond = false, omitYear = true)
|
||||
}
|
||||
|
||||
if (bAllowRelative && Pref.bpRelativeTimestamp(App1.pref)) {
|
||||
if (bAllowRelative && PrefB.bpRelativeTimestamp(App1.pref)) {
|
||||
|
||||
delta = abs(delta)
|
||||
|
||||
|
|
|
@ -326,7 +326,7 @@ class DlgListMember(
|
|||
}
|
||||
|
||||
@Suppress("ClassNaming")
|
||||
internal inner class VH_List(view: View) : CompoundButton.OnCheckedChangeListener, ListOnListMemberUpdatedCallback {
|
||||
internal inner class VH_List(view: View) : CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
private val cbItem: CheckBox
|
||||
private var bBusy: Boolean = false
|
||||
|
@ -368,16 +368,16 @@ class DlgListMember(
|
|||
// 状態をサーバに伝える
|
||||
val item = this.item ?: return
|
||||
if (isChecked) {
|
||||
activity.listMemberAdd(listOwner, item.id, whoLocal, callback = this)
|
||||
activity.listMemberAdd(listOwner, item.id, whoLocal) { willRegistered, bSuccess ->
|
||||
if (!bSuccess) revokeCheckedChanged(willRegistered)
|
||||
}
|
||||
} else {
|
||||
activity.listMemberDelete(listOwner, item.id, whoLocal, this)
|
||||
activity.listMemberDelete(listOwner, item.id, whoLocal) { willRegistered, bSuccess ->
|
||||
if (!bSuccess) revokeCheckedChanged(willRegistered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onListMemberUpdated(willRegistered: Boolean, bSuccess: Boolean) {
|
||||
if (!bSuccess) revokeCheckedChanged(willRegistered)
|
||||
}
|
||||
|
||||
private fun revokeCheckedChanged(willRegistered: Boolean) {
|
||||
item?.isRegistered = !willRegistered
|
||||
adapter.notifyDataSetChanged()
|
||||
|
|
|
@ -120,12 +120,12 @@ class DlgQuickTootMenu(
|
|||
}
|
||||
|
||||
private fun loadStrings() =
|
||||
Pref.spQuickTootMacro(activity.pref).split("\n")
|
||||
PrefS.spQuickTootMacro(activity.pref).split("\n")
|
||||
|
||||
private fun saveStrings() = activity.pref
|
||||
.edit()
|
||||
.put(
|
||||
Pref.spQuickTootMacro,
|
||||
PrefS.spQuickTootMacro,
|
||||
etText.joinToString("\n") {
|
||||
(it?.text?.toString() ?: "").replace("\n", " ")
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ class EmojiPicker(
|
|||
|
||||
// recentをロードする
|
||||
val pref = App1.pref
|
||||
val sv = Pref.spEmojiPickerRecent(pref)
|
||||
val sv = PrefS.spEmojiPickerRecent(pref)
|
||||
if (sv.isNotEmpty()) {
|
||||
try {
|
||||
for (item in sv.decodeJsonArray().objectList()) {
|
||||
|
@ -188,7 +188,7 @@ class EmojiPicker(
|
|||
R.string.emoji_category_flags
|
||||
)
|
||||
)
|
||||
if (Pref.bpEmojiPickerCategoryOther(activity)) {
|
||||
if (PrefB.bpEmojiPickerCategoryOther(activity)) {
|
||||
pageList.add(
|
||||
EmojiPickerPage(
|
||||
true,
|
||||
|
@ -528,7 +528,7 @@ class EmojiPicker(
|
|||
|
||||
// Recentをロード(他インスタンスの絵文字を含む)
|
||||
val list: MutableList<JsonObject> = try {
|
||||
Pref.spEmojiPickerRecent(pref).decodeJsonArray().objectList()
|
||||
PrefS.spEmojiPickerRecent(pref).decodeJsonArray().objectList()
|
||||
} catch (_: Throwable) {
|
||||
emptyList()
|
||||
}.toMutableList()
|
||||
|
@ -571,7 +571,7 @@ class EmojiPicker(
|
|||
// 保存する
|
||||
try {
|
||||
val sv = list.toJsonArray().toString()
|
||||
App1.pref.edit().put(Pref.spEmojiPickerRecent, sv).apply()
|
||||
App1.pref.edit().put(PrefS.spEmojiPickerRecent, sv).apply()
|
||||
} catch (ignored: Throwable) {
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package jp.juggler.subwaytooter.emoji
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.api.entity.Host
|
||||
import jp.juggler.subwaytooter.api.entity.Mappable
|
||||
import jp.juggler.util.JsonArray
|
||||
|
@ -74,7 +74,7 @@ class CustomEmoji(
|
|||
get() = shortcode
|
||||
|
||||
fun chooseUrl() = when {
|
||||
Pref.bpDisableEmojiAnimation(App1.pref) -> staticUrl
|
||||
PrefB.bpDisableEmojiAnimation(App1.pref) -> staticUrl
|
||||
else -> url
|
||||
}
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ class PollingWorker private constructor(contextArg: Context) {
|
|||
|
||||
val intervalMillis = max(
|
||||
minute * 5L,
|
||||
minute * Pref.spPullNotificationCheckInterval.toInt(context.pref())
|
||||
minute * PrefS.spPullNotificationCheckInterval.toInt(context.pref())
|
||||
)
|
||||
|
||||
val flexMillis = max(
|
||||
|
|
|
@ -12,13 +12,13 @@ import jp.juggler.subwaytooter.table.SavedAccount
|
|||
import jp.juggler.subwaytooter.table.SubscriptionServerKey
|
||||
import jp.juggler.util.*
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
||||
class PushSubscriptionHelper(
|
||||
val context: Context,
|
||||
val account: SavedAccount,
|
||||
val verbose: Boolean = false,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
private val log = LogCategory("PushSubscriptionHelper")
|
||||
|
@ -36,6 +36,12 @@ class PushSubscriptionHelper(
|
|||
|
||||
private fun Boolean.booleanToInt(trueValue: Int, falseValue: Int = 0) =
|
||||
if (this) trueValue else falseValue
|
||||
|
||||
private fun Response.closeQuietly() =
|
||||
try {
|
||||
close()
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
val flags = account.notification_boost.booleanToInt(1) +
|
||||
|
@ -153,6 +159,46 @@ class PushSubscriptionHelper(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun updateSubscription(client: TootApiClient, force: Boolean = false): TootApiResult? = try {
|
||||
when {
|
||||
isRecentlyChecked() ->
|
||||
TootApiResult(ERROR_PREVENT_FREQUENTLY_CHECK)
|
||||
|
||||
account.isPseudo ->
|
||||
TootApiResult(context.getString(R.string.pseudo_account_not_supported))
|
||||
|
||||
account.isMisskey ->
|
||||
updateSubscriptionMisskey(client)
|
||||
|
||||
else ->
|
||||
updateSubscriptionMastodon(client, force)
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
TootApiResult(ex.withCaption("error."))
|
||||
}?.apply {
|
||||
|
||||
if (error != null) addLog("$error $requestInfo".trimEnd())
|
||||
|
||||
// update error text on account table
|
||||
val log = logString
|
||||
when {
|
||||
|
||||
log.contains(ERROR_PREVENT_FREQUENTLY_CHECK) -> {
|
||||
// don't update if check was skipped.
|
||||
}
|
||||
|
||||
subscribed || log.isEmpty() ->
|
||||
// clear error text if succeeded or no error log
|
||||
if (account.last_subscription_error != null) {
|
||||
account.updateSubscriptionError(null)
|
||||
}
|
||||
|
||||
else ->
|
||||
// record error text
|
||||
account.updateSubscriptionError(log)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateSubscriptionMisskey(client: TootApiClient): TootApiResult? {
|
||||
|
||||
// 現在の購読状態を取得できないので、毎回購読の更新を行う
|
||||
|
@ -231,88 +277,33 @@ class PushSubscriptionHelper(
|
|||
// https://github.com/tootsuite/mastodon/pull/7471
|
||||
// https://github.com/tootsuite/mastodon/pull/7472
|
||||
|
||||
var r = client.request("/api/v1/push/subscription")
|
||||
var res = r?.response ?: return r // cancelled or missing response
|
||||
var subscription404 = false
|
||||
|
||||
if (res.code != 200) log.i("${account.acct}: check existing subscription: code=${res.code}")
|
||||
|
||||
when (res.code) {
|
||||
200 -> {
|
||||
if (r.error?.isNotEmpty() == true && r.jsonObject == null) {
|
||||
// Pleromaが200応答でもエラーHTMLを返す場合がある
|
||||
addLog(context.getString(R.string.instance_does_not_support_push_api_pleroma))
|
||||
return r
|
||||
}
|
||||
// たぶん購読が存在する
|
||||
}
|
||||
|
||||
404 -> {
|
||||
subscription404 = true
|
||||
// この時点では存在しないのが購読なのかAPIなのか分からない
|
||||
}
|
||||
|
||||
403 -> {
|
||||
// アクセストークンにpushスコープがない
|
||||
if (flags != 0 || verbose) {
|
||||
addLog(context.getString(R.string.missing_push_scope))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
in 400 until 500 -> {
|
||||
addLog(context.getString(R.string.instance_does_not_support_push_api_pleroma))
|
||||
return r
|
||||
}
|
||||
|
||||
else -> {
|
||||
addLog("${res.request}")
|
||||
addLog("${res.code} ${res.message}")
|
||||
}
|
||||
val subscription404: Boolean
|
||||
val oldSubscription: TootPushSubscription?
|
||||
checkCurrentSubscription(client).let {
|
||||
if (it.failed) return it.result
|
||||
subscription404 = it.is404
|
||||
oldSubscription = parseItem(::TootPushSubscription, it.result?.jsonObject)
|
||||
}
|
||||
|
||||
val oldSubscription = parseItem(::TootPushSubscription, r.jsonObject)
|
||||
if (oldSubscription == null) {
|
||||
log.i("${account.acct}: oldSubscription is null")
|
||||
|
||||
val (ti, result) = TootInstance.get(client)
|
||||
ti ?: return result
|
||||
|
||||
// 2.4.0rc1 未満にはプッシュ購読APIはない
|
||||
if (!ti.versionGE(TootInstance.VERSION_2_4_0_rc1)) {
|
||||
return TootApiResult(
|
||||
context.getString(R.string.instance_does_not_support_push_api, ti.version)
|
||||
)
|
||||
}
|
||||
|
||||
if (subscription404 && flags == 0) {
|
||||
when {
|
||||
ti.versionGE(TootInstance.VERSION_2_4_0_rc2) -> {
|
||||
// 購読が不要で現在の状況が404だった場合
|
||||
// 2.4.0rc2以降では「購読が存在しない」を示すので何もしなくてよい
|
||||
if (verbose) addLog(context.getString(R.string.push_subscription_not_exists))
|
||||
return TootApiResult()
|
||||
}
|
||||
|
||||
else -> {
|
||||
// 2.4.0rc1では「APIが存在しない」と「購読が存在しない」を判別できない
|
||||
}
|
||||
}
|
||||
}
|
||||
checkInstanceVersionMastodon(ti, subscription404)?.let { return it }
|
||||
}
|
||||
|
||||
// FCMのデバイスIDを取得
|
||||
val deviceId = PollingWorker.getFirebaseMessagingToken(context)
|
||||
?: return TootApiResult(error = context.getString(R.string.missing_fcm_device_id))
|
||||
|
||||
// アクセストークン
|
||||
val accessToken = account.getAccessToken()
|
||||
?: return TootApiResult(error = "missing access token.")
|
||||
|
||||
// インストールIDを取得
|
||||
val installId = PollingWorker.prepareInstallId(context)
|
||||
?: return TootApiResult(error = context.getString(R.string.missing_install_id))
|
||||
|
||||
// アクセストークン
|
||||
val accessToken = account.getAccessToken()
|
||||
?: return TootApiResult(error = "missing access token.")
|
||||
|
||||
// アクセストークンのダイジェスト
|
||||
val tokenDigest = accessToken.digestSHA256Base64Url()
|
||||
|
||||
|
@ -333,86 +324,50 @@ class PushSubscriptionHelper(
|
|||
put("emoji_reaction", account.notification_reaction) // fedibird拡張
|
||||
}
|
||||
|
||||
suspend fun canSkipSubscription(): TootApiResult? {
|
||||
|
||||
// 購読の更新が強制されている
|
||||
if (force) return null
|
||||
|
||||
// 購読を解除したいのに古い購読があるのなら、購読の更新が必要
|
||||
if (flags == 0 && oldSubscription != null) return null
|
||||
|
||||
// endpoint URLが合わないなら購読の更新が必要
|
||||
if (force || oldSubscription?.endpoint != endpoint) return null
|
||||
|
||||
suspend fun makeSkipResult(): TootApiResult {
|
||||
// 既に登録済みで、endpointも一致している
|
||||
subscribed = true
|
||||
if (verbose) addLog(context.getString(R.string.push_subscription_already_exists))
|
||||
return updateServerKey(client, clientIdentifier, oldSubscription.server_key)
|
||||
}
|
||||
|
||||
// STがstatus通知に対応した時期に古いサーバでここを通ると
|
||||
// flagsの値が変わりendpoint URLも変わった状態で購読を自動更新してしまう
|
||||
// しかしそのタイミングではサーバは古いのでサーバ側の購読内容は変化しなかった。
|
||||
|
||||
// サーバ上の購読アラートのリスト
|
||||
var alertsOld = oldSubscription.alerts.entries
|
||||
.mapNotNull { if (it.value) it.key else null }
|
||||
.sorted()
|
||||
|
||||
// 期待する購読アラートのリスト
|
||||
var alertsNew = newAlerts.entries
|
||||
.mapNotNull { pair -> pair.key.takeIf { pair.value == true } }
|
||||
.sorted()
|
||||
|
||||
// 両方に共通するアラートは除去する
|
||||
val bothHave = alertsOld.filter { alertsNew.contains(it) }
|
||||
alertsOld = alertsOld.filter { !bothHave.contains(it) }
|
||||
alertsNew = alertsNew.filter { !bothHave.contains(it) }
|
||||
|
||||
// サーバのバージョンを調べる前に、この時点でalertsが一致するか確認する
|
||||
if (alertsOld.joinToString(",") == alertsNew.joinToString(",")) {
|
||||
log.i("${account.acct}: same alerts(1)")
|
||||
return makeSkipResult()
|
||||
}
|
||||
|
||||
// ここでサーバのバージョンによって対応が変わる
|
||||
val (ti, result) = TootInstance.get(client)
|
||||
ti ?: return result
|
||||
|
||||
// サーバが知らないアラート種別は比較対象から除去する
|
||||
fun Iterable<String>.knownOnly() = filter {
|
||||
when (it) {
|
||||
"follow", "mention", "favourite", "reblog" -> true
|
||||
"poll" -> ti.versionGE(TootInstance.VERSION_2_8_0_rc1)
|
||||
"follow_request" -> ti.versionGE(TootInstance.VERSION_3_1_0_rc1)
|
||||
"status" -> ti.versionGE(TootInstance.VERSION_3_3_0_rc1)
|
||||
"emoji_reaction" -> ti.versionGE(TootInstance.VERSION_3_4_0_rc1) &&
|
||||
InstanceCapability.emojiReaction(account, ti)
|
||||
|
||||
else -> {
|
||||
log.w("${account.acct}: unknown alert '$it'. server version='${ti.version}'")
|
||||
false // 未知のアラートの差異は比較しない
|
||||
}
|
||||
}
|
||||
}
|
||||
alertsOld = alertsOld.knownOnly()
|
||||
alertsNew = alertsNew.knownOnly()
|
||||
|
||||
return if (alertsOld.joinToString(",") == alertsNew.joinToString(",")) {
|
||||
log.i("${account.acct}: same alerts(2)")
|
||||
makeSkipResult()
|
||||
} else {
|
||||
addLog("${account.acct}: alerts not match. account=${account.acct.pretty} old=${alertsOld.sorted()}, new=${alertsNew.sorted()}")
|
||||
null
|
||||
}
|
||||
if (!force) {
|
||||
canSkipSubscriptionMastodon(
|
||||
client = client,
|
||||
clientIdentifier = clientIdentifier,
|
||||
endpoint = endpoint,
|
||||
oldSubscription = oldSubscription,
|
||||
newAlerts = newAlerts,
|
||||
)?.let { return it }
|
||||
}
|
||||
|
||||
val skipResult = canSkipSubscription()
|
||||
if (skipResult != null) return skipResult
|
||||
|
||||
// アクセストークンの優先権を取得
|
||||
r = client.http(
|
||||
checkDeviceHasPriority(
|
||||
client,
|
||||
tokenDigest = tokenDigest,
|
||||
installId = installId,
|
||||
).let {
|
||||
if (it.failed) return it.result
|
||||
}
|
||||
|
||||
return when (flags) {
|
||||
// 通知設定が全てカラなので、購読を取り消したい
|
||||
0 -> unsubscribeMastodon(client)
|
||||
|
||||
// 通知設定が空ではないので購読を行いたい
|
||||
else -> subscribeMastodon(
|
||||
client = client,
|
||||
clientIdentifier = clientIdentifier,
|
||||
endpoint = endpoint,
|
||||
newAlerts = newAlerts
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CheckDeviceHasPriorityResult(
|
||||
val result: TootApiResult?,
|
||||
val failed: Boolean,
|
||||
)
|
||||
|
||||
private suspend fun checkDeviceHasPriority(
|
||||
client: TootApiClient,
|
||||
tokenDigest: String,
|
||||
installId: String,
|
||||
): CheckDeviceHasPriorityResult {
|
||||
val r = client.http(
|
||||
jsonObject {
|
||||
put("token_digest", tokenDigest)
|
||||
put("install_id", installId)
|
||||
|
@ -421,151 +376,275 @@ class PushSubscriptionHelper(
|
|||
.url("${PollingWorker.APP_SERVER}/webpushtokencheck")
|
||||
.build()
|
||||
)
|
||||
res = r.response ?: return r
|
||||
|
||||
if (res.code == 200) {
|
||||
try {
|
||||
res.close()
|
||||
} catch (_: Throwable) {
|
||||
fun rvError() = CheckDeviceHasPriorityResult(r, true)
|
||||
fun rvOk() = CheckDeviceHasPriorityResult(r, false)
|
||||
|
||||
val res = r.response ?: return rvError()
|
||||
return when {
|
||||
res.code != 200 -> {
|
||||
if (res.code == 403) addLog(context.getString(R.string.token_exported))
|
||||
r.caption = "(SubwayTooter App server)"
|
||||
client.readBodyString(r)
|
||||
rvError()
|
||||
}
|
||||
} else {
|
||||
if (res.code == 403) addLog(context.getString(R.string.token_exported))
|
||||
r.caption = "(SubwayTooter App server)"
|
||||
client.readBodyString(r)
|
||||
return r
|
||||
}
|
||||
|
||||
return if (flags == 0) {
|
||||
// 通知設定が全てカラなので、購読を取り消したい
|
||||
|
||||
r = client.request("/api/v1/push/subscription", Request.Builder().delete())
|
||||
res = r?.response ?: return r
|
||||
|
||||
when (res.code) {
|
||||
200 -> {
|
||||
if (verbose) addLog(context.getString(R.string.push_subscription_deleted))
|
||||
TootApiResult()
|
||||
}
|
||||
|
||||
404 -> {
|
||||
if (verbose) {
|
||||
addLog(context.getString(R.string.missing_push_api))
|
||||
r
|
||||
} else {
|
||||
TootApiResult()
|
||||
}
|
||||
}
|
||||
|
||||
403 -> {
|
||||
addLog(context.getString(R.string.missing_push_scope))
|
||||
r
|
||||
}
|
||||
|
||||
else -> {
|
||||
addLog("${res.request}")
|
||||
addLog("${res.code} ${res.message}")
|
||||
r
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 通知設定が空ではないので購読を行いたい
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
val params = JsonObject().apply {
|
||||
put("subscription", JsonObject().apply {
|
||||
put("endpoint", endpoint)
|
||||
put("keys", JsonObject().apply {
|
||||
put(
|
||||
"p256dh",
|
||||
"BBEUVi7Ehdzzpe_ZvlzzkQnhujNJuBKH1R0xYg7XdAKNFKQG9Gpm0TSGRGSuaU7LUFKX-uz8YW0hAshifDCkPuE"
|
||||
)
|
||||
put("auth", "iRdmDrOS6eK6xvG1H6KshQ")
|
||||
})
|
||||
})
|
||||
put("data", JsonObject().apply {
|
||||
put("alerts", newAlerts)
|
||||
account.push_policy?.let { put("policy", it) }
|
||||
})
|
||||
}
|
||||
|
||||
r = client.request(
|
||||
"/api/v1/push/subscription",
|
||||
params.toPostRequestBuilder()
|
||||
) ?: return null
|
||||
|
||||
res = r.response ?: return r
|
||||
|
||||
when (res.code) {
|
||||
404 -> {
|
||||
addLog(context.getString(R.string.missing_push_api))
|
||||
r
|
||||
}
|
||||
|
||||
403 -> {
|
||||
addLog(context.getString(R.string.missing_push_scope))
|
||||
r
|
||||
}
|
||||
|
||||
200 -> {
|
||||
val newSubscription = parseItem(::TootPushSubscription, r.jsonObject)
|
||||
?: return r.setError("parse error.")
|
||||
|
||||
subscribed = true
|
||||
if (verbose) addLog(context.getString(R.string.push_subscription_updated))
|
||||
|
||||
return updateServerKey(
|
||||
client,
|
||||
clientIdentifier,
|
||||
newSubscription.server_key
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
addLog(r.jsonObject?.toString())
|
||||
r
|
||||
}
|
||||
else -> {
|
||||
res.closeQuietly()
|
||||
rvOk()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateSubscription(client: TootApiClient, force: Boolean = false): TootApiResult? =
|
||||
try {
|
||||
// returns null if no error
|
||||
private fun checkInstanceVersionMastodon(ti: TootInstance, subscription404: Boolean): TootApiResult? {
|
||||
|
||||
// 2.4.0rc1 未満にはプッシュ購読APIはない
|
||||
if (!ti.versionGE(TootInstance.VERSION_2_4_0_rc1)) {
|
||||
return TootApiResult(
|
||||
context.getString(R.string.instance_does_not_support_push_api, ti.version)
|
||||
)
|
||||
}
|
||||
|
||||
if (subscription404 && flags == 0) {
|
||||
when {
|
||||
isRecentlyChecked() ->
|
||||
TootApiResult(ERROR_PREVENT_FREQUENTLY_CHECK)
|
||||
|
||||
account.isPseudo ->
|
||||
TootApiResult(context.getString(R.string.pseudo_account_not_supported))
|
||||
|
||||
account.isMisskey ->
|
||||
updateSubscriptionMisskey(client)
|
||||
|
||||
else ->
|
||||
updateSubscriptionMastodon(client, force)
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
TootApiResult(ex.withCaption("error."))
|
||||
}?.apply {
|
||||
|
||||
if (error != null) addLog("$error $requestInfo".trimEnd())
|
||||
|
||||
// update error text on account table
|
||||
val log = logString
|
||||
when {
|
||||
|
||||
log.contains(ERROR_PREVENT_FREQUENTLY_CHECK) -> {
|
||||
// don't update if check was skipped.
|
||||
ti.versionGE(TootInstance.VERSION_2_4_0_rc2) -> {
|
||||
// 購読が不要で現在の状況が404だった場合
|
||||
// 2.4.0rc2以降では「購読が存在しない」を示すので何もしなくてよい
|
||||
if (verbose) addLog(context.getString(R.string.push_subscription_not_exists))
|
||||
return TootApiResult()
|
||||
}
|
||||
|
||||
subscribed || log.isEmpty() ->
|
||||
// clear error text if succeeded or no error log
|
||||
if (account.last_subscription_error != null) {
|
||||
account.updateSubscriptionError(null)
|
||||
}
|
||||
|
||||
else ->
|
||||
// record error text
|
||||
account.updateSubscriptionError(log)
|
||||
else -> {
|
||||
// 2.4.0rc1では「APIが存在しない」と「購読が存在しない」を判別できない
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private class CheckCurrentSubscriptionResult(
|
||||
val result: TootApiResult?,
|
||||
val failed: Boolean,
|
||||
val is404: Boolean,
|
||||
)
|
||||
|
||||
@Suppress("BooleanLiteralArgument")
|
||||
private suspend fun checkCurrentSubscription(client: TootApiClient): CheckCurrentSubscriptionResult {
|
||||
val r = client.request("/api/v1/push/subscription")
|
||||
fun rvError() = CheckCurrentSubscriptionResult(r, true, false)
|
||||
fun rvOk() = CheckCurrentSubscriptionResult(r, false, false)
|
||||
fun rv404() = CheckCurrentSubscriptionResult(r, false, true)
|
||||
val res = r?.response ?: return rvError() // cancelled or missing response
|
||||
|
||||
if (res.code != 200) log.i("${account.acct}: check existing subscription: code=${res.code}")
|
||||
|
||||
return when (res.code) {
|
||||
200 -> {
|
||||
if (r.error?.isNotEmpty() == true && r.jsonObject == null) {
|
||||
// Pleromaが200応答でもエラーHTMLを返す場合がある
|
||||
addLog(context.getString(R.string.instance_does_not_support_push_api_pleroma))
|
||||
rvError()
|
||||
} else {
|
||||
// たぶん購読が存在する
|
||||
rvOk()
|
||||
}
|
||||
}
|
||||
|
||||
// この時点では存在しないのが購読なのかAPIなのか分からない
|
||||
404 -> rv404()
|
||||
|
||||
403 -> {
|
||||
// アクセストークンにpushスコープがない
|
||||
if (flags != 0 || verbose) addLog(context.getString(R.string.missing_push_scope))
|
||||
rvError()
|
||||
}
|
||||
|
||||
in 400 until 500 -> {
|
||||
addLog(context.getString(R.string.instance_does_not_support_push_api_pleroma))
|
||||
rvError()
|
||||
}
|
||||
|
||||
else -> {
|
||||
addLog("${res.request}")
|
||||
addLog("${res.code} ${res.message}")
|
||||
rvOk() // 後でリトライする
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun canSkipSubscriptionMastodon(
|
||||
client: TootApiClient,
|
||||
clientIdentifier: String,
|
||||
endpoint: String,
|
||||
oldSubscription: TootPushSubscription?,
|
||||
newAlerts: JsonObject,
|
||||
): TootApiResult? {
|
||||
|
||||
// 購読を解除したいのに古い購読があるのなら、購読の更新が必要
|
||||
if (flags == 0 && oldSubscription != null) return null
|
||||
|
||||
// endpoint URLが合わないなら購読の更新が必要
|
||||
if (oldSubscription?.endpoint != endpoint) return null
|
||||
|
||||
suspend fun makeSkipResult(): TootApiResult {
|
||||
// 既に登録済みで、endpointも一致している
|
||||
subscribed = true
|
||||
if (verbose) addLog(context.getString(R.string.push_subscription_already_exists))
|
||||
return updateServerKey(client, clientIdentifier, oldSubscription.server_key)
|
||||
}
|
||||
|
||||
// STがstatus通知に対応した時期に古いサーバでここを通ると
|
||||
// flagsの値が変わりendpoint URLも変わった状態で購読を自動更新してしまう
|
||||
// しかしそのタイミングではサーバは古いのでサーバ側の購読内容は変化しなかった。
|
||||
|
||||
// サーバ上の購読アラートのリスト
|
||||
var alertsOld = oldSubscription.alerts.entries
|
||||
.mapNotNull { if (it.value) it.key else null }
|
||||
.sorted()
|
||||
|
||||
// 期待する購読アラートのリスト
|
||||
var alertsNew = newAlerts.entries
|
||||
.mapNotNull { pair -> pair.key.takeIf { pair.value == true } }
|
||||
.sorted()
|
||||
|
||||
// 両方に共通するアラートは除去する
|
||||
val bothHave = alertsOld.filter { alertsNew.contains(it) }
|
||||
alertsOld = alertsOld.filter { !bothHave.contains(it) }
|
||||
alertsNew = alertsNew.filter { !bothHave.contains(it) }
|
||||
|
||||
// サーバのバージョンを調べる前に、この時点でalertsが一致するか確認する
|
||||
if (alertsOld.joinToString(",") == alertsNew.joinToString(",")) {
|
||||
log.i("${account.acct}: same alerts(1)")
|
||||
return makeSkipResult()
|
||||
}
|
||||
|
||||
// ここでサーバのバージョンによって対応が変わる
|
||||
val (ti, result) = TootInstance.get(client)
|
||||
ti ?: return result
|
||||
|
||||
// サーバが知らないアラート種別は比較対象から除去する
|
||||
fun Iterable<String>.knownOnly() = filter {
|
||||
when (it) {
|
||||
"follow", "mention", "favourite", "reblog" -> true
|
||||
"poll" -> ti.versionGE(TootInstance.VERSION_2_8_0_rc1)
|
||||
"follow_request" -> ti.versionGE(TootInstance.VERSION_3_1_0_rc1)
|
||||
"status" -> ti.versionGE(TootInstance.VERSION_3_3_0_rc1)
|
||||
"emoji_reaction" -> ti.versionGE(TootInstance.VERSION_3_4_0_rc1) &&
|
||||
InstanceCapability.emojiReaction(account, ti)
|
||||
|
||||
else -> {
|
||||
log.w("${account.acct}: unknown alert '$it'. server version='${ti.version}'")
|
||||
false // 未知のアラートの差異は比較しない
|
||||
}
|
||||
}
|
||||
}
|
||||
alertsOld = alertsOld.knownOnly()
|
||||
alertsNew = alertsNew.knownOnly()
|
||||
|
||||
return if (alertsOld.joinToString(",") == alertsNew.joinToString(",")) {
|
||||
log.i("${account.acct}: same alerts(2)")
|
||||
makeSkipResult()
|
||||
} else {
|
||||
addLog("${account.acct}: alerts not match. account=${account.acct.pretty} old=${alertsOld.sorted()}, new=${alertsNew.sorted()}")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun unsubscribeMastodon(
|
||||
client: TootApiClient,
|
||||
): TootApiResult? {
|
||||
|
||||
val r = client.request("/api/v1/push/subscription", Request.Builder().delete())
|
||||
val res = r?.response ?: return r
|
||||
|
||||
return when (res.code) {
|
||||
200 -> {
|
||||
if (verbose) addLog(context.getString(R.string.push_subscription_deleted))
|
||||
TootApiResult()
|
||||
}
|
||||
|
||||
404 -> {
|
||||
if (verbose) {
|
||||
addLog(context.getString(R.string.missing_push_api))
|
||||
r
|
||||
} else {
|
||||
TootApiResult()
|
||||
}
|
||||
}
|
||||
|
||||
403 -> {
|
||||
addLog(context.getString(R.string.missing_push_scope))
|
||||
r
|
||||
}
|
||||
|
||||
else -> {
|
||||
addLog("${res.request}")
|
||||
addLog("${res.code} ${res.message}")
|
||||
r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun subscribeMastodon(
|
||||
client: TootApiClient,
|
||||
clientIdentifier: String,
|
||||
endpoint: String,
|
||||
newAlerts: JsonObject,
|
||||
): TootApiResult? {
|
||||
@Suppress("SpellCheckingInspection")
|
||||
val params = JsonObject().apply {
|
||||
put("subscription", JsonObject().apply {
|
||||
put("endpoint", endpoint)
|
||||
put("keys", JsonObject().apply {
|
||||
put(
|
||||
"p256dh",
|
||||
"BBEUVi7Ehdzzpe_ZvlzzkQnhujNJuBKH1R0xYg7XdAKNFKQG9Gpm0TSGRGSuaU7LUFKX-uz8YW0hAshifDCkPuE"
|
||||
)
|
||||
put("auth", "iRdmDrOS6eK6xvG1H6KshQ")
|
||||
})
|
||||
})
|
||||
put("data", JsonObject().apply {
|
||||
put("alerts", newAlerts)
|
||||
account.push_policy?.let { put("policy", it) }
|
||||
})
|
||||
}
|
||||
|
||||
val r = client.request(
|
||||
"/api/v1/push/subscription",
|
||||
params.toPostRequestBuilder()
|
||||
) ?: return null
|
||||
|
||||
val res = r.response ?: return r
|
||||
|
||||
return when (res.code) {
|
||||
404 -> {
|
||||
addLog(context.getString(R.string.missing_push_api))
|
||||
r
|
||||
}
|
||||
|
||||
403 -> {
|
||||
addLog(context.getString(R.string.missing_push_scope))
|
||||
r
|
||||
}
|
||||
|
||||
200 -> {
|
||||
val newSubscription = parseItem(::TootPushSubscription, r.jsonObject)
|
||||
?: return r.setError("parse error.")
|
||||
|
||||
subscribed = true
|
||||
if (verbose) addLog(context.getString(R.string.push_subscription_updated))
|
||||
|
||||
return updateServerKey(
|
||||
client,
|
||||
clientIdentifier,
|
||||
newSubscription.server_key
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
addLog(r.jsonObject?.toString())
|
||||
r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import androidx.core.app.NotificationCompat
|
|||
import androidx.core.content.ContextCompat
|
||||
import jp.juggler.subwaytooter.ActCallback
|
||||
import jp.juggler.subwaytooter.EventReceiver
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.TootApiCallback
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
|
@ -339,7 +339,7 @@ class TaskRunner(
|
|||
|
||||
this.parser = TootParser(context, account)
|
||||
|
||||
if (Pref.bpSeparateReplyNotificationGroup(pref)) {
|
||||
if (PrefB.bpSeparateReplyNotificationGroup(pref)) {
|
||||
var tr = TrackingRunner(
|
||||
trackingType = TrackingType.NotReply,
|
||||
trackingName = NotificationHelper.TRACKING_NAME_DEFAULT
|
||||
|
@ -474,7 +474,7 @@ class TaskRunner(
|
|||
val first = dataList.firstOrNull()
|
||||
if (first == null) {
|
||||
log.d("showNotification[${account.acct.pretty}/$notificationTag] cancel notification.")
|
||||
if (Build.VERSION.SDK_INT >= 23 && Pref.bpDivideNotification(pref)) {
|
||||
if (Build.VERSION.SDK_INT >= 23 && PrefB.bpDivideNotification(pref)) {
|
||||
notificationManager.activeNotifications?.forEach {
|
||||
if (it != null &&
|
||||
it.id == PollingWorker.NOTIFICATION_ID &&
|
||||
|
@ -501,7 +501,7 @@ class TaskRunner(
|
|||
return
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 23 && Pref.bpDivideNotification(pref)) {
|
||||
if (Build.VERSION.SDK_INT >= 23 && PrefB.bpDivideNotification(pref)) {
|
||||
val activeNotificationMap = HashMap<String, StatusBarNotification>().apply {
|
||||
notificationManager.activeNotifications?.forEach {
|
||||
if (it != null &&
|
||||
|
@ -554,7 +554,7 @@ class TaskRunner(
|
|||
if (Build.VERSION.SDK_INT < 26) {
|
||||
var iv = 0
|
||||
|
||||
if (Pref.bpNotificationSound(pref)) {
|
||||
if (PrefB.bpNotificationSound(pref)) {
|
||||
|
||||
var soundUri: Uri? = null
|
||||
|
||||
|
@ -583,11 +583,11 @@ class TaskRunner(
|
|||
}
|
||||
}
|
||||
|
||||
if (Pref.bpNotificationVibration(pref)) {
|
||||
if (PrefB.bpNotificationVibration(pref)) {
|
||||
iv = iv or NotificationCompat.DEFAULT_VIBRATE
|
||||
}
|
||||
|
||||
if (Pref.bpNotificationLED(pref)) {
|
||||
if (PrefB.bpNotificationLED(pref)) {
|
||||
iv = iv or NotificationCompat.DEFAULT_LIGHTS
|
||||
}
|
||||
|
||||
|
@ -629,7 +629,7 @@ class TaskRunner(
|
|||
|
||||
var iv = 0
|
||||
|
||||
if (Pref.bpNotificationSound(pref)) {
|
||||
if (PrefB.bpNotificationSound(pref)) {
|
||||
|
||||
var soundUri: Uri? = null
|
||||
|
||||
|
@ -659,11 +659,11 @@ class TaskRunner(
|
|||
}
|
||||
}
|
||||
|
||||
if (Pref.bpNotificationVibration(pref)) {
|
||||
if (PrefB.bpNotificationVibration(pref)) {
|
||||
iv = iv or NotificationCompat.DEFAULT_VIBRATE
|
||||
}
|
||||
|
||||
if (Pref.bpNotificationLED(pref)) {
|
||||
if (PrefB.bpNotificationLED(pref)) {
|
||||
iv = iv or NotificationCompat.DEFAULT_LIGHTS
|
||||
}
|
||||
|
||||
|
@ -829,7 +829,7 @@ class TaskRunner(
|
|||
|
||||
private fun NotificationData.getNotificationLine(): String {
|
||||
|
||||
val name = when (Pref.bpShowAcctInSystemNotification(pref)) {
|
||||
val name = when (PrefB.bpShowAcctInSystemNotification(pref)) {
|
||||
false -> notification.accountRef?.decoded_display_name
|
||||
|
||||
true -> {
|
||||
|
|
|
@ -23,7 +23,7 @@ object MspHelper {
|
|||
private suspend fun TootApiClient.search(query: String, maxId: String?): TootApiResult? {
|
||||
|
||||
// ユーザトークンを読む
|
||||
var user_token: String? = Pref.spMspUserToken(pref)
|
||||
var user_token: String? = PrefS.spMspUserToken(pref)
|
||||
|
||||
for (nTry in 0 until 3) {
|
||||
if (callback.isApiCancelled) return null
|
||||
|
@ -56,7 +56,7 @@ object MspHelper {
|
|||
if (user_token?.isEmpty() != false) {
|
||||
return result.setError("Can't get MSP user token. response=${result.bodyString}")
|
||||
} else {
|
||||
pref.edit().put(Pref.spMspUserToken, user_token).apply()
|
||||
pref.edit().put(PrefS.spMspUserToken, user_token).apply()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import android.text.style.ReplacementSpan
|
|||
import androidx.annotation.IntRange
|
||||
import jp.juggler.apng.ApngFrames
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.util.LogCategory
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
|
@ -89,7 +89,7 @@ class NetworkEmojiSpan internal constructor(
|
|||
} ?: return
|
||||
|
||||
val t = when {
|
||||
Pref.bpDisableEmojiAnimation(App1.pref) -> 0L
|
||||
PrefB.bpDisableEmojiAnimation(App1.pref) -> 0L
|
||||
else -> invalidateCallback.timeFromStart
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ class NetworkEmojiSpan internal constructor(
|
|||
|
||||
// 少し後に描画しなおす
|
||||
val delay = mFrameFindResult.delay
|
||||
if (delay != Long.MAX_VALUE && !Pref.bpDisableEmojiAnimation(App1.pref)) {
|
||||
if (delay != Long.MAX_VALUE && !PrefB.bpDisableEmojiAnimation(App1.pref)) {
|
||||
invalidateCallback.delayInvalidate(delay)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package jp.juggler.subwaytooter.streaming
|
||||
|
||||
import android.os.SystemClock
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.api.TootApiCallback
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
import jp.juggler.subwaytooter.api.TootApiResult
|
||||
|
@ -139,7 +139,7 @@ class StreamConnection(
|
|||
}
|
||||
|
||||
private fun fireDeleteId(id: EntityId) {
|
||||
if (Pref.bpDontRemoveDeletedToot.invoke(manager.appState.pref)) return
|
||||
if (PrefB.bpDontRemoveDeletedToot.invoke(manager.appState.pref)) return
|
||||
val timelineHost = acctGroup.account.apiHost
|
||||
manager.appState.columnList.forEach {
|
||||
runOnMainLooper {
|
||||
|
|
|
@ -2,7 +2,7 @@ package jp.juggler.subwaytooter.streaming
|
|||
|
||||
import jp.juggler.subwaytooter.AppState
|
||||
import jp.juggler.subwaytooter.Column
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.api.TootApiCallback
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
import jp.juggler.subwaytooter.api.entity.Acct
|
||||
|
@ -69,7 +69,7 @@ class StreamManager(val appState: AppState) {
|
|||
return acctGroup
|
||||
}
|
||||
|
||||
if (isScreenOn && !Pref.bpDontUseStreaming(appState.pref)) {
|
||||
if (isScreenOn && !PrefB.bpDontUseStreaming(appState.pref)) {
|
||||
for (column in appState.columnList) {
|
||||
val accessInfo = column.accessInfo
|
||||
if (column.isDispose.get() || column.dontStreaming || accessInfo.isNA) continue
|
||||
|
|
|
@ -11,15 +11,12 @@ import android.os.Build
|
|||
import android.os.Bundle
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import jp.juggler.subwaytooter.ActMain
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.*
|
||||
import jp.juggler.subwaytooter.action.*
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus.Companion.findStatusIdFromUrl
|
||||
import jp.juggler.subwaytooter.api.entity.TootTag.Companion.findHashtagFromUrl
|
||||
import jp.juggler.subwaytooter.dialog.DlgAppPicker
|
||||
import jp.juggler.subwaytooter.pref
|
||||
import jp.juggler.subwaytooter.span.LinkInfo
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.util.*
|
||||
|
@ -42,7 +39,7 @@ private fun Activity.openBrowserExcludeMe(
|
|||
): Boolean {
|
||||
try {
|
||||
if (intent.component == null) {
|
||||
val cn = Pref.spWebBrowser(pref).cn()
|
||||
val cn = PrefS.spWebBrowser(pref).cn()
|
||||
if (cn?.exists(this) == true) {
|
||||
intent.component = cn
|
||||
}
|
||||
|
@ -134,7 +131,7 @@ fun Activity.openCustomTab(url: String?, pref: SharedPreferences = pref()) {
|
|||
return
|
||||
}
|
||||
|
||||
if (Pref.bpDontUseCustomTabs(pref)) {
|
||||
if (PrefB.bpDontUseCustomTabs(pref)) {
|
||||
openBrowser(url, pref)
|
||||
return
|
||||
}
|
||||
|
@ -161,7 +158,7 @@ fun Activity.openCustomTab(url: String?, pref: SharedPreferences = pref()) {
|
|||
)
|
||||
}
|
||||
|
||||
if (url.startsWith("http") && Pref.bpPriorChrome(pref)) {
|
||||
if (url.startsWith("http") && PrefB.bpPriorChrome(pref)) {
|
||||
try {
|
||||
// 初回はChrome指定で試す
|
||||
val cn = ComponentName(
|
||||
|
|
|
@ -7,7 +7,7 @@ import android.text.style.ForegroundColorSpan
|
|||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
|
@ -436,7 +436,7 @@ class CompletionHelper(
|
|||
private val openPickerEmoji: Runnable = Runnable {
|
||||
EmojiPicker(
|
||||
activity, accessInfo,
|
||||
closeOnSelected = Pref.bpEmojiPickerCloseOnSelected(pref)
|
||||
closeOnSelected = PrefB.bpEmojiPickerCloseOnSelected(pref)
|
||||
) { result ->
|
||||
val et = this.et ?: return@EmojiPicker
|
||||
|
||||
|
@ -469,7 +469,7 @@ class CompletionHelper(
|
|||
fun openEmojiPickerFromMore() {
|
||||
EmojiPicker(
|
||||
activity, accessInfo,
|
||||
closeOnSelected = Pref.bpEmojiPickerCloseOnSelected(pref)
|
||||
closeOnSelected = PrefB.bpEmojiPickerCloseOnSelected(pref)
|
||||
) { result ->
|
||||
val et = this.et ?: return@EmojiPicker
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import android.graphics.PorterDuff
|
|||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.content.ContextCompat
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefS
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
|
@ -38,22 +38,22 @@ object CustomShare {
|
|||
val defaultComponentName: String?
|
||||
when (target) {
|
||||
CustomShareTarget.Translate -> {
|
||||
src = Pref.spTranslateAppComponent(pref)
|
||||
src = PrefS.spTranslateAppComponent(pref)
|
||||
defaultComponentName = translate_app_component_default
|
||||
}
|
||||
|
||||
CustomShareTarget.CustomShare1 -> {
|
||||
src = Pref.spCustomShare1(pref)
|
||||
src = PrefS.spCustomShare1(pref)
|
||||
defaultComponentName = null
|
||||
}
|
||||
|
||||
CustomShareTarget.CustomShare2 -> {
|
||||
src = Pref.spCustomShare2(pref)
|
||||
src = PrefS.spCustomShare2(pref)
|
||||
defaultComponentName = null
|
||||
}
|
||||
|
||||
CustomShareTarget.CustomShare3 -> {
|
||||
src = Pref.spCustomShare3(pref)
|
||||
src = PrefS.spCustomShare3(pref)
|
||||
defaultComponentName = null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import android.text.Spanned
|
|||
import android.util.SparseBooleanArray
|
||||
import androidx.annotation.DrawableRes
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||
import jp.juggler.subwaytooter.emoji.EmojiMap
|
||||
|
@ -34,7 +34,7 @@ object EmojiDecoder {
|
|||
|
||||
var handleUnicodeEmoji = true
|
||||
|
||||
fun customEmojiSeparator(pref: SharedPreferences) = if (Pref.bpCustomEmojiSeparatorZwsp(pref)) {
|
||||
fun customEmojiSeparator(pref: SharedPreferences) = if (PrefB.bpCustomEmojiSeparatorZwsp(pref)) {
|
||||
'\u200B'
|
||||
} else {
|
||||
' '
|
||||
|
@ -351,7 +351,7 @@ object EmojiDecoder {
|
|||
|
||||
val useEmojioneShortcode = when (val context = options.context) {
|
||||
null -> false
|
||||
else -> Pref.bpEmojioneShortcode(context.pref())
|
||||
else -> PrefB.bpEmojioneShortcode(context.pref())
|
||||
}
|
||||
|
||||
splitShortCode(s, callback = object : ShortCodeSplitterCallback {
|
||||
|
@ -376,7 +376,7 @@ object EmojiDecoder {
|
|||
val emojiCustom = emojiMapCustom?.get(name)
|
||||
if (emojiCustom != null) {
|
||||
val url = when {
|
||||
Pref.bpDisableEmojiAnimation(App1.pref) && emojiCustom.staticUrl?.isNotEmpty() == true -> emojiCustom.staticUrl
|
||||
PrefB.bpDisableEmojiAnimation(App1.pref) && emojiCustom.staticUrl?.isNotEmpty() == true -> emojiCustom.staticUrl
|
||||
else -> emojiCustom.url
|
||||
}
|
||||
builder.addNetworkEmojiSpan(part, url)
|
||||
|
@ -421,7 +421,7 @@ object EmojiDecoder {
|
|||
s: String,
|
||||
emojiMapCustom: HashMap<String, CustomEmoji>? = null
|
||||
): String {
|
||||
val decodeEmojioneShortcode = Pref.bpEmojioneShortcode(App1.pref)
|
||||
val decodeEmojioneShortcode = PrefB.bpEmojioneShortcode(App1.pref)
|
||||
|
||||
val sb = StringBuilder()
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import android.text.SpannableStringBuilder
|
|||
import android.text.Spanned
|
||||
import android.text.style.*
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.span.*
|
||||
|
@ -468,10 +468,10 @@ object HTMLDecoder {
|
|||
else -> false
|
||||
}
|
||||
|
||||
fun canSkipEncode(isBlockParent: Boolean, parent: Node, prev: Node?, next: Node?) = when {
|
||||
fun canSkipEncode(isBlockParent: Boolean, curr: Node, parent: Node, prev: Node?, next: Node?) = when {
|
||||
!isBlockParent -> false
|
||||
tag != TAG_TEXT -> false
|
||||
text.isNotBlank() -> false
|
||||
curr.tag != TAG_TEXT -> false
|
||||
curr.text.isNotBlank() -> false
|
||||
else -> when {
|
||||
prev?.tag?.tagsCanRemoveNearSpaces() == true -> true
|
||||
next?.tag?.tagsCanRemoveNearSpaces() == true -> true
|
||||
|
@ -480,6 +480,248 @@ object HTMLDecoder {
|
|||
}
|
||||
}
|
||||
|
||||
fun encodeText(options: DecodeOptions, sb: SpannableStringBuilder) {
|
||||
if (options.context != null && options.decodeEmoji) {
|
||||
sb.append(options.decodeEmoji(decodeEntity(text)))
|
||||
} else {
|
||||
sb.append(decodeEntity(text))
|
||||
}
|
||||
}
|
||||
|
||||
fun encodeImage(options: DecodeOptions, sb: SpannableStringBuilder) {
|
||||
val attrs = parseAttributes(text)
|
||||
|
||||
if (options.unwrapEmojiImageTag) {
|
||||
val cssClass = attrs["class"]
|
||||
val title = attrs["title"]
|
||||
val url = attrs["src"]
|
||||
val alt = attrs["alt"]
|
||||
if (cssClass != null &&
|
||||
title != null &&
|
||||
cssClass.contains("emojione") &&
|
||||
reShortcode.matcher(title).find()
|
||||
) {
|
||||
sb.append(options.decodeEmoji(title))
|
||||
return
|
||||
} else if (cssClass == "emoji" && url != null && alt != null && reNotestockEmojiAlt.matches(alt)) {
|
||||
// notestock custom emoji
|
||||
sb.run {
|
||||
val start = length
|
||||
append(alt)
|
||||
val end = length
|
||||
setSpan(
|
||||
NetworkEmojiSpan(url, scale = options.enlargeCustomEmoji),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("<img ")
|
||||
val url = attrs["src"] ?: ""
|
||||
val caption = attrs["alt"] ?: ""
|
||||
if (caption.isNotEmpty() || url.isNotEmpty()) {
|
||||
val start = sb.length
|
||||
sb.append(caption.notEmpty() ?: url)
|
||||
if (reUrlStart.find(url) != null) {
|
||||
val span =
|
||||
MyClickableSpan(LinkInfo(url = url, ac = null, tag = null, caption = caption, mention = null))
|
||||
sb.setSpan(span, start, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
sb.append(" ")
|
||||
}
|
||||
sb.append("/>")
|
||||
}
|
||||
|
||||
class EncodeSpanEnv(
|
||||
val options: DecodeOptions,
|
||||
val listContext: ListContext,
|
||||
val tag: String,
|
||||
val sb: SpannableStringBuilder,
|
||||
val sbTmp: SpannableStringBuilder,
|
||||
val spanStart: Int,
|
||||
)
|
||||
|
||||
val originalFlusher: EncodeSpanEnv.() -> Unit = {
|
||||
when (tag) {
|
||||
"s", "strike", "del" -> {
|
||||
sb.setSpan(StrikethroughSpan(), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"em" -> {
|
||||
sb.setSpan(
|
||||
fontSpan(Typeface.defaultFromStyle(Typeface.ITALIC)),
|
||||
spanStart,
|
||||
sb.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
"strong" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"tr" -> {
|
||||
sb.append("|")
|
||||
}
|
||||
|
||||
"style", "script" -> {
|
||||
// sb_tmpにレンダリングした分は読み捨てる
|
||||
}
|
||||
"h1" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(1.8f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"h2" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(1.6f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"h3" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(1.4f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"h4" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(1.2f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"h5" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(1.0f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"h6" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(0.8f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"pre" -> {
|
||||
sb.setSpan(BackgroundColorSpan(0x40808080), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(0.7f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(fontSpan(Typeface.MONOSPACE), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"code" -> {
|
||||
sb.setSpan(BackgroundColorSpan(0x40808080), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(fontSpan(Typeface.MONOSPACE), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"hr" -> sb.append("----------")
|
||||
}
|
||||
}
|
||||
|
||||
val tmpFlusher = HashMap<String, EncodeSpanEnv.() -> Unit>().apply {
|
||||
|
||||
fun add(vararg tags: String, block: EncodeSpanEnv.() -> Unit) {
|
||||
for (tag in tags) this[tag] = block
|
||||
}
|
||||
|
||||
add("a") {
|
||||
val linkInfo = formatLinkCaption(options, sbTmp, href ?: "")
|
||||
val caption = linkInfo.caption
|
||||
if (caption.isNotEmpty()) {
|
||||
val start = sb.length
|
||||
sb.append(linkInfo.caption)
|
||||
val end = sb.length
|
||||
if (linkInfo.url.isNotEmpty()) {
|
||||
val span = MyClickableSpan(linkInfo)
|
||||
sb.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
|
||||
// リンクスパンを設定した後に色をつける
|
||||
val list = options.highlightTrie?.matchList(sb, start, end)
|
||||
if (list != null) {
|
||||
for (range in list) {
|
||||
val word = HighlightWord.load(range.word) ?: continue
|
||||
sb.setSpan(
|
||||
HighlightSpan(word.color_fg, word.color_bg),
|
||||
range.start,
|
||||
range.end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
|
||||
if (word.sound_type != HighlightWord.SOUND_TYPE_NONE) {
|
||||
if (options.highlightSound == null) options.highlightSound = word
|
||||
}
|
||||
|
||||
if (word.speech != 0) {
|
||||
if (options.highlightSpeech == null) options.highlightSpeech = word
|
||||
}
|
||||
|
||||
if (options.highlightAny == null) options.highlightAny = word
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add("style", "script") {
|
||||
// 読み捨てる
|
||||
// 最適化によりtmpFlusherOriginalとこのラムダが同一オブジェクトにならないようにする
|
||||
}
|
||||
|
||||
add("blockquote") {
|
||||
val bg_color = listContext.quoteColor()
|
||||
|
||||
// TextView の文字装飾では「ブロック要素の入れ子」を表現できない
|
||||
// 内容の各行の始端に何か追加するというのがまずキツい
|
||||
// しかし各行の頭に引用マークをつけないと引用のネストで意味が通じなくなってしまう
|
||||
|
||||
val startItalic = sb.length
|
||||
sbTmp.splitLines().forEach { line ->
|
||||
val lineStart = sb.length
|
||||
sb.append("> ")
|
||||
sb.setSpan(
|
||||
BackgroundColorSpan(bg_color),
|
||||
lineStart,
|
||||
lineStart + 1,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
sb.append(line)
|
||||
}
|
||||
sb.setSpan(
|
||||
fontSpan(Typeface.defaultFromStyle(Typeface.ITALIC)),
|
||||
startItalic,
|
||||
sb.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
add("li") {
|
||||
val lineHeader1 = listContext.increment()
|
||||
val lineHeader2 = " ".repeat(lineHeader1.length)
|
||||
sbTmp.splitLines().forEachIndexed { i, line ->
|
||||
sb.append(if (i == 0) lineHeader1 else lineHeader2)
|
||||
sb.append(line)
|
||||
}
|
||||
}
|
||||
|
||||
add("dt") {
|
||||
val prefix = listContext.increment()
|
||||
val startBold = sb.length
|
||||
sbTmp.splitLines().forEach { line ->
|
||||
sb.append(prefix)
|
||||
sb.append(line)
|
||||
}
|
||||
sb.setSpan(
|
||||
fontSpan(Typeface.defaultFromStyle(Typeface.BOLD)),
|
||||
startBold,
|
||||
sb.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
add("dd") {
|
||||
val prefix = listContext.increment() + " "
|
||||
sbTmp.splitLines().forEach { line ->
|
||||
sb.append(prefix)
|
||||
sb.append(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun childListContext(tag: String, outerContext: ListContext) = when (tag) {
|
||||
"ol" -> outerContext.subOrdered()
|
||||
"ul" -> outerContext.subUnordered()
|
||||
"dl" -> outerContext.subDefinition()
|
||||
"blockquote" -> outerContext.subQuote()
|
||||
else -> outerContext
|
||||
}
|
||||
|
||||
fun encodeSpan(
|
||||
options: DecodeOptions,
|
||||
sb: SpannableStringBuilder,
|
||||
|
@ -488,59 +730,11 @@ object HTMLDecoder {
|
|||
val isBlock = blockLevelElements.contains(tag)
|
||||
when (tag) {
|
||||
TAG_TEXT -> {
|
||||
if (options.context != null && options.decodeEmoji) {
|
||||
sb.append(options.decodeEmoji(decodeEntity(text)))
|
||||
} else {
|
||||
sb.append(decodeEntity(text))
|
||||
}
|
||||
encodeText(options, sb)
|
||||
return
|
||||
}
|
||||
"img" -> {
|
||||
val attrs = parseAttributes(text)
|
||||
|
||||
if (options.unwrapEmojiImageTag) {
|
||||
val cssClass = attrs["class"]
|
||||
val title = attrs["title"]
|
||||
val url = attrs["src"]
|
||||
val alt = attrs["alt"]
|
||||
if (cssClass != null &&
|
||||
title != null &&
|
||||
cssClass.contains("emojione") &&
|
||||
reShortcode.matcher(title).find()
|
||||
) {
|
||||
sb.append(options.decodeEmoji(title))
|
||||
return
|
||||
} else if (cssClass == "emoji" && url != null && alt != null && reNotestockEmojiAlt.matches(alt)) {
|
||||
// notestock custom emoji
|
||||
sb.run {
|
||||
val start = length
|
||||
append(alt)
|
||||
val end = length
|
||||
setSpan(
|
||||
NetworkEmojiSpan(url, scale = options.enlargeCustomEmoji),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("<img ")
|
||||
val url = attrs["src"] ?: ""
|
||||
val caption = attrs["alt"] ?: ""
|
||||
if (caption.isNotEmpty() || url.isNotEmpty()) {
|
||||
val start = sb.length
|
||||
sb.append(caption.notEmpty() ?: url)
|
||||
if (reUrlStart.find(url) != null) {
|
||||
val span =
|
||||
MyClickableSpan(LinkInfo(url = url, ac = null, tag = null, caption = caption, mention = null))
|
||||
sb.setSpan(span, start, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
sb.append(" ")
|
||||
}
|
||||
sb.append("/>")
|
||||
encodeImage(options, sb)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -556,216 +750,46 @@ object HTMLDecoder {
|
|||
}
|
||||
}
|
||||
|
||||
var spanStart = 0
|
||||
|
||||
val tmpFlusherOriginal: (SpannableStringBuilder) -> Unit = {
|
||||
|
||||
when (tag) {
|
||||
"s", "strike", "del" -> {
|
||||
sb.setSpan(StrikethroughSpan(), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"em" -> {
|
||||
sb.setSpan(
|
||||
fontSpan(Typeface.defaultFromStyle(Typeface.ITALIC)),
|
||||
spanStart,
|
||||
sb.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
"strong" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"tr" -> {
|
||||
sb.append("|")
|
||||
}
|
||||
|
||||
"style", "script" -> {
|
||||
// sb_tmpにレンダリングした分は読み捨てる
|
||||
}
|
||||
"h1" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(1.8f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"h2" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(1.6f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"h3" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(1.4f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"h4" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(1.2f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"h5" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(1.0f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"h6" -> {
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(0.8f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"pre" -> {
|
||||
sb.setSpan(BackgroundColorSpan(0x40808080), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(RelativeSizeSpan(0.7f), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(fontSpan(Typeface.MONOSPACE), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"code" -> {
|
||||
sb.setSpan(BackgroundColorSpan(0x40808080), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(fontSpan(Typeface.MONOSPACE), spanStart, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
"hr" -> sb.append("----------")
|
||||
}
|
||||
}
|
||||
|
||||
val tmpFlusher = when (tag) {
|
||||
"a" -> {
|
||||
{ sb_tmp ->
|
||||
val linkInfo = formatLinkCaption(options, sb_tmp, href ?: "")
|
||||
val caption = linkInfo.caption
|
||||
if (caption.isNotEmpty()) {
|
||||
val start = sb.length
|
||||
sb.append(linkInfo.caption)
|
||||
val end = sb.length
|
||||
if (linkInfo.url.isNotEmpty()) {
|
||||
val span = MyClickableSpan(linkInfo)
|
||||
sb.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
|
||||
// リンクスパンを設定した後に色をつける
|
||||
val list = options.highlightTrie?.matchList(sb, start, end)
|
||||
if (list != null) {
|
||||
for (range in list) {
|
||||
val word = HighlightWord.load(range.word) ?: continue
|
||||
sb.setSpan(
|
||||
HighlightSpan(word.color_fg, word.color_bg),
|
||||
range.start,
|
||||
range.end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
|
||||
if (word.sound_type != HighlightWord.SOUND_TYPE_NONE) {
|
||||
if (options.highlightSound == null) options.highlightSound = word
|
||||
}
|
||||
|
||||
if (word.speech != 0) {
|
||||
if (options.highlightSpeech == null) options.highlightSpeech = word
|
||||
}
|
||||
|
||||
if (options.highlightAny == null) options.highlightAny = word
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"style", "script" -> {
|
||||
{
|
||||
// 読み捨てる
|
||||
// 最適化によりtmpFlusherOriginalとこのラムダが同一オブジェクトにならないようにする
|
||||
}
|
||||
}
|
||||
|
||||
"blockquote" -> {
|
||||
{ sb_tmp ->
|
||||
val bg_color = listContext.quoteColor()
|
||||
|
||||
// TextView の文字装飾では「ブロック要素の入れ子」を表現できない
|
||||
// 内容の各行の始端に何か追加するというのがまずキツい
|
||||
// しかし各行の頭に引用マークをつけないと引用のネストで意味が通じなくなってしまう
|
||||
|
||||
val startItalic = sb.length
|
||||
sb_tmp.splitLines().forEach { line ->
|
||||
val lineStart = sb.length
|
||||
sb.append("> ")
|
||||
sb.setSpan(
|
||||
BackgroundColorSpan(bg_color),
|
||||
lineStart,
|
||||
lineStart + 1,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
sb.append(line)
|
||||
}
|
||||
sb.setSpan(
|
||||
fontSpan(Typeface.defaultFromStyle(Typeface.ITALIC)),
|
||||
startItalic,
|
||||
sb.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"li" -> {
|
||||
{ sb_tmp ->
|
||||
val lineHeader1 = listContext.increment()
|
||||
val lineHeader2 = " ".repeat(lineHeader1.length)
|
||||
sb_tmp.splitLines().forEachIndexed { i, line ->
|
||||
sb.append(if (i == 0) lineHeader1 else lineHeader2)
|
||||
sb.append(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"dt" -> {
|
||||
{ sb_tmp ->
|
||||
val prefix = listContext.increment()
|
||||
val startBold = sb.length
|
||||
sb_tmp.splitLines().forEach { line ->
|
||||
sb.append(prefix)
|
||||
sb.append(line)
|
||||
}
|
||||
sb.setSpan(
|
||||
fontSpan(Typeface.defaultFromStyle(Typeface.BOLD)),
|
||||
startBold,
|
||||
sb.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"dd" -> {
|
||||
{ sb_tmp ->
|
||||
val prefix = listContext.increment() + " "
|
||||
sb_tmp.splitLines().forEach { line ->
|
||||
sb.append(prefix)
|
||||
sb.append(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> tmpFlusherOriginal
|
||||
}
|
||||
|
||||
val sb_tmp = if (tmpFlusher == tmpFlusherOriginal) {
|
||||
sb
|
||||
var flusher = this.tmpFlusher[tag]
|
||||
val encodeSpanEnv = if (flusher != null) {
|
||||
// 一時的なバッファに子要素を出力して、後で何か処理する
|
||||
EncodeSpanEnv(
|
||||
options = options,
|
||||
listContext = listContext,
|
||||
tag = tag,
|
||||
sb = sb,
|
||||
sbTmp = SpannableStringBuilder(),
|
||||
spanStart = 0,
|
||||
)
|
||||
} else {
|
||||
SpannableStringBuilder()
|
||||
// 現在のバッファに出力する
|
||||
flusher = originalFlusher
|
||||
EncodeSpanEnv(
|
||||
options = options,
|
||||
listContext = listContext,
|
||||
tag = tag,
|
||||
sb = sb,
|
||||
sbTmp = sb,
|
||||
spanStart = sb.length
|
||||
)
|
||||
}
|
||||
|
||||
spanStart = sb_tmp.length
|
||||
|
||||
val childListContext = when (tag) {
|
||||
"ol" -> listContext.subOrdered()
|
||||
"ul" -> listContext.subUnordered()
|
||||
"dl" -> listContext.subDefinition()
|
||||
"blockquote" -> listContext.subQuote()
|
||||
else -> listContext
|
||||
}
|
||||
val childListContext = childListContext(tag, listContext)
|
||||
|
||||
child_nodes.forEachIndexed { i, child ->
|
||||
if (!canSkipEncode(
|
||||
isBlock,
|
||||
curr = child,
|
||||
parent = this,
|
||||
prev = child_nodes.elementAtOrNull(i - 1),
|
||||
next = child_nodes.elementAtOrNull(i + 1)
|
||||
)
|
||||
) {
|
||||
child.encodeSpan(options, sb_tmp, childListContext)
|
||||
child.encodeSpan(options, encodeSpanEnv.sbTmp, childListContext)
|
||||
}
|
||||
}
|
||||
|
||||
tmpFlusher(sb_tmp)
|
||||
flusher(encodeSpanEnv)
|
||||
|
||||
if (isBlock) {
|
||||
// ブロック要素
|
||||
|
@ -849,7 +873,7 @@ object HTMLDecoder {
|
|||
val linkInfo = if (fullAcct != null) {
|
||||
LinkInfo(
|
||||
url = item.url,
|
||||
caption = "@${(if (Pref.bpMentionFullAcct(App1.pref)) fullAcct else item.acct).pretty}",
|
||||
caption = "@${(if (PrefB.bpMentionFullAcct(App1.pref)) fullAcct else item.acct).pretty}",
|
||||
ac = AcctColor.load(fullAcct),
|
||||
mention = item,
|
||||
tag = link_tag
|
||||
|
@ -939,7 +963,7 @@ object HTMLDecoder {
|
|||
|
||||
fun afterFullAcctResolved(fullAcct: Acct) {
|
||||
linkInfo.ac = AcctColor.load(fullAcct)
|
||||
if (options.mentionFullAcct || Pref.bpMentionFullAcct(App1.pref)) {
|
||||
if (options.mentionFullAcct || PrefB.bpMentionFullAcct(App1.pref)) {
|
||||
linkInfo.caption = "@${fullAcct.pretty}"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import android.util.SparseArray
|
|||
import android.util.SparseBooleanArray
|
||||
import jp.juggler.subwaytooter.ActMain
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.span.*
|
||||
|
@ -860,7 +860,7 @@ object MisskeyMarkdownDecoder {
|
|||
// リンク表記はユーザの記述やアプリ設定の影響を受ける
|
||||
val caption = "@${
|
||||
when {
|
||||
Pref.bpMentionFullAcct(App1.pref) -> fullAcct
|
||||
PrefB.bpMentionFullAcct(App1.pref) -> fullAcct
|
||||
else -> rawAcct
|
||||
}.pretty
|
||||
}"
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.os.SystemClock
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.Styler
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
|
@ -131,7 +131,6 @@ class PostImpl(
|
|||
return false
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -156,7 +155,7 @@ class PostImpl(
|
|||
return false
|
||||
}
|
||||
|
||||
if (!bConfirmTagCharacter && Pref.bpWarnHashtagAsciiAndNonAscii(App1.pref)) {
|
||||
if (!bConfirmTagCharacter && PrefB.bpWarnHashtagAsciiAndNonAscii(App1.pref)) {
|
||||
val tags = TootTag.findHashtags(content, account.isMisskey)
|
||||
val badTags = tags
|
||||
?.filter {
|
||||
|
@ -565,7 +564,7 @@ class PostImpl(
|
|||
|
||||
val requestBuilder = bodyString.toRequestBody(MEDIA_TYPE_JSON).toPost()
|
||||
|
||||
if (!Pref.bpDontDuplicationCheck(App1.pref)) {
|
||||
if (!PrefB.bpDontDuplicationCheck(App1.pref)) {
|
||||
val digest = (bodyString + account.acct.ascii).digestSHA256Hex()
|
||||
requestBuilder.header("Idempotency-Key", digest)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import android.content.Intent
|
|||
import androidx.annotation.StringRes
|
||||
import jp.juggler.subwaytooter.ActText
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
|
@ -302,7 +302,7 @@ object TootTextEncoder {
|
|||
addHeader(context, sb, R.string.send_header_account_created_at, who.created_at)
|
||||
addHeader(context, sb, R.string.send_header_account_statuses_count, who.statuses_count)
|
||||
|
||||
if (!Pref.bpHideFollowCount(App1.getAppState(context).pref)) {
|
||||
if (!PrefB.bpHideFollowCount(App1.getAppState(context).pref)) {
|
||||
addHeader(
|
||||
context,
|
||||
sb,
|
||||
|
|
|
@ -23,7 +23,7 @@ import com.bumptech.glide.load.resource.gif.MyGifDrawable
|
|||
import com.bumptech.glide.request.target.ImageViewTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefB
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.clipRange
|
||||
|
||||
|
@ -80,7 +80,7 @@ class MyNetworkImageView : AppCompatImageView {
|
|||
|
||||
mCornerRadius = r
|
||||
|
||||
val gifUrl = if (Pref.bpEnableGifAnimation(pref)) gifUrlArg else null
|
||||
val gifUrl = if (PrefB.bpEnableGifAnimation(pref)) gifUrlArg else null
|
||||
|
||||
if (gifUrl?.isNotEmpty() == true) {
|
||||
mUrl = gifUrl
|
||||
|
|
|
@ -16,7 +16,7 @@ import android.widget.CompoundButton
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.PrefI
|
||||
import jp.juggler.subwaytooter.R
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import kotlin.math.pow
|
||||
|
@ -109,8 +109,8 @@ fun Context.setSwitchColor(
|
|||
root: View?
|
||||
) {
|
||||
val colorBg = attrColor(R.attr.colorWindowBackground)
|
||||
val colorOn = Pref.ipSwitchOnColor(pref)
|
||||
val colorOff = /* Pref.ipSwitchOffColor(pref).notZero() ?: */
|
||||
val colorOn = PrefI.ipSwitchOnColor(pref)
|
||||
val colorOff = /* PrefI.ipSwitchOffColor(pref).notZero() ?: */
|
||||
attrColor(android.R.attr.colorPrimary)
|
||||
|
||||
val colorDisabled = mixColor(colorBg, colorOff)
|
||||
|
@ -209,7 +209,7 @@ fun AppCompatActivity.setStatusBarColor(forceDark: Boolean = false) {
|
|||
|
||||
var c = when {
|
||||
forceDark -> Color.BLACK
|
||||
else -> Pref.ipStatusBarColor(App1.pref).notZero()
|
||||
else -> PrefI.ipStatusBarColor(App1.pref).notZero()
|
||||
?: attrColor(R.attr.colorPrimaryDark)
|
||||
}
|
||||
statusBarColor = c or Color.BLACK
|
||||
|
@ -235,7 +235,7 @@ fun AppCompatActivity.setStatusBarColor(forceDark: Boolean = false) {
|
|||
|
||||
c = when {
|
||||
forceDark -> Color.BLACK
|
||||
else -> Pref.ipNavigationBarColor(App1.pref)
|
||||
else -> PrefI.ipNavigationBarColor(App1.pref)
|
||||
}
|
||||
|
||||
if (c != 0) {
|
||||
|
|
Loading…
Reference in New Issue