- テーマ設定が変わったのでガイドのダイアログを出す
- アプリ設定画面のhttps://stackoverflow.com/questions/13614101/fatal-crash-focus-search-returned-a-view-that-wasnt-able-to-take-focus の対策 - 「アプリ設定/タブレット/スクロール時にカラム端と画面端を揃える」を追加。デフォルトON。
This commit is contained in:
parent
29f68216cf
commit
28aacd3a7f
|
@ -11,10 +11,14 @@ import android.os.Handler
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.util.JsonWriter
|
import android.util.JsonWriter
|
||||||
|
import android.view.KeyEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.View.FOCUS_FORWARD
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
|
import android.widget.TextView.OnEditorActionListener
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -217,7 +221,10 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
val e = pref.edit()
|
val e = pref.edit()
|
||||||
var changed = false
|
var changed = false
|
||||||
appSettingRoot.scan {
|
appSettingRoot.scan {
|
||||||
if (it.pref?.removeDefault(pref, e) == true) changed = true
|
when {
|
||||||
|
(it.pref as? IntPref)?.noRemove == true -> Unit
|
||||||
|
it.pref?.removeDefault(pref, e) == true -> changed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (changed) e.apply()
|
if (changed) e.apply()
|
||||||
}
|
}
|
||||||
|
@ -228,6 +235,13 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
if (sv != null) outState.putString(STATE_CHOOSE_INTENT_TARGET, sv)
|
if (sv != null) outState.putString(STATE_CHOOSE_INTENT_TARGET, sv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun dispatchKeyEvent(event: KeyEvent) = try {
|
||||||
|
super.dispatchKeyEvent(event)
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
log.e(ex, "dispatchKeyEvent error")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
|
|
||||||
|
@ -482,6 +496,17 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
views.swSwitch.setOnCheckedChangeListener(this)
|
views.swSwitch.setOnCheckedChangeListener(this)
|
||||||
views.spSpinner.onItemSelectedListener = this
|
views.spSpinner.onItemSelectedListener = this
|
||||||
views.etEditText.addTextChangedListener(this)
|
views.etEditText.addTextChangedListener(this)
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/13614101/fatal-crash-focus-search-returned-a-view-that-wasnt-able-to-take-focus
|
||||||
|
views.etEditText.setOnEditorActionListener(OnEditorActionListener { textView, actionId, event ->
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_NEXT) {
|
||||||
|
@Suppress("WrongConstant")
|
||||||
|
textView.focusSearch(FOCUS_FORWARD)?.requestFocus(FOCUS_FORWARD)
|
||||||
|
// 結果に関わらずこのアクションを処理したとみなす
|
||||||
|
return@OnEditorActionListener true
|
||||||
|
}
|
||||||
|
false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private val tvDesc = views.tvDesc
|
private val tvDesc = views.tvDesc
|
||||||
|
@ -627,21 +652,20 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
|
|
||||||
SettingType.EditText -> {
|
SettingType.EditText -> {
|
||||||
showCaption(name)
|
showCaption(name)
|
||||||
views.etEditText.vg(true)
|
views.etEditText.vg(true)?.let { etEditText ->
|
||||||
?: error("EditText must have preference.")
|
val text = when (val pi = item.pref) {
|
||||||
views.etEditText.inputType = item.inputType
|
is FloatPref ->
|
||||||
val text = when (val pi = item.pref) {
|
item.fromFloat.invoke(actAppSetting, pi(pref))
|
||||||
is FloatPref ->
|
is StringPref ->
|
||||||
item.fromFloat.invoke(actAppSetting, pi(pref))
|
pi(pref)
|
||||||
is StringPref ->
|
else -> error("EditText has incorrect pref $pi")
|
||||||
pi(pref)
|
}
|
||||||
else -> error("EditText han incorrect pref $pi")
|
|
||||||
|
etEditText.hint = item.hint ?: ""
|
||||||
|
etEditText.inputType = item.inputType
|
||||||
|
etEditText.setText(text)
|
||||||
|
etEditText.setSelection(0, text.length)
|
||||||
}
|
}
|
||||||
views.etEditText.setText(text)
|
|
||||||
views.etEditText.setSelection(0, text.length)
|
|
||||||
|
|
||||||
item.hint?.let { views.etEditText.hint = it }
|
|
||||||
|
|
||||||
updateErrorView()
|
updateErrorView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -455,6 +455,7 @@ class ActMain : AppCompatActivity(),
|
||||||
handler.postDelayed(onStartAfter, 1L)
|
handler.postDelayed(onStartAfter, 1L)
|
||||||
|
|
||||||
prNotification.checkOrLaunch()
|
prNotification.checkOrLaunch()
|
||||||
|
themeDefaultChangedDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package jp.juggler.subwaytooter.actmain
|
package jp.juggler.subwaytooter.actmain
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
@ -10,6 +11,7 @@ import jp.juggler.subwaytooter.action.openColumnList
|
||||||
import jp.juggler.subwaytooter.action.openPost
|
import jp.juggler.subwaytooter.action.openPost
|
||||||
import jp.juggler.subwaytooter.api.entity.TootAccountRef
|
import jp.juggler.subwaytooter.api.entity.TootAccountRef
|
||||||
import jp.juggler.subwaytooter.api.entity.TootTag.Companion.findHashtagFromUrl
|
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.column.Column
|
||||||
import jp.juggler.subwaytooter.columnviewholder.ColumnViewHolder
|
import jp.juggler.subwaytooter.columnviewholder.ColumnViewHolder
|
||||||
import jp.juggler.subwaytooter.columnviewholder.TabletColumnViewHolder
|
import jp.juggler.subwaytooter.columnviewholder.TabletColumnViewHolder
|
||||||
|
@ -17,8 +19,7 @@ import jp.juggler.subwaytooter.columnviewholder.ViewHolderHeaderBase
|
||||||
import jp.juggler.subwaytooter.columnviewholder.ViewHolderItem
|
import jp.juggler.subwaytooter.columnviewholder.ViewHolderItem
|
||||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||||
import jp.juggler.subwaytooter.itemviewholder.ItemViewHolder
|
import jp.juggler.subwaytooter.itemviewholder.ItemViewHolder
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.*
|
||||||
import jp.juggler.subwaytooter.pref.PrefI
|
|
||||||
import jp.juggler.subwaytooter.span.MyClickableSpan
|
import jp.juggler.subwaytooter.span.MyClickableSpan
|
||||||
import jp.juggler.subwaytooter.util.openCustomTab
|
import jp.juggler.subwaytooter.util.openCustomTab
|
||||||
import jp.juggler.util.data.addTo
|
import jp.juggler.util.data.addTo
|
||||||
|
@ -26,6 +27,7 @@ import jp.juggler.util.data.cast
|
||||||
import jp.juggler.util.data.notEmpty
|
import jp.juggler.util.data.notEmpty
|
||||||
import jp.juggler.util.log.LogCategory
|
import jp.juggler.util.log.LogCategory
|
||||||
import jp.juggler.util.log.showToast
|
import jp.juggler.util.log.showToast
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
private val log = LogCategory("ActMainActions")
|
private val log = LogCategory("ActMainActions")
|
||||||
|
|
||||||
|
@ -175,3 +177,43 @@ fun ActMain.onMyClickableSpanClickedImpl(viewClicked: View, span: MyClickableSpa
|
||||||
linkInfo = linkInfo
|
linkInfo = linkInfo
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ActMain.themeDefaultChangedDialog() {
|
||||||
|
val lpThemeDefaultChangedWarnTime = PrefL.lpThemeDefaultChangedWarnTime
|
||||||
|
val ipUiTheme = PrefI.ipUiTheme
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
|
||||||
|
// テーマが未定義でなければ警告しない
|
||||||
|
if (pref.getInt(ipUiTheme.key, -1) != -1) {
|
||||||
|
log.i("themeDefaultChangedDialog: theme was set.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 頻繁には警告しない
|
||||||
|
if (now - lpThemeDefaultChangedWarnTime.invoke(pref) < TimeUnit.DAYS.toMillis(60L)) {
|
||||||
|
log.i("themeDefaultChangedDialog: avoid frequently check.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pref.edit().put(lpThemeDefaultChangedWarnTime, now).apply()
|
||||||
|
|
||||||
|
// 色がすべてデフォルトなら警告不要
|
||||||
|
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(pref) -> customizedKeys.add(p.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.w("themeDefaultChangedDialog: customizedKeys=${customizedKeys.joinToString(",")}")
|
||||||
|
if (customizedKeys.isEmpty()) {
|
||||||
|
pref.edit().put(ipUiTheme, ipUiTheme.defVal).apply()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setMessage(R.string.color_theme_changed)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import jp.juggler.subwaytooter.ActMain
|
import jp.juggler.subwaytooter.ActMain
|
||||||
import jp.juggler.subwaytooter.column.Column
|
import jp.juggler.subwaytooter.column.Column
|
||||||
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.subwaytooter.view.GravitySnapHelper
|
import jp.juggler.subwaytooter.view.GravitySnapHelper
|
||||||
import jp.juggler.util.data.clip
|
import jp.juggler.util.data.clip
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
@ -14,7 +15,6 @@ class ActMainTabletViews(val actMain: ActMain) {
|
||||||
internal lateinit var tabletPager: RecyclerView
|
internal lateinit var tabletPager: RecyclerView
|
||||||
internal lateinit var tabletPagerAdapter: TabletColumnPagerAdapter
|
internal lateinit var tabletPagerAdapter: TabletColumnPagerAdapter
|
||||||
internal lateinit var tabletLayoutManager: LinearLayoutManager
|
internal lateinit var tabletLayoutManager: LinearLayoutManager
|
||||||
private lateinit var tabletSnapHelper: GravitySnapHelper
|
|
||||||
|
|
||||||
val visibleColumnsIndices: IntRange
|
val visibleColumnsIndices: IntRange
|
||||||
get() {
|
get() {
|
||||||
|
@ -92,8 +92,8 @@ class ActMainTabletViews(val actMain: ActMain) {
|
||||||
// if( animator is DefaultItemAnimator){
|
// if( animator is DefaultItemAnimator){
|
||||||
// animator.supportsChangeAnimations = false
|
// animator.supportsChangeAnimations = false
|
||||||
// }
|
// }
|
||||||
|
if(PrefB.bpTabletSnap()){
|
||||||
this.tabletSnapHelper = GravitySnapHelper(Gravity.START)
|
GravitySnapHelper(Gravity.START).attachToRecyclerView(this.tabletPager)
|
||||||
this.tabletSnapHelper.attachToRecyclerView(this.tabletPager)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -479,6 +479,10 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
||||||
PrefB.bpManyWindowPost,
|
PrefB.bpManyWindowPost,
|
||||||
R.string.many_window_post
|
R.string.many_window_post
|
||||||
)
|
)
|
||||||
|
sw(
|
||||||
|
PrefB.bpTabletSnap,
|
||||||
|
R.string.tablet_snap
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
section(R.string.media_attachment) {
|
section(R.string.media_attachment) {
|
||||||
|
|
|
@ -338,6 +338,10 @@ object PrefB {
|
||||||
"ManyWindowPost",
|
"ManyWindowPost",
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
val bpTabletSnap = BooleanPref(
|
||||||
|
"bpTabletSnap",
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
val bpMfmDecorationEnabled = BooleanPref(
|
val bpMfmDecorationEnabled = BooleanPref(
|
||||||
"MfmDecorationEnabled",
|
"MfmDecorationEnabled",
|
||||||
|
|
|
@ -12,6 +12,7 @@ fun SharedPreferences.Editor.remove(item: BasePref<*>): SharedPreferences.Editor
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 戻り値はEditor
|
||||||
fun SharedPreferences.Editor.put(item: BooleanPref, v: Boolean) =
|
fun SharedPreferences.Editor.put(item: BooleanPref, v: Boolean) =
|
||||||
this.apply { item.put(this, v) }
|
this.apply { item.put(this, v) }
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ object PrefI {
|
||||||
const val BACK_OPEN_COLUMN_LIST = 2
|
const val BACK_OPEN_COLUMN_LIST = 2
|
||||||
const val BACK_EXIT_APP = 3
|
const val BACK_EXIT_APP = 3
|
||||||
|
|
||||||
val ipUiTheme = IntPref("ui_theme", 2)
|
val ipUiTheme = IntPref("ui_theme", 2, noRemove = true)
|
||||||
|
|
||||||
// val ipResizeImage = IntPref("resize_image", 4)
|
// val ipResizeImage = IntPref("resize_image", 4)
|
||||||
|
|
||||||
|
|
|
@ -6,4 +6,7 @@ object PrefL {
|
||||||
|
|
||||||
// long
|
// long
|
||||||
val lpTabletTootDefaultAccount = LongPref("tablet_toot_default_account", -1L)
|
val lpTabletTootDefaultAccount = LongPref("tablet_toot_default_account", -1L)
|
||||||
}
|
|
||||||
|
// long
|
||||||
|
val lpThemeDefaultChangedWarnTime = LongPref("lpThemeDefaultChangedWarnTime", -1L)
|
||||||
|
}
|
||||||
|
|
|
@ -46,4 +46,6 @@ abstract class BasePref<T>(val key: String, val defVal: T) {
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract fun hasNonDefaultValue(pref: SharedPreferences): Boolean
|
||||||
}
|
}
|
|
@ -11,4 +11,7 @@ class BooleanPref(key: String, defVal: Boolean) : BasePref<Boolean>(key, defVal)
|
||||||
override fun put(editor: SharedPreferences.Editor, v: Boolean) {
|
override fun put(editor: SharedPreferences.Editor, v: Boolean) {
|
||||||
if (v == defVal) editor.remove(key) else editor.putBoolean(key, v)
|
if (v == defVal) editor.remove(key) else editor.putBoolean(key, v)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
override fun hasNonDefaultValue(pref: SharedPreferences) =
|
||||||
|
defVal != pref.getBoolean(key, defVal)
|
||||||
|
}
|
||||||
|
|
|
@ -10,4 +10,7 @@ class FloatPref(key: String, defVal: Float) : BasePref<Float>(key, defVal) {
|
||||||
override fun put(editor: SharedPreferences.Editor, v: Float) {
|
override fun put(editor: SharedPreferences.Editor, v: Float) {
|
||||||
if (v == defVal) editor.remove(key) else editor.putFloat(key, v)
|
if (v == defVal) editor.remove(key) else editor.putFloat(key, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hasNonDefaultValue(pref: SharedPreferences) =
|
||||||
|
defVal != pref.getFloat(key, defVal)
|
||||||
}
|
}
|
|
@ -2,12 +2,15 @@ package jp.juggler.subwaytooter.pref.impl
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
|
||||||
class IntPref(key: String, defVal: Int) : BasePref<Int>(key, defVal) {
|
class IntPref(key: String, defVal: Int, val noRemove:Boolean = false) : BasePref<Int>(key, defVal) {
|
||||||
|
|
||||||
override operator fun invoke(pref: SharedPreferences): Int =
|
override operator fun invoke(pref: SharedPreferences): Int =
|
||||||
pref.getInt(key, defVal)
|
pref.getInt(key, defVal)
|
||||||
|
|
||||||
override fun put(editor: SharedPreferences.Editor, v: Int) {
|
override fun put(editor: SharedPreferences.Editor, v: Int) {
|
||||||
if (v == defVal) editor.remove(key) else editor.putInt(key, v)
|
if (!noRemove && v == defVal) editor.remove(key) else editor.putInt(key, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hasNonDefaultValue(pref: SharedPreferences) =
|
||||||
|
defVal != pref.getInt(key, defVal)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,4 +10,7 @@ class LongPref(key: String, defVal: Long) : BasePref<Long>(key, defVal) {
|
||||||
override fun put(editor: SharedPreferences.Editor, v: Long) {
|
override fun put(editor: SharedPreferences.Editor, v: Long) {
|
||||||
if (v == defVal) editor.remove(key) else editor.putLong(key, v)
|
if (v == defVal) editor.remove(key) else editor.putLong(key, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hasNonDefaultValue(pref: SharedPreferences) =
|
||||||
|
defVal != pref.getLong(key, defVal)
|
||||||
}
|
}
|
|
@ -15,5 +15,8 @@ class StringPref(
|
||||||
if (v == defVal) editor.remove(key) else editor.putString(key, v)
|
if (v == defVal) editor.remove(key) else editor.putString(key, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hasNonDefaultValue(pref: SharedPreferences) =
|
||||||
|
defVal != pref.getString(key, defVal)
|
||||||
|
|
||||||
fun toInt(pref: SharedPreferences) = invoke(pref).toIntOrNull() ?: defVal.toInt()
|
fun toInt(pref: SharedPreferences) = invoke(pref).toIntOrNull() ?: defVal.toInt()
|
||||||
}
|
}
|
|
@ -1178,5 +1178,7 @@
|
||||||
<string name="filter_title_empty">フィルタ名がカラです</string>
|
<string name="filter_title_empty">フィルタ名がカラです</string>
|
||||||
<string name="acct_customize">Acct customize</string>
|
<string name="acct_customize">Acct customize</string>
|
||||||
<string name="delete_confirm">「%1$s」を削除しますか?</string>
|
<string name="delete_confirm">「%1$s」を削除しますか?</string>
|
||||||
|
<string name="color_theme_changed">色テーマのデフォルトが変わりました。カスタマイズ済の色設定と競合する場合があります。 アプリ設定の色セクションを確認することをお勧めします。</string>
|
||||||
|
<string name="tablet_snap">スクロール時にカラム端と画面端を揃える (アプリ再起動が必要)</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1185,4 +1185,6 @@
|
||||||
<string name="filter_title_empty">filter title is empty.</string>
|
<string name="filter_title_empty">filter title is empty.</string>
|
||||||
<string name="acct_customize">Acct customize</string>
|
<string name="acct_customize">Acct customize</string>
|
||||||
<string name="delete_confirm">delete \"%1$s\" ?</string>
|
<string name="delete_confirm">delete \"%1$s\" ?</string>
|
||||||
|
<string name="color_theme_changed">Default color theme has been updated. This may conflict with your customized color settings. We recommend reviewing the colors section in app settings.</string>
|
||||||
|
<string name="tablet_snap" >Align column edge to screen edge when scrolling (app restart required)</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue