mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-15 05:27:28 +01:00
Replace material-dialogs usage with Material Components' (#5423)
* Use Material Components' dialogs For all dialogs that has direct replacement. * Convert text input dialogs * Convert quad-state multi choices dialogs * Convert date picker dialogs This also changes the flow to remove selected start/finish tracking date and the track item itself * Remove material-dialogs dependencies
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.tachiyomi.databinding.DialogStubQuadstatemultichoiceBinding
|
||||
import eu.kanade.tachiyomi.databinding.DialogStubTextinputBinding
|
||||
|
||||
fun MaterialAlertDialogBuilder.setTextInput(
|
||||
hint: String? = null,
|
||||
prefill: String? = null,
|
||||
onTextChanged: (String) -> Unit
|
||||
): MaterialAlertDialogBuilder {
|
||||
val binding = DialogStubTextinputBinding.inflate(LayoutInflater.from(context))
|
||||
binding.textField.hint = hint
|
||||
binding.textField.editText?.apply {
|
||||
setText(prefill, TextView.BufferType.EDITABLE)
|
||||
doAfterTextChanged {
|
||||
onTextChanged(it?.toString() ?: "")
|
||||
}
|
||||
post {
|
||||
requestFocusFromTouch()
|
||||
context.getSystemService<InputMethodManager>()?.showSoftInput(this, 0)
|
||||
}
|
||||
}
|
||||
return setView(binding.root)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a list of items with checkboxes that supports 4 states.
|
||||
*
|
||||
* @see eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
|
||||
*/
|
||||
fun MaterialAlertDialogBuilder.setQuadStateMultiChoiceItems(
|
||||
items: List<CharSequence>,
|
||||
initialSelected: IntArray,
|
||||
disabledIndices: IntArray? = null,
|
||||
selection: QuadStateMultiChoiceListener
|
||||
): MaterialAlertDialogBuilder {
|
||||
val binding = DialogStubQuadstatemultichoiceBinding.inflate(LayoutInflater.from(context))
|
||||
binding.list.layoutManager = LinearLayoutManager(context)
|
||||
binding.list.adapter = QuadStateMultiChoiceDialogAdapter(
|
||||
items = items,
|
||||
disabledItems = disabledIndices,
|
||||
initialSelected = initialSelected,
|
||||
listener = selection
|
||||
)
|
||||
setView(binding.root)
|
||||
return this
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||
|
||||
import androidx.annotation.CheckResult
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.list.customListAdapter
|
||||
|
||||
/**
|
||||
* A variant of listItemsMultiChoice that allows for checkboxes that supports 4 states instead.
|
||||
*/
|
||||
@CheckResult
|
||||
fun MaterialDialog.listItemsQuadStateMultiChoice(
|
||||
items: List<CharSequence>,
|
||||
disabledIndices: IntArray? = null,
|
||||
initialSelected: IntArray = IntArray(items.size),
|
||||
selection: QuadStateMultiChoiceListener
|
||||
): MaterialDialog {
|
||||
return customListAdapter(
|
||||
QuadStateMultiChoiceDialogAdapter(
|
||||
dialog = this,
|
||||
items = items,
|
||||
disabledItems = disabledIndices,
|
||||
initialSelected = initialSelected,
|
||||
selection = selection
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.view.setVectorCompat
|
||||
|
||||
class QuadStateCheckBox @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
AppCompatImageView(context, attrs) {
|
||||
|
||||
var state: State = State.UNCHECKED
|
||||
set(value) {
|
||||
field = value
|
||||
updateDrawable()
|
||||
}
|
||||
|
||||
private fun updateDrawable() {
|
||||
when (state) {
|
||||
State.UNCHECKED -> setVectorCompat(R.drawable.ic_check_box_outline_blank_24dp, R.attr.colorControlNormal)
|
||||
State.INDETERMINATE -> setVectorCompat(R.drawable.ic_indeterminate_check_box_24dp, R.attr.colorAccent)
|
||||
State.CHECKED -> setVectorCompat(R.drawable.ic_check_box_24dp, R.attr.colorAccent)
|
||||
State.INVERSED -> setVectorCompat(R.drawable.ic_check_box_x_24dp, R.attr.colorAccent)
|
||||
}
|
||||
}
|
||||
|
||||
enum class State {
|
||||
UNCHECKED,
|
||||
INDETERMINATE,
|
||||
CHECKED,
|
||||
INVERSED,
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,9 @@
|
||||
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||
|
||||
import android.view.View
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.internal.list.DialogAdapter
|
||||
import com.afollestad.materialdialogs.list.getItemSelector
|
||||
import com.afollestad.materialdialogs.utils.MDUtil.inflate
|
||||
import com.afollestad.materialdialogs.utils.MDUtil.maybeSetTextColor
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.DialogQuadstatemultichoiceItemBinding
|
||||
|
||||
private object CheckPayload
|
||||
private object InverseCheckPayload
|
||||
@@ -17,15 +12,13 @@ private object UncheckPayload
|
||||
typealias QuadStateMultiChoiceListener = (indices: IntArray) -> Unit
|
||||
|
||||
internal class QuadStateMultiChoiceDialogAdapter(
|
||||
private var dialog: MaterialDialog,
|
||||
internal var items: List<CharSequence>,
|
||||
disabledItems: IntArray?,
|
||||
initialSelected: IntArray,
|
||||
internal var selection: QuadStateMultiChoiceListener
|
||||
) : RecyclerView.Adapter<QuadStateMultiChoiceViewHolder>(),
|
||||
DialogAdapter<CharSequence, QuadStateMultiChoiceListener> {
|
||||
internal var listener: QuadStateMultiChoiceListener
|
||||
) : RecyclerView.Adapter<QuadStateMultiChoiceViewHolder>() {
|
||||
|
||||
private val states = QuadStateCheckBox.State.values()
|
||||
private val states = QuadStateTextView.State.values()
|
||||
|
||||
private var currentSelection: IntArray = initialSelected
|
||||
set(value) {
|
||||
@@ -34,15 +27,15 @@ internal class QuadStateMultiChoiceDialogAdapter(
|
||||
previousSelection.forEachIndexed { index, previous ->
|
||||
val current = value[index]
|
||||
when {
|
||||
current == QuadStateCheckBox.State.CHECKED.ordinal && previous != QuadStateCheckBox.State.CHECKED.ordinal -> {
|
||||
current == QuadStateTextView.State.CHECKED.ordinal && previous != QuadStateTextView.State.CHECKED.ordinal -> {
|
||||
// This value was selected
|
||||
notifyItemChanged(index, CheckPayload)
|
||||
}
|
||||
current == QuadStateCheckBox.State.INVERSED.ordinal && previous != QuadStateCheckBox.State.INVERSED.ordinal -> {
|
||||
current == QuadStateTextView.State.INVERSED.ordinal && previous != QuadStateTextView.State.INVERSED.ordinal -> {
|
||||
// This value was inverse selected
|
||||
notifyItemChanged(index, InverseCheckPayload)
|
||||
}
|
||||
current == QuadStateCheckBox.State.UNCHECKED.ordinal && previous != QuadStateCheckBox.State.UNCHECKED.ordinal -> {
|
||||
current == QuadStateTextView.State.UNCHECKED.ordinal && previous != QuadStateTextView.State.UNCHECKED.ordinal -> {
|
||||
// This value was unselected
|
||||
notifyItemChanged(index, UncheckPayload)
|
||||
}
|
||||
@@ -54,26 +47,24 @@ internal class QuadStateMultiChoiceDialogAdapter(
|
||||
internal fun itemClicked(index: Int) {
|
||||
val newSelection = this.currentSelection.toMutableList()
|
||||
newSelection[index] = when (currentSelection[index]) {
|
||||
QuadStateCheckBox.State.CHECKED.ordinal -> QuadStateCheckBox.State.INVERSED.ordinal
|
||||
QuadStateCheckBox.State.INVERSED.ordinal -> QuadStateCheckBox.State.UNCHECKED.ordinal
|
||||
QuadStateTextView.State.CHECKED.ordinal -> QuadStateTextView.State.INVERSED.ordinal
|
||||
QuadStateTextView.State.INVERSED.ordinal -> QuadStateTextView.State.UNCHECKED.ordinal
|
||||
// INDETERMINATE or UNCHECKED
|
||||
else -> QuadStateCheckBox.State.CHECKED.ordinal
|
||||
else -> QuadStateTextView.State.CHECKED.ordinal
|
||||
}
|
||||
this.currentSelection = newSelection.toIntArray()
|
||||
listener(currentSelection)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
): QuadStateMultiChoiceViewHolder {
|
||||
val listItemView: View = parent.inflate(dialog.windowContext, R.layout.md_listitem_quadstatemultichoice)
|
||||
val viewHolder = QuadStateMultiChoiceViewHolder(
|
||||
itemView = listItemView,
|
||||
return QuadStateMultiChoiceViewHolder(
|
||||
itemBinding = DialogQuadstatemultichoiceItemBinding
|
||||
.inflate(LayoutInflater.from(parent.context), parent, false),
|
||||
adapter = this
|
||||
)
|
||||
viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content)
|
||||
|
||||
return viewHolder
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
@@ -83,14 +74,8 @@ internal class QuadStateMultiChoiceDialogAdapter(
|
||||
position: Int
|
||||
) {
|
||||
holder.isEnabled = !disabledIndices.contains(position)
|
||||
|
||||
holder.controlView.state = states[currentSelection[position]]
|
||||
holder.titleView.text = items[position]
|
||||
holder.itemView.background = dialog.getItemSelector()
|
||||
|
||||
if (dialog.bodyFont != null) {
|
||||
holder.titleView.typeface = dialog.bodyFont
|
||||
}
|
||||
holder.controlView.text = items[position]
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
@@ -100,88 +85,18 @@ internal class QuadStateMultiChoiceDialogAdapter(
|
||||
) {
|
||||
when (payloads.firstOrNull()) {
|
||||
CheckPayload -> {
|
||||
holder.controlView.state = QuadStateCheckBox.State.CHECKED
|
||||
holder.controlView.state = QuadStateTextView.State.CHECKED
|
||||
return
|
||||
}
|
||||
InverseCheckPayload -> {
|
||||
holder.controlView.state = QuadStateCheckBox.State.INVERSED
|
||||
holder.controlView.state = QuadStateTextView.State.INVERSED
|
||||
return
|
||||
}
|
||||
UncheckPayload -> {
|
||||
holder.controlView.state = QuadStateCheckBox.State.UNCHECKED
|
||||
holder.controlView.state = QuadStateTextView.State.UNCHECKED
|
||||
return
|
||||
}
|
||||
}
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
}
|
||||
|
||||
override fun positiveButtonClicked() {
|
||||
selection.invoke(currentSelection)
|
||||
}
|
||||
|
||||
override fun replaceItems(
|
||||
items: List<CharSequence>,
|
||||
listener: QuadStateMultiChoiceListener?
|
||||
) {
|
||||
this.items = items
|
||||
if (listener != null) {
|
||||
this.selection = listener
|
||||
}
|
||||
this.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun disableItems(indices: IntArray) {
|
||||
this.disabledIndices = indices
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun checkItems(indices: IntArray) {
|
||||
val newSelection = this.currentSelection.toMutableList()
|
||||
for (index in indices) {
|
||||
newSelection[index] = QuadStateCheckBox.State.CHECKED.ordinal
|
||||
}
|
||||
this.currentSelection = newSelection.toIntArray()
|
||||
}
|
||||
|
||||
override fun uncheckItems(indices: IntArray) {
|
||||
val newSelection = this.currentSelection.toMutableList()
|
||||
for (index in indices) {
|
||||
newSelection[index] = QuadStateCheckBox.State.UNCHECKED.ordinal
|
||||
}
|
||||
this.currentSelection = newSelection.toIntArray()
|
||||
}
|
||||
|
||||
override fun toggleItems(indices: IntArray) {
|
||||
val newSelection = this.currentSelection.toMutableList()
|
||||
for (index in indices) {
|
||||
if (this.disabledIndices.contains(index)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (this.currentSelection[index] != QuadStateCheckBox.State.CHECKED.ordinal) {
|
||||
newSelection[index] = QuadStateCheckBox.State.CHECKED.ordinal
|
||||
} else {
|
||||
newSelection[index] = QuadStateCheckBox.State.UNCHECKED.ordinal
|
||||
}
|
||||
}
|
||||
this.currentSelection = newSelection.toIntArray()
|
||||
}
|
||||
|
||||
override fun checkAllItems() {
|
||||
this.currentSelection = IntArray(itemCount) { QuadStateCheckBox.State.CHECKED.ordinal }
|
||||
}
|
||||
|
||||
override fun uncheckAllItems() {
|
||||
this.currentSelection = IntArray(itemCount) { QuadStateCheckBox.State.UNCHECKED.ordinal }
|
||||
}
|
||||
|
||||
override fun toggleAllChecked() {
|
||||
if (this.currentSelection.any { it != QuadStateCheckBox.State.CHECKED.ordinal }) {
|
||||
checkAllItems()
|
||||
} else {
|
||||
uncheckAllItems()
|
||||
}
|
||||
}
|
||||
|
||||
override fun isItemChecked(index: Int) = this.currentSelection[index] == QuadStateCheckBox.State.CHECKED.ordinal
|
||||
}
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.DialogQuadstatemultichoiceItemBinding
|
||||
|
||||
internal class QuadStateMultiChoiceViewHolder(
|
||||
itemView: View,
|
||||
itemBinding: DialogQuadstatemultichoiceItemBinding,
|
||||
private val adapter: QuadStateMultiChoiceDialogAdapter
|
||||
) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
|
||||
) : RecyclerView.ViewHolder(itemBinding.root), View.OnClickListener {
|
||||
init {
|
||||
itemView.setOnClickListener(this)
|
||||
}
|
||||
|
||||
val controlView: QuadStateCheckBox = itemView.findViewById(R.id.md_quad_state_control)
|
||||
val titleView: TextView = itemView.findViewById(R.id.md_quad_state_title)
|
||||
val controlView = itemBinding.quadStateControl
|
||||
|
||||
var isEnabled: Boolean
|
||||
get() = itemView.isEnabled
|
||||
set(value) {
|
||||
itemView.isEnabled = value
|
||||
controlView.isEnabled = value
|
||||
titleView.isEnabled = value
|
||||
}
|
||||
|
||||
override fun onClick(view: View) = adapter.itemClicked(bindingAdapterPosition)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import com.mikepenz.aboutlibraries.util.getThemeColor
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
class QuadStateTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
AppCompatTextView(context, attrs) {
|
||||
|
||||
var state: State = State.UNCHECKED
|
||||
set(value) {
|
||||
field = value
|
||||
updateDrawable()
|
||||
}
|
||||
|
||||
private fun updateDrawable() {
|
||||
val drawableStartId = when (state) {
|
||||
State.UNCHECKED -> R.drawable.ic_check_box_outline_blank_24dp
|
||||
State.INDETERMINATE -> R.drawable.ic_indeterminate_check_box_24dp
|
||||
State.CHECKED -> R.drawable.ic_check_box_24dp
|
||||
State.INVERSED -> R.drawable.ic_check_box_x_24dp
|
||||
}
|
||||
setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStartId, 0, 0, 0)
|
||||
|
||||
val tint = if (state == State.UNCHECKED) {
|
||||
context.getThemeColor(R.attr.colorControlNormal)
|
||||
} else {
|
||||
context.getThemeColor(R.attr.colorAccent)
|
||||
}
|
||||
if (tint != 0) {
|
||||
compoundDrawableTintList = ColorStateList.valueOf(tint)
|
||||
}
|
||||
}
|
||||
|
||||
enum class State {
|
||||
UNCHECKED,
|
||||
INDETERMINATE,
|
||||
CHECKED,
|
||||
INVERSED,
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,10 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.customview.customView
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.dd.processbutton.iml.ActionProcessButton
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.databinding.PrefAccountLoginBinding
|
||||
@@ -28,15 +27,13 @@ abstract class LoginDialogPreference(
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
binding = PrefAccountLoginBinding.inflate(LayoutInflater.from(activity!!))
|
||||
onViewCreated(binding!!.root)
|
||||
val titleName = activity!!.getString(getTitleName())
|
||||
val dialog = MaterialDialog(activity!!)
|
||||
.title(text = activity!!.getString(R.string.login_title, titleName))
|
||||
.customView(view = binding!!.root)
|
||||
.negativeButton(android.R.string.cancel)
|
||||
|
||||
onViewCreated(dialog.view)
|
||||
|
||||
return dialog
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(activity!!.getString(R.string.login_title, titleName))
|
||||
.setView(binding!!.root)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
fun onViewCreated(view: View) {
|
||||
|
||||
Reference in New Issue
Block a user