SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/util/AppOpener.kt

164 lines
5.0 KiB
Kotlin

package jp.juggler.subwaytooter.util
import android.content.ComponentName
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.app.Activity
import androidx.browser.customtabs.CustomTabsIntent
import jp.juggler.subwaytooter.Pref
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.api.entity.TootAttachment
import jp.juggler.subwaytooter.dialog.DlgAppPicker
import jp.juggler.subwaytooter.pref
import jp.juggler.util.*
private val log = LogCategory("AppOpener")
// returns true if activity is opened.
// returns false if fallback required
private fun Activity.startActivityExcludeMyApp(
intent : Intent,
startAnimationBundle : Bundle? = null
) : Boolean {
try {
val pm = packageManager !!
val myName = packageName
val filter : (ResolveInfo) -> Boolean = {
it.activityInfo.packageName != myName &&
it.activityInfo.exported &&
- 1 == it.activityInfo.packageName.indexOf("com.huawei.android.internal")
}
// resolveActivity がこのアプリ以外のActivityを返すなら、それがベストなんだろう
// ただしAndroid M以降はMATCH_DEFAULT_ONLYだと「常時」が設定されてないとnullを返す
val ri = pm.resolveActivity(
intent,
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PackageManager.MATCH_ALL
} else {
PackageManager.MATCH_DEFAULT_ONLY
}
)?.takeIf(filter)
if(ri != null) {
intent.setClassName(ri.activityInfo.packageName, ri.activityInfo.name)
startActivity(intent, startAnimationBundle)
return true
}
return DlgAppPicker(
this,
intent,
autoSelect = true,
filter = filter
) {
try {
intent.component = it.cn()
startActivity(intent, startAnimationBundle)
} catch(ex : Throwable) {
log.trace(ex)
showToast(ex, "can't open. ${intent.data}")
}
}.show()
} catch(ex : Throwable) {
log.trace(ex)
showToast(ex, "can't open. ${intent.data}")
return true // fallback not required in this case
}
}
fun Activity.openBrowser(uri : Uri?) {
if(uri != null) {
val rv = startActivityExcludeMyApp(Intent(Intent.ACTION_VIEW, uri))
if(! rv) showToast(true, "there is no app that can open $uri")
}
}
fun Activity.openBrowser(url : String?) = openBrowser(url.mayUri())
// ubway Tooterの「アプリ設定/挙動/リンクを開く際にCustom Tabsを使わない」をONにして
// 投稿のコンテキストメニューの「トゥートへのアクション/Webページを開く」「ユーザへのアクション/Webページを開く」を使うと
// 投げたインテントをST自身が受け取って「次のアカウントから開く」ダイアログが出て
// 「Webページを開く」をまた押すと無限ループしてダイアログの影が徐々に濃くなりそのうち壊れる
// これを避けるには、投稿やトゥートを開く際に bpDontUseCustomTabs がオンならST以外のアプリを列挙したアプリ選択ダイアログを出すしかない
fun Activity.openCustomTabOrBrowser(url : String?) {
url ?: return
if(! Pref.bpDontUseCustomTabs(pref())) {
openCustomTab(url)
} else {
openBrowser(url)
}
}
// Chrome Custom Tab を開く
fun Activity.openCustomTab(url : String?) {
url ?: return
if(url.isEmpty()) {
showToast(false, "URL is empty string.")
return
}
val pref = pref()
if(Pref.bpDontUseCustomTabs(pref)) {
openCustomTabOrBrowser(url)
return
}
try {
if(url.startsWith("http") && Pref.bpPriorChrome(pref)) {
try {
// 初回はChrome指定で試す
val customTabsIntent = CustomTabsIntent.Builder()
.setToolbarColor(getAttributeColor(R.attr.colorPrimary))
.setShowTitle(true)
.build()
val rv = startActivityExcludeMyApp(
customTabsIntent.intent.also {
it.component = ComponentName(
"com.android.chrome",
"com.google.android.apps.chrome.Main"
)
it.data = url.toUri()
},
customTabsIntent.startAnimationBundle
)
if(rv) return
} catch(ex2 : Throwable) {
log.e(ex2, "openChromeTab: missing chrome. retry to other application.")
}
}
// Chromeがないようなのでcomponent指定なしでリトライ
val customTabsIntent = CustomTabsIntent.Builder()
.setToolbarColor(getAttributeColor(R.attr.colorPrimary))
.setShowTitle(true)
.build()
val rv = startActivityExcludeMyApp(
customTabsIntent.intent.also {
it.data = url.toUri()
},
customTabsIntent.startAnimationBundle
)
if(! rv) {
showToast(true, "the browser app is not installed.")
}
} catch(ex : Throwable) {
log.trace(ex)
val scheme = url.mayUri()?.scheme ?: url
showToast(true, "can't open browser app for %s", scheme)
}
}
fun Activity.openCustomTab(ta : TootAttachment) =
openCustomTab(ta.getLargeUrl(pref()))