- 「アプリ設定/挙動/Webブラウザ」を除去

- ST以外のアプリでブラウザを開く実装を諦める。アプリから開いたのにアプリが起動されたらアプリ選択ダイアログを明示的に表示する。
- JDKをazul-14 に戻す。IDEが警告を出すけどこっちのほうが安定してる
- kotlin 1.5.31
- okhttp3:mockwebserver の推移的な依存関係の除外。
This commit is contained in:
tateisu 2021-11-06 11:11:28 +09:00
parent eb48bbe7b8
commit 18c4245a31
10 changed files with 224 additions and 149 deletions

View File

@ -3,11 +3,11 @@
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8">
<module name="apng" target="1.7" />
<module name="SubwayTooter.apng_android" target="11" />
<module name="SubwayTooter.app" target="11" />
<module name="SubwayTooter.colorpicker" target="11" />
<module name="SubwayTooter.emoji" target="11" />
<module name="SubwayTooter.sample_apng" target="11" />
<module name="SubwayTooter.apng_android" target="14" />
<module name="SubwayTooter.app" target="14" />
<module name="SubwayTooter.colorpicker" target="14" />
<module name="SubwayTooter.emoji" target="14" />
<module name="SubwayTooter.sample_apng" target="14" />
</bytecodeTargetLevel>
</component>
</project>

View File

@ -7,7 +7,7 @@
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="Embedded JDK" />
<option name="gradleJvm" value="azul-14" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

View File

