fix #212, アプリ設定画面のListViewが過剰に更新されるのを防ぐ
This commit is contained in:
parent
3df1fde938
commit
b12b834d11
|
@ -18,15 +18,19 @@ import android.widget.*
|
||||||
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
|
||||||
import androidx.appcompat.widget.SwitchCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.jrummyapps.android.colorpicker.ColorPickerDialog
|
import com.jrummyapps.android.colorpicker.ColorPickerDialog
|
||||||
import com.jrummyapps.android.colorpicker.ColorPickerDialogListener
|
import com.jrummyapps.android.colorpicker.ColorPickerDialogListener
|
||||||
import jp.juggler.subwaytooter.appsetting.AppDataExporter
|
import jp.juggler.subwaytooter.appsetting.AppDataExporter
|
||||||
import jp.juggler.subwaytooter.appsetting.AppSettingItem
|
import jp.juggler.subwaytooter.appsetting.AppSettingItem
|
||||||
import jp.juggler.subwaytooter.appsetting.SettingType
|
import jp.juggler.subwaytooter.appsetting.SettingType
|
||||||
import jp.juggler.subwaytooter.appsetting.appSettingRoot
|
import jp.juggler.subwaytooter.appsetting.appSettingRoot
|
||||||
|
import jp.juggler.subwaytooter.databinding.ActAppSettingBinding
|
||||||
|
import jp.juggler.subwaytooter.databinding.LvSettingItemBinding
|
||||||
import jp.juggler.subwaytooter.dialog.DlgAppPicker
|
import jp.juggler.subwaytooter.dialog.DlgAppPicker
|
||||||
import jp.juggler.subwaytooter.notification.restartAllWorker
|
import jp.juggler.subwaytooter.notification.restartAllWorker
|
||||||
import jp.juggler.subwaytooter.pref.impl.BooleanPref
|
import jp.juggler.subwaytooter.pref.impl.BooleanPref
|
||||||
|
@ -47,8 +51,10 @@ import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStreamWriter
|
import java.io.OutputStreamWriter
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
@ -79,9 +85,14 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
|
|
||||||
lateinit var pref: SharedPreferences
|
lateinit var pref: SharedPreferences
|
||||||
lateinit var handler: Handler
|
lateinit var handler: Handler
|
||||||
private lateinit var lvList: ListView
|
|
||||||
private lateinit var adapter: MyAdapter
|
val views by lazy {
|
||||||
private lateinit var etSearch: EditText
|
ActAppSettingBinding.inflate(layoutInflater)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val adapter by lazy {
|
||||||
|
MyAdapter()
|
||||||
|
}
|
||||||
|
|
||||||
private val arNoop = ActivityResultHandler(log) { }
|
private val arNoop = ActivityResultHandler(log) { }
|
||||||
|
|
||||||
|
@ -108,6 +119,18 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var pendingQuery: String? = null
|
||||||
|
|
||||||
|
private val procQuery: Runnable = Runnable {
|
||||||
|
if (pendingQuery != null) load(null, pendingQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val divider = Any()
|
||||||
|
|
||||||
|
private var lastSection: AppSettingItem? = null
|
||||||
|
private var lastQuery: String? = null
|
||||||
|
private var colorTarget: AppSettingItem? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
backPressed {
|
backPressed {
|
||||||
|
@ -151,17 +174,15 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initUi() {
|
private fun initUi() {
|
||||||
setContentView(R.layout.act_app_setting)
|
setContentView(views.root)
|
||||||
App1.initEdgeToEdge(this)
|
App1.initEdgeToEdge(this)
|
||||||
|
|
||||||
Styler.fixHorizontalPadding0(findViewById(R.id.llContent))
|
Styler.fixHorizontalPadding0(views.llContent)
|
||||||
lvList = findViewById(R.id.lvList)
|
|
||||||
|
|
||||||
adapter = MyAdapter()
|
views.lvList.layoutManager = LinearLayoutManager(this)
|
||||||
lvList.adapter = adapter
|
views.lvList.adapter = adapter
|
||||||
|
|
||||||
etSearch = findViewById<EditText>(R.id.etSearch).apply {
|
views.etSearch.addTextChangedListener(object : TextWatcher {
|
||||||
addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun afterTextChanged(p0: Editable?) {
|
override fun afterTextChanged(p0: Editable?) {
|
||||||
pendingQuery = p0?.toString()
|
pendingQuery = p0?.toString()
|
||||||
this@ActAppSetting.handler.removeCallbacks(procQuery)
|
this@ActAppSetting.handler.removeCallbacks(procQuery)
|
||||||
|
@ -184,9 +205,8 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
findViewById<View>(R.id.btnSearchReset).setOnClickListener(this@ActAppSetting)
|
views.btnSearchReset.setOnClickListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeDefaultPref() {
|
private fun removeDefaultPref() {
|
||||||
|
@ -215,34 +235,18 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
when (v.id) {
|
when (v.id) {
|
||||||
R.id.btnSearchReset -> {
|
R.id.btnSearchReset -> {
|
||||||
handler.removeCallbacks(procQuery)
|
handler.removeCallbacks(procQuery)
|
||||||
etSearch.setText("")
|
views.etSearch.setText("")
|
||||||
etSearch.hideKeyboard()
|
views.etSearch.hideKeyboard()
|
||||||
load(lastSection, null)
|
load(lastSection, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////
|
|
||||||
private var pendingQuery: String? = null
|
|
||||||
|
|
||||||
private val procQuery: Runnable = Runnable {
|
|
||||||
if (pendingQuery != null) load(null, pendingQuery)
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private val divider = Any()
|
|
||||||
private val list = ArrayList<Any>()
|
|
||||||
|
|
||||||
private var lastSection: AppSettingItem? = null
|
|
||||||
private var lastQuery: String? = null
|
|
||||||
|
|
||||||
private fun load(section: AppSettingItem?, query: String?) {
|
private fun load(section: AppSettingItem?, query: String?) {
|
||||||
list.clear()
|
adapter.items = buildList {
|
||||||
|
|
||||||
var lastPath: String? = null
|
var lastPath: String? = null
|
||||||
fun addParentPath(item: AppSettingItem) {
|
fun addParentPath(item: AppSettingItem) {
|
||||||
list.add(divider)
|
add(divider)
|
||||||
|
|
||||||
val pathList = ArrayList<String>()
|
val pathList = ArrayList<String>()
|
||||||
var parent = item.parent
|
var parent = item.parent
|
||||||
|
@ -253,12 +257,14 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
val path = pathList.joinToString("/")
|
val path = pathList.joinToString("/")
|
||||||
if (path != lastPath) {
|
if (path != lastPath) {
|
||||||
lastPath = path
|
lastPath = path
|
||||||
list.add(path)
|
add(path)
|
||||||
list.add(divider)
|
add(divider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query?.isNotEmpty() == true) {
|
when {
|
||||||
|
// 検索キーワードあり
|
||||||
|
query?.isNotBlank() == true -> {
|
||||||
lastQuery = query
|
lastQuery = query
|
||||||
fun scanGroup(level: Int, item: AppSettingItem) {
|
fun scanGroup(level: Int, item: AppSettingItem) {
|
||||||
if (item.caption == 0) return
|
if (item.caption == 0) return
|
||||||
|
@ -267,7 +273,9 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
if (item.type == SettingType.Group) {
|
if (item.type == SettingType.Group) {
|
||||||
for (child in item.items) {
|
for (child in item.items) {
|
||||||
if (child.caption == 0) continue
|
if (child.caption == 0) continue
|
||||||
if (getString(item.caption).contains(query, ignoreCase = true)) {
|
if (getString(item.caption).contains(query,
|
||||||
|
ignoreCase = true)
|
||||||
|
) {
|
||||||
match = true
|
match = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -275,17 +283,15 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
if (match) {
|
if (match) {
|
||||||
// put entire group
|
// put entire group
|
||||||
addParentPath(item)
|
addParentPath(item)
|
||||||
list.add(item)
|
add(item)
|
||||||
for (child in item.items) {
|
addAll(item.items)
|
||||||
list.add(child)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
addParentPath(item)
|
addParentPath(item)
|
||||||
list.add(item)
|
add(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (child in item.items) {
|
for (child in item.items) {
|
||||||
|
@ -293,31 +299,29 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scanGroup(0, appSettingRoot)
|
scanGroup(0, appSettingRoot)
|
||||||
if (list.isNotEmpty()) list.add(divider)
|
}
|
||||||
} else if (section == null) {
|
|
||||||
// show root page
|
// show root page
|
||||||
|
section == null -> {
|
||||||
val root = appSettingRoot
|
val root = appSettingRoot
|
||||||
lastQuery = null
|
lastQuery = null
|
||||||
lastSection = null
|
lastSection = null
|
||||||
for (child in root.items) {
|
for (child in root.items) {
|
||||||
list.add(divider)
|
add(divider)
|
||||||
list.add(child)
|
add(child)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
list.add(divider)
|
|
||||||
} else {
|
|
||||||
// show section page
|
// show section page
|
||||||
|
else -> {
|
||||||
lastSection = section
|
lastSection = section
|
||||||
lastQuery = null
|
lastQuery = null
|
||||||
fun scanGroup(level: Int, parent: AppSettingItem?) {
|
fun scanGroup(level: Int, parent: AppSettingItem?) {
|
||||||
parent ?: return
|
parent ?: return
|
||||||
for (item in parent.items) {
|
for (item in parent.items) {
|
||||||
list.add(divider)
|
add(divider)
|
||||||
list.add(item)
|
add(item)
|
||||||
if (item.items.isNotEmpty()) {
|
if (item.items.isNotEmpty()) {
|
||||||
if (item.type == SettingType.Group) {
|
if (item.type == SettingType.Group) {
|
||||||
for (child in item.items) {
|
addAll(item.items)
|
||||||
list.add(child)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
scanGroup(level + 1, item)
|
scanGroup(level + 1, item)
|
||||||
}
|
}
|
||||||
|
@ -325,41 +329,11 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scanGroup(0, section.cast())
|
scanGroup(0, section.cast())
|
||||||
if (list.isNotEmpty()) list.add(divider)
|
|
||||||
}
|
}
|
||||||
adapter.notifyDataSetChanged()
|
|
||||||
lvList.setSelectionFromTop(0, 0)
|
|
||||||
}
|
}
|
||||||
|
if (isNotEmpty()) add(divider)
|
||||||
inner class MyAdapter : BaseAdapter() {
|
|
||||||
|
|
||||||
override fun getCount(): Int = list.size
|
|
||||||
override fun getItemId(position: Int): Long = 0
|
|
||||||
override fun getItem(position: Int): Any = list[position]
|
|
||||||
override fun getViewTypeCount(): Int = SettingType.values().maxByOrNull { it.id }!!.id + 1
|
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int =
|
|
||||||
when (val item = list[position]) {
|
|
||||||
is AppSettingItem -> item.type.id
|
|
||||||
is String -> SettingType.Path.id
|
|
||||||
divider -> SettingType.Divider.id
|
|
||||||
else -> error("can't generate view for type $item")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 isEnabled(position: Int): Boolean = list[position] is AppSettingItem
|
|
||||||
|
|
||||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View =
|
|
||||||
when (val item = list[position]) {
|
|
||||||
is AppSettingItem ->
|
|
||||||
getViewSettingItem(item, convertView, parent)
|
|
||||||
is String -> getViewPath(item, convertView)
|
|
||||||
divider -> getViewDivider(convertView)
|
|
||||||
else -> error("can't generate view for type $item")
|
|
||||||
}
|
}
|
||||||
|
views.lvList.scrollToPosition(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun dip(dp: Float): Int =
|
private fun dip(dp: Float): Int =
|
||||||
|
@ -367,62 +341,6 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
|
|
||||||
private fun dip(dp: Int): Int = dip(dp.toFloat())
|
private fun dip(dp: Int): Int = dip(dp.toFloat())
|
||||||
|
|
||||||
private fun getViewDivider(convertView: View?): View =
|
|
||||||
convertView ?: FrameLayout(this@ActAppSetting).apply {
|
|
||||||
layoutParams = AbsListView.LayoutParams(
|
|
||||||
AbsListView.LayoutParams.MATCH_PARENT,
|
|
||||||
AbsListView.LayoutParams.WRAP_CONTENT
|
|
||||||
)
|
|
||||||
addView(View(this@ActAppSetting).apply {
|
|
||||||
layoutParams = FrameLayout.LayoutParams(
|
|
||||||
AbsListView.LayoutParams.MATCH_PARENT,
|
|
||||||
dip(1)
|
|
||||||
).apply {
|
|
||||||
val marginX = 0
|
|
||||||
val marginY = dip(6)
|
|
||||||
setMargins(marginX, marginY, marginX, marginY)
|
|
||||||
}
|
|
||||||
setBackgroundColor(context.attrColor(R.attr.colorSettingDivider))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getViewPath(path: String, convertView: View?): View {
|
|
||||||
val tv: MyTextView =
|
|
||||||
convertView.cast() ?: MyTextView(this@ActAppSetting).apply {
|
|
||||||
layoutParams = AbsListView.LayoutParams(
|
|
||||||
AbsListView.LayoutParams.MATCH_PARENT,
|
|
||||||
AbsListView.LayoutParams.WRAP_CONTENT
|
|
||||||
)
|
|
||||||
val padX = 0
|
|
||||||
val padY = dip(3)
|
|
||||||
setTypeface(typeface, Typeface.BOLD)
|
|
||||||
setPaddingRelative(padX, padY, padX, padY)
|
|
||||||
}
|
|
||||||
tv.text = path
|
|
||||||
return tv
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getViewSettingItem(
|
|
||||||
item: AppSettingItem,
|
|
||||||
convertView: View?,
|
|
||||||
parent: ViewGroup?,
|
|
||||||
): View {
|
|
||||||
val view: View
|
|
||||||
val holder: ViewHolderSettingItem
|
|
||||||
if (convertView != null) {
|
|
||||||
view = convertView
|
|
||||||
holder = convertView.tag.cast()!!
|
|
||||||
} else {
|
|
||||||
view = layoutInflater.inflate(R.layout.lv_setting_item, parent, false)
|
|
||||||
holder = ViewHolderSettingItem(view)
|
|
||||||
view.tag = holder
|
|
||||||
}
|
|
||||||
holder.bind(item)
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
private var colorTarget: AppSettingItem? = null
|
|
||||||
|
|
||||||
override fun onDialogDismissed(dialogId: Int) {
|
override fun onDialogDismissed(dialogId: Int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,40 +356,146 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
colorTarget.changed(this)
|
colorTarget.changed(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolderSettingItem(viewRoot: View) :
|
inner class MyAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
var items: List<Any> = emptyList()
|
||||||
|
set(newItems) {
|
||||||
|
val oldItems = field
|
||||||
|
field = newItems
|
||||||
|
DiffUtil.calculateDiff(object : DiffUtil.Callback() {
|
||||||
|
override fun getOldListSize() = oldItems.size
|
||||||
|
override fun getNewListSize() = newItems.size
|
||||||
|
override fun areItemsTheSame(
|
||||||
|
oldItemPosition: Int,
|
||||||
|
newItemPosition: Int,
|
||||||
|
) = oldItems.elementAtOrNull(oldItemPosition) == newItems.elementAtOrNull(
|
||||||
|
newItemPosition)
|
||||||
|
|
||||||
|
override fun areContentsTheSame(
|
||||||
|
oldItemPosition: Int,
|
||||||
|
newItemPosition: Int,
|
||||||
|
) = oldItems.elementAtOrNull(oldItemPosition) == newItems.elementAtOrNull(
|
||||||
|
newItemPosition)
|
||||||
|
}, true).dispatchUpdatesTo(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val settingHolderList =
|
||||||
|
ConcurrentHashMap<AppSettingItem, WeakReference<VhSettingItem>>()
|
||||||
|
|
||||||
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int) =
|
||||||
|
when (val item = items.elementAtOrNull(position)) {
|
||||||
|
divider -> SettingType.Divider.id
|
||||||
|
is String -> SettingType.Path.id
|
||||||
|
is AppSettingItem -> item.type.id
|
||||||
|
else -> error("can't generate view for type $item")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||||
|
when (SettingType.map[viewType]) {
|
||||||
|
SettingType.Divider -> VhDivider()
|
||||||
|
SettingType.Path -> VhPath(parent)
|
||||||
|
else -> VhSettingItem(this@ActAppSetting, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
when (val item = items.elementAtOrNull(position)) {
|
||||||
|
divider -> viewHolder.cast<VhDivider>()
|
||||||
|
is String -> viewHolder.cast<VhPath>()?.bind(item)
|
||||||
|
is AppSettingItem -> if (viewHolder is VhSettingItem) {
|
||||||
|
viewHolder.bind(item)
|
||||||
|
// 古い紐付けを削除
|
||||||
|
settingHolderList.entries.filter {
|
||||||
|
when (it.value.get()) {
|
||||||
|
null, viewHolder -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}.forEach { settingHolderList.remove(it.key) }
|
||||||
|
// 新しい紐付けを覚える
|
||||||
|
settingHolderList[item] = WeakReference(viewHolder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findVhSetting(item: AppSettingItem) = settingHolderList[item]?.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class VhDivider(
|
||||||
|
viewRoot: FrameLayout = FrameLayout(this@ActAppSetting).apply {
|
||||||
|
layoutParams = RecyclerView.LayoutParams(
|
||||||
|
RecyclerView.LayoutParams.MATCH_PARENT,
|
||||||
|
RecyclerView.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
addView(View(this@ActAppSetting).apply {
|
||||||
|
layoutParams = FrameLayout.LayoutParams(
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
dip(1)
|
||||||
|
).apply {
|
||||||
|
val marginX = 0
|
||||||
|
val marginY = dip(6)
|
||||||
|
setMargins(marginX, marginY, marginX, marginY)
|
||||||
|
}
|
||||||
|
setBackgroundColor(context.attrColor(R.attr.colorSettingDivider))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
) : RecyclerView.ViewHolder(viewRoot)
|
||||||
|
|
||||||
|
private inner class VhPath(
|
||||||
|
val parent: ViewGroup,
|
||||||
|
val viewRoot: MyTextView = MyTextView(this@ActAppSetting).apply {
|
||||||
|
layoutParams = RecyclerView.LayoutParams(
|
||||||
|
RecyclerView.LayoutParams.MATCH_PARENT,
|
||||||
|
RecyclerView.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
val padX = 0
|
||||||
|
val padY = dip(3)
|
||||||
|
setTypeface(typeface, Typeface.BOLD)
|
||||||
|
setPaddingRelative(padX, padY, padX, padY)
|
||||||
|
},
|
||||||
|
) : RecyclerView.ViewHolder(viewRoot) {
|
||||||
|
fun bind(path: String) {
|
||||||
|
viewRoot.text = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not private
|
||||||
|
class VhSettingItem(
|
||||||
|
val activity: ActAppSetting,
|
||||||
|
parent: ViewGroup,
|
||||||
|
private val views: LvSettingItemBinding = LvSettingItemBinding
|
||||||
|
.inflate(activity.layoutInflater, parent, false),
|
||||||
|
) : RecyclerView.ViewHolder(views.root),
|
||||||
TextWatcher,
|
TextWatcher,
|
||||||
AdapterView.OnItemSelectedListener,
|
AdapterView.OnItemSelectedListener,
|
||||||
CompoundButton.OnCheckedChangeListener {
|
CompoundButton.OnCheckedChangeListener {
|
||||||
|
|
||||||
private val tvCaption: TextView = viewRoot.findViewById(R.id.tvCaption)
|
private val btnAction = views.btnAction
|
||||||
private val btnAction: Button = viewRoot.findViewById(R.id.btnAction)
|
|
||||||
|
|
||||||
private val checkBox: CheckBox = viewRoot.findViewById<CheckBox>(R.id.checkBox)
|
private val checkBox = views.checkBox
|
||||||
.also { it.setOnCheckedChangeListener(this) }
|
.also { it.setOnCheckedChangeListener(this) }
|
||||||
|
|
||||||
private val swSwitch: SwitchCompat = viewRoot.findViewById<SwitchCompat>(R.id.swSwitch)
|
private val swSwitch = views.swSwitch
|
||||||
.also { it.setOnCheckedChangeListener(this) }
|
.also { it.setOnCheckedChangeListener(this) }
|
||||||
|
|
||||||
val llExtra: LinearLayout = viewRoot.findViewById(R.id.llExtra)
|
val llExtra = views.llExtra
|
||||||
|
|
||||||
val textView1: TextView = viewRoot.findViewById(R.id.textView1)
|
val textView1 = views.textView1
|
||||||
|
|
||||||
private val llButtonBar: LinearLayout = viewRoot.findViewById(R.id.llButtonBar)
|
private val llButtonBar = views.llButtonBar
|
||||||
private val vColor: View = viewRoot.findViewById(R.id.vColor)
|
private val vColor = views.vColor
|
||||||
private val btnEdit: Button = viewRoot.findViewById(R.id.btnEdit)
|
private val btnEdit = views.btnEdit
|
||||||
private val btnReset: Button = viewRoot.findViewById(R.id.btnReset)
|
private val btnReset = views.btnReset
|
||||||
|
|
||||||
private val spSpinner: Spinner = viewRoot.findViewById<Spinner>(R.id.spSpinner)
|
private val spSpinner = views.spSpinner
|
||||||
.also { it.onItemSelectedListener = this }
|
.also { it.onItemSelectedListener = this }
|
||||||
|
|
||||||
private val etEditText: EditText = viewRoot.findViewById<EditText>(R.id.etEditText)
|
private val etEditText = views.etEditText
|
||||||
.also { it.addTextChangedListener(this) }
|
.also { it.addTextChangedListener(this) }
|
||||||
|
|
||||||
private val tvDesc: TextView = viewRoot.findViewById(R.id.tvDesc)
|
private val tvDesc = views.tvDesc
|
||||||
private val tvError: TextView = viewRoot.findViewById(R.id.tvError)
|
private val tvError = views.tvError
|
||||||
|
|
||||||
val activity: ActAppSetting
|
private val pref = activity.pref
|
||||||
get() = this@ActAppSetting
|
|
||||||
|
|
||||||
var item: AppSettingItem? = null
|
var item: AppSettingItem? = null
|
||||||
|
|
||||||
|
@ -482,7 +506,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
try {
|
try {
|
||||||
this.item = item
|
this.item = item
|
||||||
|
|
||||||
tvCaption.vg(false)
|
views.tvCaption.vg(false)
|
||||||
btnAction.vg(false)
|
btnAction.vg(false)
|
||||||
checkBox.vg(false)
|
checkBox.vg(false)
|
||||||
swSwitch.vg(false)
|
swSwitch.vg(false)
|
||||||
|
@ -495,11 +519,11 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
tvDesc.vg(false)
|
tvDesc.vg(false)
|
||||||
tvError.vg(false)
|
tvError.vg(false)
|
||||||
|
|
||||||
val name = if (item.caption == 0) "" else getString(item.caption)
|
val name = if (item.caption == 0) "" else activity.getString(item.caption)
|
||||||
|
|
||||||
if (item.desc != 0) {
|
if (item.desc != 0) {
|
||||||
tvDesc.vg(true)
|
tvDesc.vg(true)
|
||||||
tvDesc.text = getString(item.desc)
|
tvDesc.text = activity.getString(item.desc)
|
||||||
if (item.descClickSet) {
|
if (item.descClickSet) {
|
||||||
tvDesc.background = ContextCompat.getDrawable(
|
tvDesc.background = ContextCompat.getDrawable(
|
||||||
activity,
|
activity,
|
||||||
|
@ -520,7 +544,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
btnAction.text = name
|
btnAction.text = name
|
||||||
btnAction.isEnabledAlpha = item.enabled
|
btnAction.isEnabledAlpha = item.enabled
|
||||||
btnAction.setOnClickListener {
|
btnAction.setOnClickListener {
|
||||||
load(item.cast()!!, null)
|
activity.load(item.cast()!!, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,7 +563,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
checkBox.vg(false) // skip animation
|
checkBox.vg(false) // skip animation
|
||||||
checkBox.text = name
|
checkBox.text = name
|
||||||
checkBox.isEnabledAlpha = item.enabled
|
checkBox.isEnabledAlpha = item.enabled
|
||||||
checkBox.isChecked = bp(pref)
|
checkBox.isChecked = bp(activity.pref)
|
||||||
checkBox.vg(true)
|
checkBox.vg(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,9 +572,9 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
item.pref.cast() ?: error("$name has no boolean pref")
|
item.pref.cast() ?: error("$name has no boolean pref")
|
||||||
showCaption(name)
|
showCaption(name)
|
||||||
swSwitch.vg(false) // skip animation
|
swSwitch.vg(false) // skip animation
|
||||||
setSwitchColor(swSwitch)
|
activity.setSwitchColor(swSwitch)
|
||||||
swSwitch.isEnabledAlpha = item.enabled
|
swSwitch.isEnabledAlpha = item.enabled
|
||||||
swSwitch.isChecked = bp(pref)
|
swSwitch.isChecked = bp(activity.pref)
|
||||||
swSwitch.vg(true)
|
swSwitch.vg(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,7 +585,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
SettingType.Sample -> {
|
SettingType.Sample -> {
|
||||||
llExtra.vg(true)
|
llExtra.vg(true)
|
||||||
llExtra.removeAllViews()
|
llExtra.removeAllViews()
|
||||||
layoutInflater.inflate(item.sampleLayoutId, llExtra, true)
|
activity.layoutInflater.inflate(item.sampleLayoutId, llExtra, true)
|
||||||
item.sampleUpdate(activity, llExtra)
|
item.sampleUpdate(activity, llExtra)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,12 +594,12 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
showCaption(name)
|
showCaption(name)
|
||||||
llButtonBar.vg(true)
|
llButtonBar.vg(true)
|
||||||
vColor.vg(true)
|
vColor.vg(true)
|
||||||
vColor.setBackgroundColor(ip(pref))
|
vColor.setBackgroundColor(ip(activity.pref))
|
||||||
btnEdit.isEnabledAlpha = item.enabled
|
btnEdit.isEnabledAlpha = item.enabled
|
||||||
btnReset.isEnabledAlpha = item.enabled
|
btnReset.isEnabledAlpha = item.enabled
|
||||||
btnEdit.setOnClickListener {
|
btnEdit.setOnClickListener {
|
||||||
colorTarget = item
|
activity.colorTarget = item
|
||||||
val color = ip(pref)
|
val color = ip(activity.pref)
|
||||||
val builder = ColorPickerDialog.newBuilder()
|
val builder = ColorPickerDialog.newBuilder()
|
||||||
.setDialogType(ColorPickerDialog.TYPE_CUSTOM)
|
.setDialogType(ColorPickerDialog.TYPE_CUSTOM)
|
||||||
.setAllowPresets(true)
|
.setAllowPresets(true)
|
||||||
|
@ -585,7 +609,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
builder.show(activity)
|
builder.show(activity)
|
||||||
}
|
}
|
||||||
btnReset.setOnClickListener {
|
btnReset.setOnClickListener {
|
||||||
pref.edit().remove(ip).apply()
|
activity.pref.edit().remove(ip).apply()
|
||||||
showColor()
|
showColor()
|
||||||
item.changed.invoke(activity)
|
item.changed.invoke(activity)
|
||||||
}
|
}
|
||||||
|
@ -602,11 +626,12 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
// 整数型の設定のSpinnerは全て選択肢を単純に覚える
|
// 整数型の設定のSpinnerは全て選択肢を単純に覚える
|
||||||
val argsInt = item.spinnerArgs
|
val argsInt = item.spinnerArgs
|
||||||
if (argsInt != null) {
|
if (argsInt != null) {
|
||||||
initSpinner(spSpinner, argsInt.map { getString(it) })
|
activity.initSpinner(spSpinner,
|
||||||
|
argsInt.map { activity.getString(it) })
|
||||||
} else {
|
} else {
|
||||||
initSpinner(spSpinner, item.spinnerArgsProc(activity))
|
activity.initSpinner(spSpinner, item.spinnerArgsProc(activity))
|
||||||
}
|
}
|
||||||
spSpinner.setSelection(pi.invoke(pref))
|
spSpinner.setSelection(pi.invoke(activity.pref))
|
||||||
} else {
|
} else {
|
||||||
item.spinnerInitializer.invoke(activity, spSpinner)
|
item.spinnerInitializer.invoke(activity, spSpinner)
|
||||||
}
|
}
|
||||||
|
@ -615,18 +640,13 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
SettingType.EditText -> {
|
SettingType.EditText -> {
|
||||||
showCaption(name)
|
showCaption(name)
|
||||||
etEditText.vg(true)
|
etEditText.vg(true)
|
||||||
|
|
||||||
?: error("EditText must have preference.")
|
?: error("EditText must have preference.")
|
||||||
etEditText.inputType = item.inputType
|
etEditText.inputType = item.inputType
|
||||||
val text = when (val pi = item.pref) {
|
val text = when (val pi = item.pref) {
|
||||||
is FloatPref -> {
|
is FloatPref ->
|
||||||
item.fromFloat.invoke(activity, pi(pref))
|
item.fromFloat.invoke(activity, pi(activity.pref))
|
||||||
}
|
is StringPref ->
|
||||||
|
pi(activity.pref)
|
||||||
is StringPref -> {
|
|
||||||
pi(pref)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> error("EditText han incorrect pref $pi")
|
else -> error("EditText han incorrect pref $pi")
|
||||||
}
|
}
|
||||||
etEditText.setText(text)
|
etEditText.setText(text)
|
||||||
|
@ -662,8 +682,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
|
|
||||||
private fun showCaption(caption: String) {
|
private fun showCaption(caption: String) {
|
||||||
if (caption.isNotEmpty()) {
|
if (caption.isNotEmpty()) {
|
||||||
tvCaption.vg(true)
|
views.tvCaption.vg(true)?.text = caption
|
||||||
tvCaption.text = caption
|
|
||||||
updateCaption()
|
updateCaption()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -672,16 +691,16 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
val item = item ?: return
|
val item = item ?: return
|
||||||
val key = item.pref?.key ?: return
|
val key = item.pref?.key ?: return
|
||||||
|
|
||||||
val sample: TextView = tvCaption
|
val sample = views.tvCaption
|
||||||
var defaultExtra = defaultLineSpacingExtra[key]
|
var defaultExtra = activity.defaultLineSpacingExtra[key]
|
||||||
if (defaultExtra == null) {
|
if (defaultExtra == null) {
|
||||||
defaultExtra = sample.lineSpacingExtra
|
defaultExtra = sample.lineSpacingExtra
|
||||||
defaultLineSpacingExtra[key] = defaultExtra
|
activity.defaultLineSpacingExtra[key] = defaultExtra
|
||||||
}
|
}
|
||||||
var defaultMultiplier = defaultLineSpacingMultiplier[key]
|
var defaultMultiplier = activity.defaultLineSpacingMultiplier[key]
|
||||||
if (defaultMultiplier == null) {
|
if (defaultMultiplier == null) {
|
||||||
defaultMultiplier = sample.lineSpacingMultiplier
|
defaultMultiplier = sample.lineSpacingMultiplier
|
||||||
defaultLineSpacingMultiplier[key] = defaultMultiplier
|
activity.defaultLineSpacingMultiplier[key] = defaultMultiplier
|
||||||
}
|
}
|
||||||
|
|
||||||
val size = item.captionFontSize.invoke(activity)
|
val size = item.captionFontSize.invoke(activity)
|
||||||
|
@ -705,7 +724,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
fun showColor() {
|
fun showColor() {
|
||||||
val item = item ?: return
|
val item = item ?: return
|
||||||
val ip = item.pref.cast<IntPref>() ?: return
|
val ip = item.pref.cast<IntPref>() ?: return
|
||||||
val c = ip(pref)
|
val c = ip(activity.pref)
|
||||||
vColor.setBackgroundColor(c)
|
vColor.setBackgroundColor(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,15 +743,15 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
when (val pi = item.pref) {
|
when (val pi = item.pref) {
|
||||||
|
|
||||||
is StringPref -> {
|
is StringPref -> {
|
||||||
pref.edit().put(pi, sv).apply()
|
activity.pref.edit().put(pi, sv).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
is FloatPref -> {
|
is FloatPref -> {
|
||||||
val fv = item.toFloat.invoke(activity, sv)
|
val fv = item.toFloat.invoke(activity, sv)
|
||||||
if (fv.isFinite()) {
|
if (fv.isFinite()) {
|
||||||
pref.edit().put(pi, fv).apply()
|
activity.pref.edit().put(pi, fv).apply()
|
||||||
} else {
|
} else {
|
||||||
pref.edit().remove(pi.key).apply()
|
activity.pref.edit().remove(pi.key).apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,7 +775,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
if (bindingBusy) return
|
if (bindingBusy) return
|
||||||
val item = item ?: return
|
val item = item ?: return
|
||||||
when (val pi = item.pref) {
|
when (val pi = item.pref) {
|
||||||
is IntPref -> pref.edit().put(pi, spSpinner.selectedItemPosition).apply()
|
is IntPref -> activity.pref.edit().put(pi, spSpinner.selectedItemPosition).apply()
|
||||||
else -> item.spinnerOnSelected.invoke(activity, spSpinner, position)
|
else -> item.spinnerOnSelected.invoke(activity, spSpinner, position)
|
||||||
}
|
}
|
||||||
item.changed.invoke(activity)
|
item.changed.invoke(activity)
|
||||||
|
@ -865,13 +884,9 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findItemViewHolder(item: AppSettingItem?): ViewHolderSettingItem? {
|
fun findItemViewHolder(item: AppSettingItem?): VhSettingItem? {
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
for (i in 0 until lvList.childCount) {
|
adapter.findVhSetting(item)
|
||||||
val view = lvList.getChildAt(i)
|
|
||||||
val holder: ViewHolderSettingItem? = view?.tag?.cast()
|
|
||||||
if (holder?.item == item) return holder
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -884,8 +899,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
}
|
}
|
||||||
|
|
||||||
// リスト内部のSwitchCompat全ての色を更新する
|
// リスト内部のSwitchCompat全ての色を更新する
|
||||||
fun setSwitchColor() =
|
fun setSwitchColor() = setSwitchColor(views.lvList)
|
||||||
setSwitchColor(lvList)
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -914,8 +928,8 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
return Float.NaN
|
return Float.NaN
|
||||||
}
|
}
|
||||||
|
|
||||||
private val defaultLineSpacingExtra = HashMap<String, Float>()
|
val defaultLineSpacingExtra = HashMap<String, Float>()
|
||||||
private val defaultLineSpacingMultiplier = HashMap<String, Float>()
|
val defaultLineSpacingMultiplier = HashMap<String, Float>()
|
||||||
|
|
||||||
private fun handleFontResult(item: AppSettingItem?, data: Intent, fileName: String) {
|
private fun handleFontResult(item: AppSettingItem?, data: Intent, fileName: String) {
|
||||||
item ?: error("handleFontResult : setting item is null")
|
item ?: error("handleFontResult : setting item is null")
|
||||||
|
|
|
@ -18,6 +18,7 @@ import jp.juggler.subwaytooter.util.CustomShareTarget
|
||||||
import jp.juggler.subwaytooter.util.openBrowser
|
import jp.juggler.subwaytooter.util.openBrowser
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
import org.jetbrains.anko.backgroundDrawable
|
import org.jetbrains.anko.backgroundDrawable
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
enum class SettingType(val id: Int) {
|
enum class SettingType(val id: Int) {
|
||||||
Path(0),
|
Path(0),
|
||||||
|
@ -33,6 +34,12 @@ enum class SettingType(val id: Int) {
|
||||||
TextWithSelector(10),
|
TextWithSelector(10),
|
||||||
CheckBox(11),
|
CheckBox(11),
|
||||||
Section(12)
|
Section(12)
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val map = values().associateBy { it.id }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppSettingItem(
|
class AppSettingItem(
|
||||||
|
@ -41,6 +48,7 @@ class AppSettingItem(
|
||||||
@StringRes val caption: Int,
|
@StringRes val caption: Int,
|
||||||
val pref: BasePref<*>? = null,
|
val pref: BasePref<*>? = null,
|
||||||
) {
|
) {
|
||||||
|
val id = idSeed.incrementAndGet()
|
||||||
|
|
||||||
@StringRes
|
@StringRes
|
||||||
var desc: Int = 0
|
var desc: Int = 0
|
||||||
|
@ -191,7 +199,11 @@ class AppSettingItem(
|
||||||
for (item in items) item.scan(block)
|
for (item in items) item.scan(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hashCode() = id
|
||||||
|
override fun equals(other: Any?) = (other as? AppSettingItem)?.id == this.id
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
var idSeed = AtomicInteger(0)
|
||||||
|
|
||||||
var SAMPLE_CCD_HEADER: AppSettingItem? = null
|
var SAMPLE_CCD_HEADER: AppSettingItem? = null
|
||||||
var SAMPLE_CCD_BODY: AppSettingItem? = null
|
var SAMPLE_CCD_BODY: AppSettingItem? = null
|
||||||
|
@ -467,7 +479,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
||||||
|
|
||||||
spinner(PrefI.ipMediaBackground, R.string.background_pattern) {
|
spinner(PrefI.ipMediaBackground, R.string.background_pattern) {
|
||||||
MediaBackgroundDrawable.Kind.values()
|
MediaBackgroundDrawable.Kind.values()
|
||||||
.filter{it.isMediaBackground}
|
.filter { it.isMediaBackground }
|
||||||
.map { it.name }
|
.map { it.name }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ListView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/lvList"
|
android:id="@+id/lvList"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
|
Loading…
Reference in New Issue