SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/columnviewholder/ColumnViewHolderLifecycle.kt

323 lines
12 KiB
Kotlin

package jp.juggler.subwaytooter.columnviewholder
import android.view.View
import android.widget.ImageView
import androidx.core.net.toUri
import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.actmain.closePopup
import jp.juggler.subwaytooter.column.*
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.subwaytooter.pref.PrefI
import jp.juggler.subwaytooter.util.endPadding
import jp.juggler.subwaytooter.util.startPadding
import jp.juggler.subwaytooter.view.ListDivider
import jp.juggler.util.coroutine.AppDispatchers
import jp.juggler.util.coroutine.launchMain
import jp.juggler.util.data.notZero
import jp.juggler.util.log.LogCategory
import jp.juggler.util.media.createResizedBitmap
import jp.juggler.util.ui.attrColor
import jp.juggler.util.ui.createRoundDrawable
import jp.juggler.util.ui.isCheckedNoAnime
import jp.juggler.util.ui.vg
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.bottomPadding
import org.jetbrains.anko.topPadding
private val log = LogCategory("ColumnViewHolderLifeCycle")
fun ColumnViewHolder.closeBitmaps() {
try {
ivColumnBackgroundImage.visibility = View.GONE
ivColumnBackgroundImage.setImageDrawable(null)
lastImageBitmap?.recycle()
lastImageBitmap = null
lastImageTask?.cancel()
lastImageTask = null
lastImageUri = null
} catch (ex: Throwable) {
log.e(ex, "closeBitmaps failed.")
}
}
fun ColumnViewHolder.loadBackgroundImage(iv: ImageView, url: String?) {
try {
if (url == null || url.isEmpty() || PrefB.bpDontShowColumnBackgroundImage.value) {
// 指定がないなら閉じる
closeBitmaps()
return
}
if (url == lastImageUri) {
// 今表示してるのと同じ
return
}
// 直前の処理をキャンセルする。Bitmapも破棄する
closeBitmaps()
// ロード開始
lastImageUri = url
val screenW = iv.resources.displayMetrics.widthPixels
val screenH = iv.resources.displayMetrics.heightPixels
// 非同期処理を開始
lastImageTask = launchMain {
val bitmap = try {
withContext(AppDispatchers.IO) {
try {
createResizedBitmap(
activity,
url.toUri(),
when {
screenW > screenH -> screenW
else -> screenH
},
)
} catch (ex: Throwable) {
log.e(ex, "createResizedBitmap failed.")
null
}
}
} catch (ex: Throwable) {
log.w(ex, "loadBackgroundImage failed.")
null
}
if (bitmap != null) {
if (!coroutineContext.isActive || url != lastImageUri) {
bitmap.recycle()
} else {
lastImageBitmap = bitmap
iv.setImageBitmap(lastImageBitmap)
iv.visibility = View.VISIBLE
}
}
}
} catch (ex: Throwable) {
log.e(ex, "loadBackgroundImage failed.")
}
}
fun ColumnViewHolder.onPageDestroy(pageIdx: Int) {
// タブレットモードの場合、onPageCreateより前に呼ばれる
val column = this.column
if (column != null) {
ColumnViewHolder.log.d("onPageDestroy [$pageIdx] ${tvColumnName.text}")
saveScrollPosition()
listView.adapter = null
column.removeColumnViewHolder(this)
this.column = null
}
closeBitmaps()
activity.closePopup()
}
fun ColumnViewHolder.onPageCreate(column: Column, pageIdx: Int, pageCount: Int) {
bindingBusy = true
try {
this.column = column
this.pageIdx = pageIdx
ColumnViewHolder.log.d("onPageCreate [$pageIdx] ${column.getColumnName(true)}")
val bSimpleList = !column.isConversation && PrefB.bpSimpleList.value
tvColumnIndex.text = activity.getString(R.string.column_index, pageIdx + 1, pageCount)
tvColumnStatus.text = "?"
ivColumnIcon.setImageResource(column.getIconId())
listView.adapter = null
if (listView.itemDecorationCount == 0) {
listView.addItemDecoration(ListDivider(activity))
}
val statusAdapter = ItemListAdapter(activity, column, this, bSimpleList)
this.statusAdapter = statusAdapter
val isNotificationColumn = column.isNotificationColumn
// 添付メディアや正規表現のフィルタ
val bAllowFilter = column.canStatusFilter()
showColumnSetting(false)
for (invalidator in emojiQueryInvalidatorList) {
invalidator.register(null)
}
emojiQueryInvalidatorList.clear()
for (invalidator in extraInvalidatorList) {
invalidator.register(null)
}
extraInvalidatorList.clear()
cbDontCloseColumn.isCheckedNoAnime = column.dontClose
cbShowMediaDescription.isCheckedNoAnime = column.showMediaDescription
cbRemoteOnly.isCheckedNoAnime = column.remoteOnly
cbWithAttachment.isCheckedNoAnime = column.withAttachment
cbWithHighlight.isCheckedNoAnime = column.withHighlight
cbDontShowBoost.isCheckedNoAnime = column.dontShowBoost
cbDontShowFollow.isCheckedNoAnime = column.dontShowFollow
cbDontShowFavourite.isCheckedNoAnime = column.dontShowFavourite
cbDontShowReply.isCheckedNoAnime = column.dontShowReply
cbDontShowReaction.isCheckedNoAnime = column.dontShowReaction
cbDontShowVote.isCheckedNoAnime = column.dontShowVote
cbDontShowNormalToot.isCheckedNoAnime = column.dontShowNormalToot
cbDontShowNonPublicToot.isCheckedNoAnime = column.dontShowNonPublicToot
cbInstanceLocal.isCheckedNoAnime = column.instanceLocal
cbDontStreaming.isCheckedNoAnime = column.dontStreaming
cbDontAutoRefresh.isCheckedNoAnime = column.dontAutoRefresh
cbHideMediaDefault.isCheckedNoAnime = column.hideMediaDefault
cbSystemNotificationNotRelated.isCheckedNoAnime = column.systemNotificationNotRelated
cbEnableSpeech.isCheckedNoAnime = column.enableSpeech
cbOldApi.isCheckedNoAnime = column.useOldApi
etRegexFilter.setText(column.regexText)
etSearch.setText(column.searchQuery)
cbResolve.isCheckedNoAnime = column.searchResolve
cbRemoteOnly.vg(column.canRemoteOnly())
cbWithAttachment.vg(bAllowFilter)
cbWithHighlight.vg(bAllowFilter)
etRegexFilter.vg(bAllowFilter)
llRegexFilter.vg(bAllowFilter)
btnLanguageFilter.vg(bAllowFilter)
cbDontShowBoost.vg(column.canFilterBoost())
cbDontShowReply.vg(column.canFilterReply())
cbDontShowNormalToot.vg(column.canFilterNormalToot())
cbDontShowNonPublicToot.vg(column.canFilterNonPublicToot())
cbDontShowReaction.vg(isNotificationColumn && column.isMisskey)
cbDontShowVote.vg(isNotificationColumn)
cbDontShowFavourite.vg(isNotificationColumn && !column.isMisskey)
cbDontShowFollow.vg(isNotificationColumn)
cbInstanceLocal.vg(column.type == ColumnType.HASHTAG)
cbDontStreaming.vg(column.canStreamingType())
cbDontAutoRefresh.vg(column.canAutoRefresh())
cbHideMediaDefault.vg(column.canNSFWDefault())
cbSystemNotificationNotRelated.vg(column.isNotificationColumn)
cbEnableSpeech.vg(column.canSpeech())
cbOldApi.vg(column.type == ColumnType.DIRECT_MESSAGES)
btnDeleteNotification.vg(column.isNotificationColumn)
when {
column.isSearchColumn -> {
llSearch.vg(true)
flEmoji.vg(false)
tvEmojiDesc.vg(false)
btnEmojiAdd.vg(false)
etSearch.vg(true)
btnSearchClear.vg(PrefB.bpShowSearchClear.value)
cbResolve.vg(column.type == ColumnType.SEARCH)
}
column.type == ColumnType.REACTIONS && column.accessInfo.isMastodon -> {
llSearch.vg(true)
flEmoji.vg(true)
tvEmojiDesc.vg(true)
btnEmojiAdd.vg(true)
etSearch.vg(false)
btnSearchClear.vg(false)
cbResolve.vg(false)
}
else -> llSearch.vg(false)
}
llListList.vg(column.type == ColumnType.LIST_LIST)
llHashtagExtra.vg(column.hasHashtagExtra)
etHashtagExtraAny.setText(column.hashtagAny)
etHashtagExtraAll.setText(column.hashtagAll)
etHashtagExtraNone.setText(column.hashtagNone)
// tvRegexFilterErrorの表示を更新
if (bAllowFilter) {
isRegexValid()
}
val canRefreshTop = column.canRefreshTopBySwipe()
val canRefreshBottom = column.canRefreshBottomBySwipe()
refreshLayout.isEnabled = canRefreshTop || canRefreshBottom
refreshLayout.direction = if (canRefreshTop && canRefreshBottom) {
SwipyRefreshLayoutDirection.BOTH
} else if (canRefreshTop) {
SwipyRefreshLayoutDirection.TOP
} else {
SwipyRefreshLayoutDirection.BOTTOM
}
bRefreshErrorWillShown = false
llRefreshError.clearAnimation()
llRefreshError.visibility = View.GONE
//
listView.adapter = statusAdapter
//XXX FastScrollerのサポートを諦める。ライブラリはいくつかあるんだけど、設定でON/OFFできなかったり頭文字バブルを無効にできなかったり
// listView.isFastScrollEnabled = ! PrefB.bpDisableFastScroller(Pref.pref(activity))
column.addColumnViewHolder(this)
lastAnnouncementShown = -1L
fun dip(dp: Int): Int = (activity.density * dp + 0.5f).toInt()
val context = activity
val announcementsBgColor = PrefI.ipAnnouncementsBgColor.value.notZero()
?: context.attrColor(R.attr.colorSearchFormBackground)
btnAnnouncementsCutout.apply {
color = announcementsBgColor
}
llAnnouncementsBox.apply {
background = createRoundDrawable(dip(6).toFloat(), announcementsBgColor)
val padV = dip(2)
setPadding(0, padV, 0, padV)
}
val searchBgColor = PrefI.ipSearchBgColor.value.notZero()
?: context.attrColor(R.attr.colorSearchFormBackground)
llSearch.apply {
backgroundColor = searchBgColor
startPadding = dip(12)
endPadding = dip(12)
topPadding = dip(3)
bottomPadding = dip(3)
}
llListList.apply {
backgroundColor = searchBgColor
startPadding = dip(12)
endPadding = dip(12)
topPadding = dip(3)
bottomPadding = dip(3)
}
showColumnColor()
showContent(reason = "onPageCreate", reset = true)
} finally {
bindingBusy = false
}
}