- テーマ設定が変わったのでガイドのダイアログを出す

- アプリ設定画面のhttps://stackoverflow.com/questions/13614101/fatal-crash-focus-search-returned-a-view-that-wasnt-able-to-take-focus の対策
- 「アプリ設定/タブレット/スクロール時にカラム端と画面端を揃える」を追加。デフォルトON。
This commit is contained in:
tateisu 2023-01-16 14:58:23 +09:00
parent 29f68216cf
commit 28aacd3a7f
17 changed files with 126 additions and 26 deletions

View File

@ -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()
} }

View File

@ -455,6 +455,7 @@ class ActMain : AppCompatActivity(),
handler.postDelayed(onStartAfter, 1L) handler.postDelayed(onStartAfter, 1L)
prNotification.checkOrLaunch() prNotification.checkOrLaunch()
themeDefaultChangedDialog()
} }
} }

View File

@ -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()
}

View File

@ -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) }
} }
} }

View File

@ -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) {

View File

@ -338,6 +338,10 @@ object PrefB {
"ManyWindowPost", "ManyWindowPost",
false false
) )
val bpTabletSnap = BooleanPref(
"bpTabletSnap",
true
)
val bpMfmDecorationEnabled = BooleanPref( val bpMfmDecorationEnabled = BooleanPref(
"MfmDecorationEnabled", "MfmDecorationEnabled",

View File

@ -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) }

View File

@ -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)

View File

@ -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)
}

View File

@ -46,4 +46,6 @@ abstract class BasePref<T>(val key: String, val defVal: T) {
} else { } else {
false false
} }
abstract fun hasNonDefaultValue(pref: SharedPreferences): Boolean
} }

View File

@ -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)
}

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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()
} }

View File

@ -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>

View File

@ -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>