@ -26,6 +26,17 @@ android {
}
}
kotlinOptions {
jvmTarget = jvm_target
useIR = true
freeCompilerArgs += [
"-Xopt-in=kotlin.ExperimentalStdlibApi",
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-Xopt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-Xopt-in=androidx.compose.animation.ExperimentalAnimationApi",
]
}
}
repositories {

View File

@ -30,7 +30,7 @@ android {
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = jvm_target
useIR = true
freeCompilerArgs += [
"-Xopt-in=kotlin.ExperimentalStdlibApi",
@ -179,8 +179,22 @@ dependencies {
def okhttpVersion = "4.9.2"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
implementation "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
testImplementation "com.squareup.okhttp3:mockwebserver:$okhttpVersion"
androidTestImplementation "com.squareup.okhttp3:mockwebserver:$okhttpVersion"
testImplementation("com.squareup.okhttp3:mockwebserver:$okhttpVersion") {
exclude group: 'com.squareup.okio', module: 'okio'
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common'
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
}
androidTestImplementation("com.squareup.okhttp3:mockwebserver:$okhttpVersion") {
exclude group: 'com.squareup.okio', module: 'okio'
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common'
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
}
def glideVersion = '4.12.0'
implementation "com.github.bumptech.glide:glide:$glideVersion"

View File

@ -7,6 +7,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import jp.juggler.util.LogCategory
import jp.juggler.util.digestSHA256Hex
import jp.juggler.util.showToast
import okhttp3.internal.toHexString
import org.apache.commons.io.IOUtils
import java.io.File
@ -29,6 +30,20 @@ class ActCallback : AppCompatActivity() {
this.startsWith("audio/") -> true
else -> false
}
@Volatile
private var uriFromApp: Uri? = null
fun setUriFromApp(uri: Uri?) {
synchronized(this) {
uriFromApp = uri
}
}
fun containsUriFromApp(uri: Uri?) =
synchronized(this) {
uri == uriFromApp
}
}
override fun onCreate(savedInstanceState: Bundle?) {
@ -64,6 +79,11 @@ class ActCallback : AppCompatActivity() {
if (intent != null) {
sent_intent.set(intent)
}
} else if (forbidUriFromApp(intent)) {
// last_uriをクリアする
last_uri.set(null)
// ダイアログを閉じるまで画面遷移しない
return
} else {
val uri = intent.data
if (uri != null) {
@ -74,7 +94,12 @@ class ActCallback : AppCompatActivity() {
}
// どうであれメイン画面に戻る
intent = Intent(this, ActMain::class.java)
afterDispatch()
}
private fun afterDispatch() {
val intent = Intent(this, ActMain::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
@ -220,4 +245,21 @@ class ActCallback : AppCompatActivity() {
log.trace(ex)
}
}
// return true if open app chooser dialog
private fun forbidUriFromApp(src: Intent): Boolean {
if (src.action != Intent.ACTION_VIEW) return false
val uri = src.data ?: return false
if (!containsUriFromApp(uri)) return false
setUriFromApp(null)
try {
startActivity(Intent.createChooser(src, uri.toString()))
finish()
} catch (ex: Throwable) {
showToast(ex, "can't open chooser for $uri")
}
return true
}
}

View File

@ -629,7 +629,7 @@ object PrefS {
val spCustomShare1 = StringPref("CustomShare1", "")
val spCustomShare2 = StringPref("CustomShare2", "")
val spCustomShare3 = StringPref("CustomShare3", "")
val spWebBrowser = StringPref("WebBrowser", "")
// val spWebBrowser = StringPref("WebBrowser", "")
val spTimelineSpacing = StringPref("TimelineSpacing", "")
}

View File

@ -257,42 +257,42 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
sw(PrefB.bpDontUseCustomTabs, R.string.dont_use_custom_tabs)
sw(PrefB.bpPriorChrome, R.string.prior_chrome_custom_tabs)
item(
SettingType.TextWithSelector,
PrefS.spWebBrowser,
R.string.web_browser
) {
onClickEdit = {
openWebBrowserChooser(
this@item,
intent = Intent(Intent.ACTION_VIEW, "https://joinmastodon.org/".toUri()).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
addCategory(Intent.CATEGORY_BROWSABLE)
},
filter = {
when {
it.activityInfo.packageName == packageName -> false
!it.activityInfo.exported -> false
// Huaweiの謎Activityのせいでうまく働かないことがある
-1 != it.activityInfo.packageName.indexOf("com.huawei.android.internal") -> false
// 標準アプリが設定されていない場合、アプリを選択するためのActivityが出てくる場合がある
it.activityInfo.packageName == "android" -> false
it.activityInfo.javaClass.name.startsWith("com.android.internal") -> false
it.activityInfo.javaClass.name.startsWith("com.android.systemui") -> false
// たぶんChromeとかfirefoxとか
else -> true
}
}
)
}
onClickReset = { setWebBrowser(this@item, "") }
showTextView = {
showWebBrowser(it, this@item.pref.cast<StringPref>()!!.invoke(pref))
}
}
// item(
// SettingType.TextWithSelector,
// PrefS.spWebBrowser,
// R.string.web_browser
// ) {
// onClickEdit = {
// openWebBrowserChooser(
// this@item,
// intent = Intent(Intent.ACTION_VIEW, "https://joinmastodon.org/".toUri()).apply {
// addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// addCategory(Intent.CATEGORY_BROWSABLE)
// },
// filter = {
// when {
// it.activityInfo.packageName == packageName -> false
// !it.activityInfo.exported -> false
//
// // Huaweiの謎Activityのせいでうまく働かないことがある
// -1 != it.activityInfo.packageName.indexOf("com.huawei.android.internal") -> false
//
// // 標準アプリが設定されていない場合、アプリを選択するためのActivityが出てくる場合がある
// it.activityInfo.packageName == "android" -> false
// it.activityInfo.javaClass.name.startsWith("com.android.internal") -> false
// it.activityInfo.javaClass.name.startsWith("com.android.systemui") -> false
//
// // たぶんChromeとかfirefoxとか
// else -> true
// }
// }
// )
// }
// onClickReset = { setWebBrowser(this@item, "") }
// showTextView = {
// showWebBrowser(it, this@item.pref.cast<StringPref>()!!.invoke(pref))
// }
// }
sw(PrefB.bpAllowColumnDuplication, R.string.allow_column_duplication)
sw(PrefB.bpForceGap, R.string.force_gap_when_refresh)

View File

@ -4,19 +4,17 @@ import android.app.Activity
import android.content.ComponentName
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
import jp.juggler.subwaytooter.*
import jp.juggler.subwaytooter.action.*
import jp.juggler.subwaytooter.action.conversationLocal
import jp.juggler.subwaytooter.action.conversationOtherInstance
import jp.juggler.subwaytooter.action.tagDialog
import jp.juggler.subwaytooter.action.userProfile
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.span.LinkInfo
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.util.*
@ -30,97 +28,95 @@ import java.util.*
private val log = LogCategory("AppOpener")
// returns true if activity is opened.
// returns false if fallback required
private fun Activity.openBrowserExcludeMe(
pref: SharedPreferences,
intent: Intent,
startAnimationBundle: Bundle? = null
): Boolean {
// 例外を出す
private fun Activity.openBrowserExcludeMe(intent: Intent) {
// if (intent.component == null) {
// val cn = PrefS.spWebBrowser(pref).cn()
// if (cn?.exists(this) == true) {
// intent.component = cn
// }
// }
ActCallback.setUriFromApp(intent.data)
startActivity(intent)
//
// // このアプリのパッケージ名
// val myName = packageName
//
// val filter: (ResolveInfo) -> Boolean = {
// when {
// it.activityInfo.packageName == myName -> false
// !it.activityInfo.exported -> false
//
// // Huaweiの謎Activityのせいでうまく働かないことがある
// -1 != it.activityInfo.packageName.indexOf("com.huawei.android.internal") -> false
//
// // 標準アプリが設定されていない場合、アプリを選択するためのActivityが出てくる場合がある
// it.activityInfo.packageName == "android" -> false
// it.activityInfo.javaClass.name.startsWith("com.android.internal") -> false
// it.activityInfo.javaClass.name.startsWith("com.android.systemui") -> false
//
// // たぶんChromeとかfirefoxとか
// else -> true
// }
// }
//
// // resolveActivity がこのアプリ以外のActivityを返すなら、それがベストなんだろう
// // ただしAndroid M以降はMATCH_DEFAULT_ONLYだと「常時」が設定されてないとnullを返す
// val ri = packageManager!!.resolveActivity(
// intent,
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// PackageManager.MATCH_ALL
// } else {
// PackageManager.MATCH_DEFAULT_ONLY
// }
// )?.takeIf(filter)
//
// return when {
//
// ri != null -> {
// intent.setClassName(ri.activityInfo.packageName, ri.activityInfo.name)
// log.d("startActivityExcludeMyApp(1) $intent")
// startActivity(intent, startAnimationBundle)
// true
// }
//
// else -> DlgAppPicker(
// this,
// intent,
// autoSelect = true,
// filter = filter,
// addCopyAction = false
// ) {
// try {
// intent.component = it.cn()
// log.d("startActivityExcludeMyApp(2) $intent")
// 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}")
// }
}
fun Activity.openBrowser(uri: Uri?) {
uri ?: return
try {
if (intent.component == null) {
val cn = PrefS.spWebBrowser(pref).cn()
if (cn?.exists(this) == true) {
intent.component = cn
}
}
// このアプリのパッケージ名
val myName = packageName
val filter: (ResolveInfo) -> Boolean = {
when {
it.activityInfo.packageName == myName -> false
!it.activityInfo.exported -> false
// Huaweiの謎Activityのせいでうまく働かないことがある
-1 != it.activityInfo.packageName.indexOf("com.huawei.android.internal") -> false
// 標準アプリが設定されていない場合、アプリを選択するためのActivityが出てくる場合がある
it.activityInfo.packageName == "android" -> false
it.activityInfo.javaClass.name.startsWith("com.android.internal") -> false
it.activityInfo.javaClass.name.startsWith("com.android.systemui") -> false
// たぶんChromeとかfirefoxとか
else -> true
}
}
// resolveActivity がこのアプリ以外のActivityを返すなら、それがベストなんだろう
// ただしAndroid M以降はMATCH_DEFAULT_ONLYだと「常時」が設定されてないとnullを返す
val ri = packageManager!!.resolveActivity(
intent,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PackageManager.MATCH_ALL
} else {
PackageManager.MATCH_DEFAULT_ONLY
}
)?.takeIf(filter)
return when {
ri != null -> {
intent.setClassName(ri.activityInfo.packageName, ri.activityInfo.name)
log.d("startActivityExcludeMyApp(1) $intent")
startActivity(intent, startAnimationBundle)
true
}
else -> DlgAppPicker(
this,
intent,
autoSelect = true,
filter = filter,
addCopyAction = false
) {
try {
intent.component = it.cn()
log.d("startActivityExcludeMyApp(2) $intent")
startActivity(intent, startAnimationBundle)
} catch (ex: Throwable) {
log.trace(ex)
showToast(ex, "can't open. ${intent.data}")
}
}.show()
}
openBrowserExcludeMe(
Intent(Intent.ACTION_VIEW, uri).apply { addCategory(Intent.CATEGORY_BROWSABLE) }
)
} 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?, pref: SharedPreferences = pref()) {
uri ?: return
val rv = openBrowserExcludeMe(
pref,
Intent(Intent.ACTION_VIEW, uri)
.apply { addCategory(Intent.CATEGORY_BROWSABLE) }
)
if (!rv) showToast(true, "there is no app that can open $uri")
}
fun Activity.openBrowser(url: String?, pref: SharedPreferences = pref()) = openBrowser(url.mayUri(), pref)
fun Activity.openBrowser(url: String?) =
openBrowser(url.mayUri())
// Chrome Custom Tab を開く
fun Activity.openCustomTab(url: String?, pref: SharedPreferences = pref()) {
@ -132,11 +128,12 @@ fun Activity.openCustomTab(url: String?, pref: SharedPreferences = pref()) {
}
if (PrefB.bpDontUseCustomTabs(pref)) {
openBrowser(url, pref)
openBrowser(url)
return
}
try {
// 例外を出す
fun startCustomTabIntent(cn: ComponentName?) =
CustomTabsIntent.Builder()
.setDefaultColorSchemeParams(
@ -149,12 +146,10 @@ fun Activity.openCustomTab(url: String?, pref: SharedPreferences = pref()) {
.let {
log.w("startCustomTabIntent ComponentName=$cn")
openBrowserExcludeMe(
pref,
it.intent.also { intent ->
if (cn != null) intent.component = cn
intent.data = url.toUri()
},
it.startAnimationBundle
)
}
@ -165,15 +160,14 @@ fun Activity.openCustomTab(url: String?, pref: SharedPreferences = pref()) {
"com.android.chrome",
"com.google.android.apps.chrome.Main"
)
if (startCustomTabIntent(cn)) return
startCustomTabIntent(cn)
} catch (ex2: Throwable) {
log.e(ex2, "openCustomTab: missing chrome. retry to other application.")
}
}
// Chromeがないようなのでcomponent指定なしでリトライ
if (startCustomTabIntent(null)) return
showToast(true, "the browser app is not installed.")
startCustomTabIntent(null)
} catch (ex: Throwable) {
log.trace(ex)
val scheme = url.mayUri()?.scheme ?: url

View File

@ -1,20 +1,22 @@
buildscript {
ext.jvm_target = "14"
ext.min_sdk_version = 21
ext.target_sdk_version = 31
ext.compile_sdk_version = 31
ext.appcompat_version='1.3.1'
ext.lifecycle_version="2.4.0-rc01"
ext.appcompat_version = '1.3.1'
ext.lifecycle_version = "2.4.0-rc01"
ext.arch_version = "2.1.0"
ext.kotlin_version = '1.5.30'
ext.kotlin_version = '1.5.31'
ext.kotlinx_coroutines_version = '1.5.2'
ext.anko_version='0.10.8'
ext.anko_version = '0.10.8'
ext.junit_version='4.13.2'
ext.junit_version = '4.13.2'
ext.detekt_version='1.18.1'
ext.detekt_version = '1.18.1'
ext.compose_version = '1.0.4'

View File

@ -32,6 +32,18 @@ android {
// https://github.com/Kotlin/kotlinx.coroutines/issues/1064
pickFirst("META-INF/atomicfu.kotlin_module")
}
kotlinOptions {
jvmTarget = jvm_target
useIR = true
freeCompilerArgs += [
"-Xopt-in=kotlin.ExperimentalStdlibApi",
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-Xopt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-Xopt-in=androidx.compose.animation.ExperimentalAnimationApi",
]
}
}
dependencies {