SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainActions.kt

260 lines
9.4 KiB
Kotlin

package jp.juggler.subwaytooter.actmain
import android.app.AlertDialog
import android.text.Spannable
import android.view.View
import android.widget.TextView
import androidx.core.view.GravityCompat
import androidx.work.WorkManager
import jp.juggler.subwaytooter.ActMain
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.action.openColumnList
import jp.juggler.subwaytooter.action.openPost
import jp.juggler.subwaytooter.api.entity.TootAccountRef
import jp.juggler.subwaytooter.api.entity.TootTag.Companion.findHashtagFromUrl
import jp.juggler.subwaytooter.appsetting.appSettingRoot
import jp.juggler.subwaytooter.column.Column
import jp.juggler.subwaytooter.columnviewholder.ColumnViewHolder
import jp.juggler.subwaytooter.columnviewholder.TabletColumnViewHolder
import jp.juggler.subwaytooter.columnviewholder.ViewHolderHeaderBase
import jp.juggler.subwaytooter.columnviewholder.ViewHolderItem
import jp.juggler.subwaytooter.dialog.actionsDialog
import jp.juggler.subwaytooter.itemviewholder.ItemViewHolder
import jp.juggler.subwaytooter.pref.*
import jp.juggler.subwaytooter.push.PushWorker
import jp.juggler.subwaytooter.push.pushRepo
import jp.juggler.subwaytooter.span.MyClickableSpan
import jp.juggler.subwaytooter.util.checkPrivacyPolicy
import jp.juggler.subwaytooter.util.openCustomTab
import jp.juggler.util.coroutine.launchAndShowError
import jp.juggler.util.data.addTo
import jp.juggler.util.data.cast
import jp.juggler.util.data.notEmpty
import jp.juggler.util.log.LogCategory
import jp.juggler.util.log.showToast
import jp.juggler.util.ui.dismissSafe
import kotlinx.coroutines.suspendCancellableCoroutine
import java.util.concurrent.TimeUnit
private val log = LogCategory("ActMainActions")
fun ActMain.onBackPressedImpl() {
launchAndShowError {
// メニューが開いていたら閉じる
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START)
return@launchAndShowError
}
// カラムが0個ならアプリを終了する
if (appState.columnCount == 0) {
finish()
return@launchAndShowError
}
// カラム設定が開いているならカラム設定を閉じる
if (closeColumnSetting()) {
return@launchAndShowError
}
fun getClosableColumnList(): List<Column> {
val visibleColumnList = ArrayList<Column>()
phoneTab({ env ->
try {
appState.column(env.pager.currentItem)?.addTo(visibleColumnList)
} catch (ex: Throwable) {
log.e(ex, "getClosableColumnList failed.")
}
}, { env ->
visibleColumnList.addAll(env.visibleColumns)
})
return visibleColumnList.filter { !it.dontClose }
}
// カラムが1個以上ある場合は設定に合わせて挙動を変える
when (PrefI.ipBackButtonAction.value) {
PrefI.BACK_EXIT_APP -> finish()
PrefI.BACK_OPEN_COLUMN_LIST -> openColumnList()
PrefI.BACK_CLOSE_COLUMN -> {
val closeableColumnList = getClosableColumnList()
when (closeableColumnList.size) {
0 -> when {
PrefB.bpExitAppWhenCloseProtectedColumn.value &&
PrefB.bpDontConfirmBeforeCloseColumn.value ->
finish()
else -> showToast(false, R.string.missing_closeable_column)
}
1 -> closeColumn(closeableColumnList.first())
else -> showToast(
false,
R.string.cant_close_column_by_back_button_when_multiple_column_shown
)
}
}
/* PrefI.BACK_ASK_ALWAYS */
else -> actionsDialog {
val closeableColumnList = getClosableColumnList()
if (closeableColumnList.size == 1) {
val column = closeableColumnList.first()
action(getString(R.string.close_column)) {
closeColumn(column, bConfirmed = true)
}
}
action(getString(R.string.open_column_list)) { openColumnList() }
action(getString(R.string.app_exit)) { finish() }
}
}
}
}
fun ActMain.onClickImpl(v: View) {
when (v.id) {
R.id.btnToot -> openPost()
R.id.btnQuickToot -> performQuickPost(null)
R.id.btnQuickTootMenu -> toggleQuickPostMenu()
R.id.btnMenu -> if (!drawer.isDrawerOpen(GravityCompat.START)) {
drawer.openDrawer(GravityCompat.START)
}
}
}
fun ActMain.onMyClickableSpanClickedImpl(viewClicked: View, span: MyClickableSpan) {
// ビュー階層を下から辿って文脈を取得する
var column: Column? = null
var whoRef: TootAccountRef? = null
var view = viewClicked
loop@ while (true) {
when (val tag = view.tag) {
is ItemViewHolder -> {
column = tag.column
whoRef = tag.getAccount()
break@loop
}
is ViewHolderItem -> {
column = tag.ivh.column
whoRef = tag.ivh.getAccount()
break@loop
}
is ColumnViewHolder -> {
column = tag.column
whoRef = null
break@loop
}
is ViewHolderHeaderBase -> {
column = tag.column
whoRef = tag.getAccount()
break@loop
}
is TabletColumnViewHolder -> {
column = tag.columnViewHolder.column
break@loop
}
else -> when (val parent = view.parent) {
is View -> view = parent
else -> break@loop
}
}
}
val hashtagList = ArrayList<String>().apply {
try {
val cs = viewClicked.cast<TextView>()?.text
if (cs is Spannable) {
for (s in cs.getSpans(0, cs.length, MyClickableSpan::class.java)) {
val li = s.linkInfo
val pair = li.url.findHashtagFromUrl()
if (pair != null) add(if (li.text.startsWith('#')) li.text else "#${pair.first}")
}
}
} catch (ex: Throwable) {
log.e(ex, "can't create hashtagList")
}
}
val linkInfo = span.linkInfo
openCustomTab(
this,
nextPosition(column),
linkInfo.url,
accessInfo = column?.accessInfo,
tagList = hashtagList.notEmpty(),
whoRef = whoRef,
linkInfo = linkInfo
)
}
suspend fun ActMain.themeDefaultChangedDialog() {
val lpThemeDefaultChangedWarnTime = PrefL.lpThemeDefaultChangedWarnTime
val ipUiTheme = PrefI.ipUiTheme
val now = System.currentTimeMillis()
// テーマが未定義でなければ警告しない
if (lazyPref.getInt(ipUiTheme.key, -1) != -1) {
log.i("themeDefaultChangedDialog: theme was set.")
return
}
// 頻繁には警告しない
if (now - lpThemeDefaultChangedWarnTime.value < TimeUnit.DAYS.toMillis(60L)) {
log.i("themeDefaultChangedDialog: avoid frequently check.")
return
}
lpThemeDefaultChangedWarnTime.value = now
// 色がすべてデフォルトなら警告不要
val customizedKeys = ArrayList<String>()
appSettingRoot.items.find { it.caption == R.string.color }?.scan { item ->
item.pref?.let { p ->
when {
p == PrefS.spBoostAlpha -> Unit
p.hasNonDefaultValue() -> customizedKeys.add(p.key)
}
}
}
if (customizedKeys.isEmpty()) {
ipUiTheme.value = ipUiTheme.defVal
return
}
log.w("themeDefaultChangedDialog: customizedKeys=${customizedKeys.joinToString(",")}")
suspendCancellableCoroutine { cont ->
val dialog = AlertDialog.Builder(this)
.setMessage(R.string.color_theme_changed)
.setPositiveButton(android.R.string.ok, null)
.setOnDismissListener {
if (cont.isActive) cont.resume(Unit) {}
}
.create()
cont.invokeOnCancellation { dialog.dismissSafe() }
dialog.show()
}
}
fun ActMain.launchDialogs() {
launchAndShowError {
// プライバシーポリシー
val agreed = try {
checkPrivacyPolicy()
} catch (ex: Throwable) {
log.e(ex, "checkPrivacyPolicy failed.")
return@launchAndShowError
}
// 同意がないなら残りの何かは表示しない
if (!agreed) return@launchAndShowError
// テーマ告知
themeDefaultChangedDialog()
// 通知権限の確認
if(!prNotification.checkOrLaunch()) return@launchAndShowError
// Workの掃除
WorkManager.getInstance(applicationContext).pruneWork()
// 定期的にendpointを再登録したい
PushWorker.enqueueRegisterEndpoint(applicationContext, keepAliveMode = true)
}
}