fix 「アプリ設定/挙動/リンクを開く際にCustom Tabsを使わない」をONにしてから投稿のコンテキストメニューの「トゥートへのアクション/Webページを開く」「ユーザへのアクション/Webページを開く」を使うと投げたインテントをST自身が受け取って「次のアカウントから開く」ダイアログが出て「Webページを開く」をまた押すと無限ループ

This commit is contained in:
tateisu 2020-02-14 23:16:07 +09:00
parent f1f2400a51
commit 24a64054dd
5 changed files with 183 additions and 103 deletions

View File

@ -102,11 +102,11 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
initUi()
removeDefaultPref()
load(null, null)
}
private fun initUi(){
private fun initUi() {
setContentView(R.layout.act_app_setting)
App1.initEdgeToEdge(this)
@ -147,11 +147,11 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
}
}
private fun removeDefaultPref(){
private fun removeDefaultPref() {
val e = pref.edit()
var changed = false
appSettingRoot.scan{
if( it.pref?.removeDefault(pref,e) ==true ) changed = true
appSettingRoot.scan {
if(it.pref?.removeDefault(pref, e) == true) changed = true
}
if(changed) e.apply()
}
@ -174,16 +174,18 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
}
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
if(resultCode == RESULT_OK && data != null ){
if(resultCode == RESULT_OK && data != null) {
when(requestCode) {
REQUEST_CODE_APP_DATA_IMPORT -> {
data.handleGetContentResult(contentResolver).firstOrNull()?.uri?.let {
importAppData2(false, it)
}
}
REQUEST_CODE_TIMELINE_FONT -> {
handleFontResult(AppSettingItem.TIMELINE_FONT, data, "TimelineFont")
}
REQUEST_CODE_TIMELINE_FONT_BOLD -> {
handleFontResult(AppSettingItem.TIMELINE_FONT_BOLD, data, "TimelineFontBold")
}
@ -339,7 +341,8 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
// true if the item at the specified position is not a separator.
// (A separator is a non-selectable, non-clickable item).
override fun areAllItemsEnabled() : Boolean =false
override fun areAllItemsEnabled() : Boolean = false
override fun isEnabled(position : Int) : Boolean = list[position] is AppSettingItem
override fun getView(position : Int, convertView : View?, parent : ViewGroup?) : View =
@ -1201,7 +1204,14 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
fun openCustomShareChooser(target : CustomShareTarget) {
try {
DlgAppPicker(this){ setCustomShare(target, it) }.show()
DlgAppPicker(
this,
Intent().apply {
action = Intent.ACTION_SEND
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, getString(R.string.content_sample))
}
) { setCustomShare(target, it) }.show()
} catch(ex : Throwable) {
log.trace(ex)
showToast(this, ex, "openCustomShareChooser failed.")
@ -1229,7 +1239,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
fun showCustomShareIcon(tv : TextView?, target : CustomShareTarget) {
tv ?: return
val cn = CustomShare.getCustomShareComponentName(pref, target)
val (label, icon) =CustomShare.getInfo(this, cn)
val (label, icon) = CustomShare.getInfo(this, cn)
tv.text = label ?: getString(R.string.not_selected)
tv.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null)
tv.compoundDrawablePadding = (resources.displayMetrics.density * 4f + 0.5f).toInt()

View File

@ -7,6 +7,7 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
@ -14,6 +15,7 @@ import android.graphics.Color
import android.graphics.PorterDuff
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.WindowManager
@ -29,8 +31,10 @@ import com.bumptech.glide.load.engine.executor.GlideExecutor
import com.bumptech.glide.load.engine.executor.GlideExecutor.newDiskCacheExecutor
import com.bumptech.glide.load.engine.executor.GlideExecutor.newSourceExecutor
import com.bumptech.glide.load.model.GlideUrl
import jp.juggler.subwaytooter.action.cn
import jp.juggler.subwaytooter.api.TootApiClient
import jp.juggler.subwaytooter.api.entity.TootAttachment
import jp.juggler.subwaytooter.dialog.DlgAppPicker
import jp.juggler.subwaytooter.table.*
import jp.juggler.subwaytooter.util.CustomEmojiCache
import jp.juggler.subwaytooter.util.CustomEmojiLister
@ -59,7 +63,7 @@ class App1 : Application() {
override fun onCreate() {
log.d("onCreate")
super.onCreate()
prepare(applicationContext,"App1.onCreate")
prepare(applicationContext, "App1.onCreate")
}
override fun onTerminate() {
@ -218,7 +222,7 @@ class App1 : Application() {
// return maxSize * 1024;
// }
val reNotAllowedInUserAgent ="[^\\x21-\\x7e]+".asciiPattern()
val reNotAllowedInUserAgent = "[^\\x21-\\x7e]+".asciiPattern()
val userAgentDefault =
"SubwayTooter/${BuildConfig.VERSION_NAME} Android/${Build.VERSION.RELEASE}"
@ -296,7 +300,7 @@ class App1 : Application() {
@SuppressLint("StaticFieldLeak")
lateinit var custom_emoji_lister : CustomEmojiLister
fun prepare(app_context : Context,caller:String) : AppState {
fun prepare(app_context : Context, caller : String) : AppState {
var state = appStateX
if(state != null) return state
@ -407,21 +411,21 @@ class App1 : Application() {
state = AppState(app_context, pref)
appStateX = state
// getAppState()を使える状態にしてからカラム一覧をロードする
log.d("load column list...")
state.loadColumnList()
log.d("prepare() complete! caller=$caller")
return state
}
@SuppressLint("StaticFieldLeak")
private var appStateX : AppState? = null
fun getAppState(context : Context,caller:String="getAppState") : AppState {
return prepare(context.applicationContext,caller)
fun getAppState(context : Context, caller : String = "getAppState") : AppState {
return prepare(context.applicationContext, caller)
}
fun sound(item : HighlightWord) {
@ -508,21 +512,21 @@ class App1 : Application() {
fun setActivityTheme(
activity : Activity,
noActionBar : Boolean = false,
forceDark :Boolean = false
forceDark : Boolean = false
) {
prepare(activity.applicationContext,"setActivityTheme")
prepare(activity.applicationContext, "setActivityTheme")
val theme_idx = Pref.ipUiTheme(pref)
activity.setTheme(
if( forceDark || theme_idx ==1){
if(forceDark || theme_idx == 1) {
if(noActionBar) R.style.AppTheme_Dark_NoActionBar else R.style.AppTheme_Dark
}else{
} else {
if(noActionBar) R.style.AppTheme_Light_NoActionBar else R.style.AppTheme_Light
}
)
setStatusBarColor(activity,forceDark=forceDark)
setStatusBarColor(activity, forceDark = forceDark)
}
internal val CACHE_CONTROL = CacheControl.Builder()
@ -592,59 +596,114 @@ class App1 : Application() {
}
fun openBrowser(context : Context, uri : Uri?) {
private fun startActivityExcludeMyApp(
activity : AppCompatActivity,
intent : Intent,
startAnimationBundle : Bundle? = null
) {
try {
uri ?: return
val intent = Intent(Intent.ACTION_VIEW, uri)
context.startActivity(intent)
val pm = activity.packageManager!!
val flags = PackageManager.MATCH_DEFAULT_ONLY
val ri = pm.resolveActivity(intent, flags)
if(ri != null && ri.activityInfo.packageName != activity.packageName) {
// ST以外が選択された
activity.startActivity(intent, startAnimationBundle)
return
}
DlgAppPicker(
activity,
intent,
autoSelect = true,
filter = {it.activityInfo.packageName != activity.packageName }
) {
try {
intent.component = it.cn()
activity.startActivity(intent, startAnimationBundle)
} catch(ex : Throwable) {
log.trace(ex)
showToast(activity, ex, "can't open. ${intent.data}")
}
}.show()
} catch(ex : Throwable) {
log.trace(ex, "openBrowser")
showToast(context, true, "missing web browser")
log.trace(ex)
showToast(activity, ex, "can't open. ${intent.data}")
}
}
fun openBrowser(context : Context, url : String?) =
openBrowser(context, url.mayUri())
fun openBrowser(activity : AppCompatActivity, uri : Uri?) {
if(uri != null) startActivityExcludeMyApp(activity, Intent(Intent.ACTION_VIEW, uri))
}
fun openBrowser(activity : AppCompatActivity, url : String?) =
openBrowser(activity, url.mayUri())
// ubway Tooterの「アプリ設定/挙動/リンクを開く際にCustom Tabsを使わない」をONにして
// 投稿のコンテキストメニューの「トゥートへのアクション/Webページを開く」「ユーザへのアクション/Webページを開く」を使うと
// 投げたインテントをST自身が受け取って「次のアカウントから開く」ダイアログが出て
// 「Webページを開く」をまた押すと無限ループしてダイアログの影が徐々に濃くなりそのうち壊れる
// これを避けるには、投稿やトゥートを開く際に bpDontUseCustomTabs がオンならST以外のアプリを列挙したアプリ選択ダイアログを出すしかない
fun openCustomTabOrBrowser(activity : AppCompatActivity, url : String) {
if(! Pref.bpDontUseCustomTabs(pref)) {
openCustomTab(activity, url)
} else {
openBrowser(activity, url)
}
}
// Chrome Custom Tab を開く
fun openCustomTab(activity : Activity, url : String) {
fun openCustomTab(activity : AppCompatActivity, url : String) {
if(Pref.bpDontUseCustomTabs(pref)) {
openCustomTabOrBrowser(activity, url)
return
}
try {
if(Pref.bpDontUseCustomTabs(pref)) {
openBrowser(activity, url)
} else {
if(url.startsWith("http") && Pref.bpPriorChrome(pref)) {
try {
// 初回はChrome指定で試す
val customTabsIntent = CustomTabsIntent.Builder()
.setToolbarColor(
getAttributeColor(
activity,
R.attr.colorPrimary
)
if(url.startsWith("http") && Pref.bpPriorChrome(pref)) {
try {
// 初回はChrome指定で試す
val customTabsIntent = CustomTabsIntent.Builder()
.setToolbarColor(
getAttributeColor(
activity,
R.attr.colorPrimary
)
.setShowTitle(true)
.build()
customTabsIntent.intent.component = ComponentName(
"com.android.chrome",
"com.google.android.apps.chrome.Main"
)
customTabsIntent.launchUrl(activity, url.toUri())
return
} catch(ex2 : Throwable) {
log.e(ex2, "openChromeTab: missing chrome. retry to other application.")
}
.setShowTitle(true)
.build()
startActivityExcludeMyApp(
activity,
customTabsIntent.intent.also {
it.component = ComponentName(
"com.android.chrome",
"com.google.android.apps.chrome.Main"
)
it.data = url.toUri()
},
customTabsIntent.startAnimationBundle
)
return
} catch(ex2 : Throwable) {
log.e(ex2, "openChromeTab: missing chrome. retry to other application.")
}
// Chromeがないようなのでcomponent指定なしでリトライ
CustomTabsIntent.Builder()
.setToolbarColor(getAttributeColor(activity, R.attr.colorPrimary))
.setShowTitle(true)
.build()
.launchUrl(activity, url.toUri())
}
// Chromeがないようなのでcomponent指定なしでリトライ
val customTabsIntent = CustomTabsIntent.Builder()
.setToolbarColor(getAttributeColor(activity, R.attr.colorPrimary))
.setShowTitle(true)
.build()
startActivityExcludeMyApp(
activity,
customTabsIntent.intent.also {
it.data = url.toUri()
},
customTabsIntent.startAnimationBundle
)
} catch(ex : Throwable) {
log.trace(ex)
val scheme = url.mayUri()?.scheme ?: url
@ -653,7 +712,7 @@ class App1 : Application() {
}
fun openCustomTab(activity : Activity, ta : TootAttachment) {
fun openCustomTab(activity : AppCompatActivity, ta : TootAttachment) {
val url = ta.getLargeUrl(pref) ?: return
openCustomTab(activity, url)
}
@ -706,14 +765,14 @@ class App1 : Application() {
)
}
fun setStatusBarColor(activity : Activity,forceDark:Boolean=false ) {
fun setStatusBarColor(activity : Activity, forceDark : Boolean = false) {
activity.window?.apply {
// 古い端末ではナビゲーションバーのアイコン色を設定できないため
// メディアビューア画面ではステータスバーやナビゲーションバーの色を設定しない…
if( forceDark && Build.VERSION.SDK_INT < 26 ) return
if(forceDark && Build.VERSION.SDK_INT < 26) return
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
@ -721,13 +780,13 @@ class App1 : Application() {
var c = when {
forceDark -> Color.BLACK
else -> Pref.ipStatusBarColor(pref).notZero()
?: getAttributeColor(activity,R.attr.colorPrimaryDark)
?: getAttributeColor(activity, R.attr.colorPrimaryDark)
}
statusBarColor = c or Color.BLACK
if(Build.VERSION.SDK_INT >= 23) {
decorView.systemUiVisibility =
if( rgbToLab(c).first >= 50f) {
if(rgbToLab(c).first >= 50f) {
//Dark Text to show up on your light status bar
decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
@ -758,7 +817,6 @@ class App1 : Application() {
}
}
fun setSwitchColor1(
activity : AppCompatActivity,
pref : SharedPreferences,

View File

@ -736,7 +736,7 @@ internal class DlgContextMenu(
Action_User.mention(activity, access_info, who)
R.id.btnAccountWebPage -> who.url?.let { url ->
App1.openCustomTab(activity, url)
App1.openCustomTabOrBrowser(activity, url)
}
R.id.btnFollowRequestOK ->
@ -973,7 +973,7 @@ internal class DlgContextMenu(
when(v.id) {
R.id.btnStatusWebPage -> status?.url?.let { url ->
App1.openCustomTab(activity, url)
App1.openCustomTabOrBrowser(activity, url)
}
R.id.btnText -> if(status != null) {

View File

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.graphics.drawable.Drawable
import android.os.Build
import android.view.View
@ -18,12 +19,16 @@ import jp.juggler.util.*
class DlgAppPicker(
val activity : AppCompatActivity,
val intent : Intent,
val autoSelect : Boolean = false,
val filter : (ResolveInfo) -> Boolean = { true },
val callback : (String) -> Unit
) {
companion object{
fun Char.isAlpha() = ('A' <= this && this <= 'Z')||('a' <= this && this <= 'z')
companion object {
fun Char.isAlpha() = ('A' <= this && this <= 'Z') || ('a' <= this && this <= 'z')
}
class ListItem(
val icon : Drawable?,
val text : String,
@ -34,11 +39,7 @@ class DlgAppPicker(
val pm = activity.packageManager
val listResolveInfo = pm.queryIntentActivities(
Intent().apply {
action = Intent.ACTION_SEND
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, activity.getString(R.string.content_sample))
},
intent,
if(Build.VERSION.SDK_INT >= 23) {
PackageManager.MATCH_ALL
} else {
@ -47,6 +48,7 @@ class DlgAppPicker(
)
for(it in listResolveInfo) {
if(! filter(it)) continue
val cn = "${it.activityInfo.packageName}/${it.activityInfo.name}"
add(
ListItem(
@ -57,43 +59,52 @@ class DlgAppPicker(
)
}
val (label, icon) = CustomShare.getInfo(activity, CustomShare.CN_CLIPBOARD.cn())
add(ListItem(icon, label.toString(), CustomShare.CN_CLIPBOARD))
sortWith(Comparator { a, b->
if(! autoSelect) {
val (label, icon) = CustomShare.getInfo(activity, CustomShare.CN_CLIPBOARD.cn())
add(ListItem(icon, label.toString(), CustomShare.CN_CLIPBOARD))
}
sortWith(Comparator { a, b ->
val a1 = a.text.firstOrNull() ?: '\u0000'
val b1 = b.text.firstOrNull() ?: '\u0000'
when {
!a1.isAlpha() && b1.isAlpha() -> -1
a1.isAlpha() && !b1.isAlpha() -> 1
else -> a.text.compareTo(b.text,ignoreCase = true)
! a1.isAlpha() && b1.isAlpha() -> - 1
a1.isAlpha() && ! b1.isAlpha() -> 1
else -> a.text.compareTo(b.text, ignoreCase = true)
}
})
}
val dialog : AlertDialog
val dialog : AlertDialog?
init {
@SuppressLint("InflateParams")
val listView : ListView =
activity.layoutInflater.inflate(R.layout.dlg_app_picker, null, false).cast() !!
val adapter = MyAdapter()
listView.adapter = adapter
listView.onItemClickListener = adapter
dialog = AlertDialog.Builder(activity)
.setView(listView)
.setNegativeButton(R.string.cancel, null)
.create()
if(autoSelect && list.size == 1) {
callback(list.first().componentName)
dialog = null
} else {
@SuppressLint("InflateParams")
val listView : ListView =
activity.layoutInflater.inflate(R.layout.dlg_app_picker, null, false).cast() !!
val adapter = MyAdapter()
listView.adapter = adapter
listView.onItemClickListener = adapter
dialog = AlertDialog.Builder(activity)
.setView(listView)
.setNegativeButton(R.string.cancel, null)
.create()
}
}
@SuppressLint("InflateParams")
fun show() {
dialog.window?.setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT
)
dialog.show()
dialog?.run {
window?.setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT
)
this.show()
}
}
private inner class MyAdapter : BaseAdapter(), AdapterView.OnItemClickListener {
@ -118,7 +129,7 @@ class DlgAppPicker(
}
override fun onItemClick(parent : AdapterView<*>?, view : View?, idx : Int, id : Long) {
dialog.dismissSafe()
dialog?.dismissSafe()
callback(list[idx].componentName)
}
}

View File

@ -9,6 +9,7 @@ import android.widget.Button
import android.widget.CheckBox
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.api.entity.Host
@ -18,7 +19,7 @@ import jp.juggler.subwaytooter.util.LinkHelper
import jp.juggler.util.*
class DlgCreateAccount(
val activity : Activity,
val activity : AppCompatActivity,
val instance : Host,
val onClickOk : (
dialog : Dialog,