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:
Ivan Iskandar
2021-07-15 05:04:03 +07:00
committed by GitHub
parent 117fd4bd0f
commit ae97bb0445
56 changed files with 701 additions and 696 deletions

View File

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

View File

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

View File

@@ -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,
;
}
}

View File

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

View File

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

View File

@@ -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,
;
}
}

View File

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