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
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 701 additions and 696 deletions

View file

@ -245,12 +245,6 @@ dependencies {
implementation("com.github.tachiyomiorg:DirectionalViewPager:1.0.0")
implementation("dev.chrisbanes.insetter:insetter:0.6.0")
// 3.2.0+ introduces weird UI blinking or cut off issues on some devices
val materialDialogsVersion = "3.1.1"
implementation("com.afollestad.material-dialogs:core:$materialDialogsVersion")
implementation("com.afollestad.material-dialogs:input:$materialDialogsVersion")
implementation("com.afollestad.material-dialogs:datetime:$materialDialogsVersion")
// Conductor
val conductorVersion = "3.0.0"
implementation("com.bluelinelabs:conductor:$conductorVersion")

View file

@ -2,9 +2,6 @@ package eu.kanade.tachiyomi.data.track.anilist
import android.net.Uri
import androidx.core.net.toUri
import com.afollestad.date.dayOfMonth
import com.afollestad.date.month
import com.afollestad.date.year
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.POST
@ -315,9 +312,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
val calendar = Calendar.getInstance()
calendar.timeInMillis = dateValue
return buildJsonObject {
put("year", calendar.year)
put("month", calendar.month + 1)
put("day", calendar.dayOfMonth)
put("year", calendar.get(Calendar.YEAR))
put("month", calendar.get(Calendar.MONTH) + 1)
put("day", calendar.get(Calendar.DAY_OF_MONTH))
}
}

View file

@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.ui.browse.extension
import android.app.Dialog
import android.os.Bundle
import androidx.core.os.bundleOf
import com.afollestad.materialdialogs.MaterialDialog
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.DialogController
@ -21,15 +21,16 @@ class ExtensionTrustDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.title(R.string.untrusted_extension)
.message(R.string.untrusted_extension_message)
.positiveButton(R.string.ext_trust) {
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.untrusted_extension)
.setMessage(R.string.untrusted_extension_message)
.setPositiveButton(R.string.ext_trust) { _, _ ->
(targetController as? Listener)?.trustSignature(args.getString(SIGNATURE_KEY)!!)
}
.negativeButton(R.string.ext_uninstall) {
.setNegativeButton(R.string.ext_uninstall) { _, _ ->
(targetController as? Listener)?.uninstallExtension(args.getString(PKGNAME_KEY)!!)
}
.create()
}
private companion object {

View file

@ -3,10 +3,9 @@ package eu.kanade.tachiyomi.ui.browse.migration.search
import android.app.Dialog
import android.os.Bundle
import androidx.core.view.isVisible
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.RouterTransaction
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
@ -89,26 +88,26 @@ class SearchController(
@Suppress("DEPRECATION")
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val prefValue = preferences.migrateFlags().get()
val enabledFlagsPositions = MigrationFlags.getEnabledFlagsPositions(prefValue)
val items = MigrationFlags.titles
.map { resources?.getString(it) }
.toTypedArray()
val selected = items
.mapIndexed { i, _ -> enabledFlagsPositions.contains(i) }
.toBooleanArray()
val preselected =
MigrationFlags.getEnabledFlagsPositions(
prefValue
)
return MaterialDialog(activity!!)
.title(R.string.migration_dialog_what_to_include)
.listItemsMultiChoice(
items = MigrationFlags.titles.map { resources?.getString(it) as CharSequence },
initialSelection = preselected.toIntArray()
) { _, positions, _ ->
// Save current settings for the next time
val newValue =
MigrationFlags.getFlagsFromPositions(
positions.toTypedArray()
)
preferences.migrateFlags().set(newValue)
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.migration_dialog_what_to_include)
.setMultiChoiceItems(items, selected) { _, which, checked ->
selected[which] = checked
}
.positiveButton(R.string.migrate) {
.setPositiveButton(R.string.migrate) { _, _ ->
// Save current settings for the next time
val selectedIndices = mutableListOf<Int>()
selected.forEachIndexed { i, b -> if (b) selectedIndices.add(i) }
val newValue = MigrationFlags.getFlagsFromPositions(selectedIndices.toTypedArray())
preferences.migrateFlags().set(newValue)
if (callingController != null) {
if (callingController.javaClass == SourceSearchController::class.java) {
router.popController(callingController)
@ -116,7 +115,7 @@ class SearchController(
}
(targetController as? SearchController)?.migrateManga(manga, newManga)
}
.negativeButton(R.string.copy) {
.setNegativeButton(R.string.copy) { _, _, ->
if (callingController != null) {
if (callingController.javaClass == SourceSearchController::class.java) {
router.popController(callingController)
@ -124,7 +123,8 @@ class SearchController(
}
(targetController as? SearchController)?.copyManga(manga, newManga)
}
.neutralButton(android.R.string.cancel)
.setNeutralButton(android.R.string.cancel, null)
.create()
}
}

View file

@ -9,10 +9,9 @@ import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItems
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.chrisbanes.insetter.applyInsetter
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
@ -238,15 +237,13 @@ class SourceController :
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.title(text = source)
.listItems(
items = items.map { it.first },
waitForPositiveButton = false
) { dialog, which, _ ->
return MaterialAlertDialogBuilder(activity!!)
.setTitle(source)
.setItems(items.map { it.first }.toTypedArray()) { dialog, which ->
items[which].second()
dialog.dismiss()
}
.create()
}
}

View file

@ -13,8 +13,7 @@ import androidx.core.view.updatePadding
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItems
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.snackbar.Snackbar
import com.tfcporciuncula.flow.Preference
@ -589,11 +588,9 @@ open class BrowseSourceController(bundle: Bundle) :
val manga = (adapter?.getItem(position) as? SourceItem?)?.manga ?: return
if (manga.favorite) {
MaterialDialog(activity)
.listItems(
items = listOf(activity.getString(R.string.remove_from_library)),
waitForPositiveButton = false
) { _, which, _ ->
MaterialAlertDialogBuilder(activity)
.setTitle(manga.title)
.setItems(arrayOf(activity.getString(R.string.remove_from_library))) { _, which ->
when (which) {
0 -> {
presenter.changeMangaFavorite(manga)

View file

@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.ui.category
import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.input.input
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.widget.materialdialogs.setTextInput
/**
* Dialog to create a new category for the library.
@ -30,18 +30,16 @@ class CategoryCreateDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
* @return a new dialog instance.
*/
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.title(R.string.action_add_category)
.negativeButton(android.R.string.cancel)
.input(
hint = resources?.getString(R.string.name),
prefill = currentName
) { _, input ->
currentName = input.toString()
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.action_add_category)
.setTextInput(prefill = currentName) {
currentName = it
}
.positiveButton(android.R.string.ok) {
.setPositiveButton(android.R.string.ok) { _, _ ->
(targetController as? Listener)?.createCategory(currentName)
}
.setNegativeButton(android.R.string.cancel, null)
.create()
}
interface Listener {

View file

@ -2,12 +2,12 @@ package eu.kanade.tachiyomi.ui.category
import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.input.input
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.widget.materialdialogs.setTextInput
/**
* Dialog to rename an existing category of the library.
@ -35,16 +35,14 @@ class CategoryRenameDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
* @return a new dialog instance.
*/
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.title(R.string.action_rename_category)
.negativeButton(android.R.string.cancel)
.input(
hint = resources?.getString(R.string.name),
prefill = currentName
) { _, input ->
currentName = input.toString()
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.action_rename_category)
.setTextInput(prefill = currentName) {
currentName = it
}
.positiveButton(android.R.string.ok) { onPositive() }
.setPositiveButton(android.R.string.ok) { _, _ -> onPositive() }
.setNegativeButton(android.R.string.cancel, null)
.create()
}
/**

View file

@ -2,9 +2,8 @@ package eu.kanade.tachiyomi.ui.library
import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
@ -32,23 +31,24 @@ class ChangeMangaCategoriesDialog<T>(bundle: Bundle? = null) :
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.title(R.string.action_move_category)
.negativeButton(android.R.string.cancel)
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.action_move_category)
.setNegativeButton(android.R.string.cancel, null)
.apply {
if (categories.isNotEmpty()) {
listItemsMultiChoice(
items = categories.map { it.name },
initialSelection = preselected.toIntArray(),
allowEmptySelection = true
) { _, selections, _ ->
val newCategories = selections.map { categories[it] }
val selected = categories
.mapIndexed { i, _ -> preselected.contains(i) }
.toBooleanArray()
setMultiChoiceItems(categories.map { it.name }.toTypedArray(), selected) { _, which, checked ->
selected[which] = checked
}
setPositiveButton(android.R.string.ok) { _, _ ->
val newCategories = categories.filterIndexed { i, _ -> selected[i] }
(targetController as? Listener)?.updateCategoriesForMangas(mangas, newCategories)
}
.positiveButton(android.R.string.ok)
} else {
message(R.string.information_empty_category_dialog)
.positiveButton(R.string.action_edit_categories) {
setMessage(R.string.information_empty_category_dialog)
setPositiveButton(R.string.action_edit_categories) { _, _ ->
if (targetController is LibraryController) {
val libController = targetController as LibraryController
libController.clearSelection()
@ -58,6 +58,7 @@ class ChangeMangaCategoriesDialog<T>(bundle: Bundle? = null) :
}
}
}
.create()
}
interface Listener {

View file

@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.library
import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.ui.base.controller.DialogController
@ -20,15 +20,16 @@ class ChangeMangaCoverDialog<T>(bundle: Bundle? = null) :
@Suppress("DEPRECATION")
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.title(R.string.action_edit_cover)
.positiveButton(R.string.action_edit) {
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.action_edit_cover)
.setPositiveButton(R.string.action_edit) { _, _ ->
(targetController as? Listener)?.openMangaCoverPicker(manga)
}
.negativeButton(android.R.string.cancel)
.neutralButton(R.string.action_delete) {
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.action_delete) { _, _ ->
(targetController as? Listener)?.deleteMangaCover(manga)
}
.create()
}
interface Listener {

View file

@ -2,9 +2,8 @@ package eu.kanade.tachiyomi.ui.library
import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.ui.base.controller.DialogController
@ -20,18 +19,22 @@ class DeleteLibraryMangasDialog<T>(bundle: Bundle? = null) :
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.title(R.string.action_remove)
.listItemsMultiChoice(
R.array.delete_selected_mangas,
initialSelection = intArrayOf(0)
) { _, selections, _ ->
val deleteFromLibrary = 0 in selections
val deleteChapters = 1 in selections
val items = resources!!.getStringArray(R.array.delete_selected_mangas)
val selected = items
.mapIndexed { i, _ -> i == 0 }
.toBooleanArray()
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.action_remove)
.setMultiChoiceItems(items, selected) { _, which, checked ->
selected[which] = checked
}
.setPositiveButton(android.R.string.ok) { _, _ ->
val deleteFromLibrary = selected[0]
val deleteChapters = selected[1]
(targetController as? Listener)?.deleteMangas(mangas, deleteFromLibrary, deleteChapters)
}
.positiveButton(android.R.string.ok)
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
interface Listener {

View file

@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.main
import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.DialogController
@ -12,11 +12,12 @@ class WhatsNewDialogController(bundle: Bundle? = null) : DialogController(bundle
@Suppress("DEPRECATION")
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.title(text = activity!!.getString(R.string.updated_version, BuildConfig.VERSION_NAME))
.positiveButton(android.R.string.ok)
.neutralButton(R.string.whats_new) {
return MaterialAlertDialogBuilder(activity!!)
.setTitle(activity!!.getString(R.string.updated_version, BuildConfig.VERSION_NAME))
.setPositiveButton(android.R.string.ok, null)
.setNeutralButton(R.string.whats_new) { _, _ ->
openInBrowser("https://github.com/tachiyomiorg/tachiyomi/releases/tag/v${BuildConfig.VERSION_NAME}")
}
.create()
}
}

View file

@ -289,7 +289,7 @@ class MangaController :
}
}
trackSheet = TrackSheet(this, manga!!)
trackSheet = TrackSheet(this, manga!!, (activity as MainActivity).supportFragmentManager)
updateFilterIconState()
}

View file

@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.manga.chapter
import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.DialogController
@ -15,12 +15,13 @@ class DeleteChaptersDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.message(R.string.confirm_delete_chapters)
.positiveButton(android.R.string.ok) {
return MaterialAlertDialogBuilder(activity!!)
.setMessage(R.string.confirm_delete_chapters)
.setPositiveButton(android.R.string.ok) { _, _ ->
(targetController as? Listener)?.deleteChapters()
}
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
interface Listener {

View file

@ -3,9 +3,8 @@ package eu.kanade.tachiyomi.ui.manga.chapter
import android.app.Dialog
import android.os.Bundle
import androidx.core.os.bundleOf
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.widget.DialogCustomDownloadView
@ -57,13 +56,14 @@ class DownloadCustomChaptersDialog<T> : DialogController
// Build dialog.
// when positive dialog is pressed call custom listener.
return MaterialDialog(activity)
.title(R.string.custom_download)
.customView(view = view, scrollable = true)
.positiveButton(android.R.string.ok) {
return MaterialAlertDialogBuilder(activity)
.setTitle(R.string.custom_download)
.setView(view)
.setPositiveButton(android.R.string.ok) { _, _ ->
(targetController as? Listener)?.downloadCustomChapters(view.amount)
}
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
interface Listener {

View file

@ -3,8 +3,7 @@ package eu.kanade.tachiyomi.ui.manga.chapter
import android.app.Dialog
import android.os.Bundle
import androidx.core.os.bundleOf
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.ui.base.controller.DialogController
@ -24,13 +23,10 @@ class SetChapterSettingsDialog(bundle: Bundle? = null) : DialogController(bundle
setOptionDescription(R.string.also_set_chapter_settings_for_library)
}
return MaterialDialog(activity!!)
.title(R.string.chapter_settings)
.customView(
view = view,
horizontalPadding = true
)
.positiveButton(android.R.string.ok) {
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.chapter_settings)
.setView(view)
.setPositiveButton(android.R.string.ok) { _, _ ->
ChapterSettingsHelper.setGlobalSettings(args.getSerializable(MANGA_KEY)!! as Manga)
if (view.isChecked()) {
ChapterSettingsHelper.updateAllMangasWithGlobalDefaults()
@ -38,7 +34,8 @@ class SetChapterSettingsDialog(bundle: Bundle? = null) : DialogController(bundle
activity?.toast(activity!!.getString(R.string.chapter_settings_updated))
}
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
private companion object {

View file

@ -2,15 +2,14 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.app.Dialog
import android.os.Bundle
import android.widget.NumberPicker
import android.view.LayoutInflater
import androidx.core.os.bundleOf
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.afollestad.materialdialogs.customview.getCustomView
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.databinding.TrackChaptersDialogBinding
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -38,23 +37,9 @@ class SetTrackChaptersDialog<T> : DialogController
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val item = item
val pickerView = TrackChaptersDialogBinding.inflate(LayoutInflater.from(activity!!))
val np = pickerView.chaptersPicker
val dialog = MaterialDialog(activity!!)
.title(R.string.chapters)
.customView(R.layout.track_chapters_dialog, dialogWrapContent = false)
.positiveButton(android.R.string.ok) { dialog ->
val view = dialog.getCustomView()
// Remove focus to update selected number
val np: NumberPicker = view.findViewById(R.id.chapters_picker)
np.clearFocus()
listener.setChaptersRead(item, np.value)
}
.negativeButton(android.R.string.cancel)
val view = dialog.getCustomView()
val np: NumberPicker = view.findViewById(R.id.chapters_picker)
// Set initial value
np.value = item.track?.last_chapter_read ?: 0
@ -66,7 +51,15 @@ class SetTrackChaptersDialog<T> : DialogController
// Don't allow to go from 0 to 9999
np.wrapSelectorWheel = false
return dialog
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.chapters)
.setView(pickerView.root)
.setPositiveButton(android.R.string.ok) { _, _ ->
np.clearFocus()
listener.setChaptersRead(item, np.value)
}
.setNegativeButton(android.R.string.cancel, null)
.create()
}
interface Listener {

View file

@ -1,87 +0,0 @@
package eu.kanade.tachiyomi.ui.manga.track
import android.app.Dialog
import android.os.Bundle
import androidx.core.os.bundleOf
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.datetime.datePicker
import com.bluelinelabs.conductor.Controller
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Calendar
class SetTrackReadingDatesDialog<T> : DialogController
where T : Controller {
private val item: TrackItem
private val dateToUpdate: ReadingDate
private lateinit var listener: Listener
constructor(target: T, listener: Listener, dateToUpdate: ReadingDate, item: TrackItem) : super(
bundleOf(KEY_ITEM_TRACK to item.track)
) {
targetController = target
this.listener = listener
this.item = item
this.dateToUpdate = dateToUpdate
}
@Suppress("unused")
constructor(bundle: Bundle) : super(bundle) {
val track = bundle.getSerializable(KEY_ITEM_TRACK) as Track
val service = Injekt.get<TrackManager>().getService(track.sync_id)!!
item = TrackItem(track, service)
dateToUpdate = ReadingDate.Start
}
@Suppress("DEPRECATION")
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.title(
when (dateToUpdate) {
ReadingDate.Start -> R.string.track_started_reading_date
ReadingDate.Finish -> R.string.track_finished_reading_date
}
)
.datePicker(currentDate = getCurrentDate()) { _, date ->
listener.setReadingDate(item, dateToUpdate, date.timeInMillis)
}
.neutralButton(R.string.action_remove) {
listener.setReadingDate(item, dateToUpdate, 0L)
}
}
private fun getCurrentDate(): Calendar {
// Today if no date is set, otherwise the already set date
return Calendar.getInstance().apply {
item.track?.let {
val date = when (dateToUpdate) {
ReadingDate.Start -> it.started_reading_date
ReadingDate.Finish -> it.finished_reading_date
}
if (date != 0L) {
timeInMillis = date
}
}
}
}
interface Listener {
fun setReadingDate(item: TrackItem, type: ReadingDate, date: Long)
}
enum class ReadingDate {
Start,
Finish
}
companion object {
private const val KEY_ITEM_TRACK = "SetTrackReadingDatesDialog.item.track"
}
}

View file

@ -2,15 +2,14 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.app.Dialog
import android.os.Bundle
import android.widget.NumberPicker
import android.view.LayoutInflater
import androidx.core.os.bundleOf
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.afollestad.materialdialogs.customview.getCustomView
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.databinding.TrackScoreDialogBinding
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -38,23 +37,9 @@ class SetTrackScoreDialog<T> : DialogController
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val item = item
val pickerView = TrackScoreDialogBinding.inflate(LayoutInflater.from(activity!!))
val np = pickerView.scorePicker
val dialog = MaterialDialog(activity!!)
.title(R.string.score)
.customView(R.layout.track_score_dialog, dialogWrapContent = false)
.positiveButton(android.R.string.ok) { dialog ->
val view = dialog.getCustomView()
// Remove focus to update selected number
val np: NumberPicker = view.findViewById(R.id.score_picker)
np.clearFocus()
listener.setScore(item, np.value)
}
.negativeButton(android.R.string.cancel)
val view = dialog.getCustomView()
val np: NumberPicker = view.findViewById(R.id.score_picker)
val scores = item.service.getScoreList().toTypedArray()
np.maxValue = scores.size - 1
np.displayedValues = scores
@ -66,7 +51,15 @@ class SetTrackScoreDialog<T> : DialogController
np.value = if (index != -1) index else 0
}
return dialog
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.score)
.setView(pickerView.root)
.setPositiveButton(android.R.string.ok) { _, _ ->
np.clearFocus()
listener.setScore(item, np.value)
}
.setNegativeButton(android.R.string.cancel, null)
.create()
}
interface Listener {

View file

@ -3,9 +3,8 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.app.Dialog
import android.os.Bundle
import androidx.core.os.bundleOf
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsSingleChoice
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager
@ -36,22 +35,20 @@ class SetTrackStatusDialog<T> : DialogController
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val item = item
val statusList = item.service.getStatusList()
val statusString = statusList.map { item.service.getStatus(it) }
val selectedIndex = statusList.indexOf(item.track?.status)
var selectedIndex = statusList.indexOf(item.track?.status)
return MaterialDialog(activity!!)
.title(R.string.status)
.negativeButton(android.R.string.cancel)
.listItemsSingleChoice(
items = statusString,
initialSelection = selectedIndex,
waitForPositiveButton = false
) { dialog, position, _ ->
listener.setStatus(item, position)
dialog.dismiss()
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.status)
.setSingleChoiceItems(statusString.toTypedArray(), selectedIndex) { _, which ->
selectedIndex = which
}
.setPositiveButton(android.R.string.ok) { _, _ ->
listener.setStatus(item, selectedIndex)
}
.setNegativeButton(android.R.string.cancel, null)
.create()
}
interface Listener {

View file

@ -5,7 +5,6 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.databinding.TrackItemBinding
import eu.kanade.tachiyomi.util.view.applyElevationOverlay
import uy.kohesive.injekt.api.get
class TrackAdapter(listener: OnClickListener) : RecyclerView.Adapter<TrackHolder>() {
@ -40,13 +39,16 @@ class TrackAdapter(listener: OnClickListener) : RecyclerView.Adapter<TrackHolder
}
interface OnClickListener {
fun onLogoClick(position: Int)
fun onOpenInBrowserClick(position: Int)
fun onSetClick(position: Int)
fun onTitleLongClick(position: Int)
fun onStatusClick(position: Int)
fun onChaptersClick(position: Int)
fun onScoreClick(position: Int)
fun onStartDateClick(position: Int)
fun onFinishDateClick(position: Int)
fun onStartDateEditClick(position: Int)
fun onStartDateRemoveClick(position: Int)
fun onFinishDateEditClick(position: Int)
fun onFinishDateRemoveClick(position: Int)
fun onRemoveItemClick(position: Int)
}
}

View file

@ -6,6 +6,7 @@ import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.TrackItemBinding
import eu.kanade.tachiyomi.util.view.popupMenu
import uy.kohesive.injekt.injectLazy
import java.text.DateFormat
@ -17,10 +18,9 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
preferences.dateFormat()
}
init {
val listener = adapter.rowClickListener
private val listener = adapter.rowClickListener
binding.logoContainer.setOnClickListener { listener.onLogoClick(bindingAdapterPosition) }
init {
binding.trackSet.setOnClickListener { listener.onSetClick(bindingAdapterPosition) }
binding.trackTitle.setOnClickListener { listener.onSetClick(bindingAdapterPosition) }
binding.trackTitle.setOnLongClickListener {
@ -30,8 +30,6 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
binding.trackStatus.setOnClickListener { listener.onStatusClick(bindingAdapterPosition) }
binding.trackChapters.setOnClickListener { listener.onChaptersClick(bindingAdapterPosition) }
binding.trackScore.setOnClickListener { listener.onScoreClick(bindingAdapterPosition) }
binding.trackStartDate.setOnClickListener { listener.onStartDateClick(bindingAdapterPosition) }
binding.trackFinishDate.setOnClickListener { listener.onFinishDateClick(bindingAdapterPosition) }
}
@SuppressLint("SetTextI18n")
@ -42,6 +40,7 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
binding.trackSet.isVisible = track == null
binding.trackTitle.isVisible = track != null
binding.more.isVisible = track != null
binding.middleRow.isVisible = track != null
binding.bottomDivider.isVisible = track != null
@ -77,20 +76,55 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
if (track.started_reading_date != 0L) {
binding.trackStartDate.text = dateFormat.format(track.started_reading_date)
binding.trackStartDate.alpha = SET_STATUS_TEXT_ALPHA
binding.trackStartDate.setOnClickListener {
it.popupMenu(R.menu.track_item_date) {
when (itemId) {
R.id.action_edit -> listener.onStartDateEditClick(bindingAdapterPosition)
R.id.action_remove -> listener.onStartDateRemoveClick(bindingAdapterPosition)
}
}
}
} else {
binding.trackStartDate.text = ctx.getString(R.string.track_started_reading_date)
binding.trackStartDate.alpha = UNSET_STATUS_TEXT_ALPHA
binding.trackStartDate.setOnClickListener {
listener.onStartDateEditClick(bindingAdapterPosition)
}
}
if (track.finished_reading_date != 0L) {
binding.trackFinishDate.text = dateFormat.format(track.finished_reading_date)
binding.trackFinishDate.alpha = SET_STATUS_TEXT_ALPHA
binding.trackFinishDate.setOnClickListener {
it.popupMenu(R.menu.track_item_date) {
when (itemId) {
R.id.action_edit -> listener.onFinishDateEditClick(bindingAdapterPosition)
R.id.action_remove -> listener.onFinishDateRemoveClick(bindingAdapterPosition)
}
}
}
} else {
binding.trackFinishDate.text = ctx.getString(R.string.track_finished_reading_date)
binding.trackFinishDate.alpha = UNSET_STATUS_TEXT_ALPHA
binding.trackFinishDate.setOnClickListener {
listener.onFinishDateEditClick(bindingAdapterPosition)
}
}
}
binding.bottomDivider.isVisible = supportsReadingDates
binding.bottomRow.isVisible = supportsReadingDates
binding.more.setOnClickListener {
it.popupMenu(R.menu.track_item) {
when (itemId) {
R.id.action_open_in_browser -> {
listener.onOpenInBrowserClick(bindingAdapterPosition)
}
R.id.action_remove -> {
listener.onRemoveItemClick(bindingAdapterPosition)
}
}
}
}
}
}

View file

@ -74,14 +74,9 @@ class TrackSearchDialog : DialogController {
dialog?.dismiss()
}
}
R.id.remove -> {
trackController.presenter.unregisterTracking(service)
dialog?.dismiss()
}
}
true
}
binding!!.toolbar.menu.findItem(R.id.remove).isVisible = currentTrackUrl != null
// Create adapter
adapter = TrackSearchAdapter(currentTrackUrl) { which ->

View file

@ -3,9 +3,14 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
import eu.kanade.tachiyomi.R.string
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.DateValidatorPointBackward
import com.google.android.material.datepicker.DateValidatorPointForward
import com.google.android.material.datepicker.MaterialDatePicker
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.track.UnattendedTrackService
import eu.kanade.tachiyomi.databinding.TrackControllerBinding
@ -13,6 +18,7 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.toUtcCalendar
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.toast
@ -23,13 +29,13 @@ import uy.kohesive.injekt.api.get
class TrackSheet(
val controller: MangaController,
val manga: Manga,
val fragmentManager: FragmentManager,
private val sourceManager: SourceManager = Injekt.get()
) : BaseBottomSheetDialog(controller.activity!!),
TrackAdapter.OnClickListener,
SetTrackStatusDialog.Listener,
SetTrackChaptersDialog.Listener,
SetTrackScoreDialog.Listener,
SetTrackReadingDatesDialog.Listener {
SetTrackScoreDialog.Listener {
private lateinit var binding: TrackControllerBinding
@ -63,7 +69,7 @@ class TrackSheet(
}
}
override fun onLogoClick(position: Int) {
override fun onOpenInBrowserClick(position: Int) {
val track = adapter.getItem(position)?.track ?: return
if (track.tracking_url.isNotBlank()) {
@ -81,7 +87,7 @@ class TrackSheet(
}
if (!item.service.accept(sourceManager.getOrStub(manga.source))) {
controller.presenter.view?.applicationContext?.toast(string.source_unsupported)
controller.presenter.view?.applicationContext?.toast(R.string.source_unsupported)
return
}
@ -90,9 +96,9 @@ class TrackSheet(
item.service.match(manga)?.let { track ->
controller.presenter.registerTracking(track, item.service)
}
?: withUIContext { controller.presenter.view?.applicationContext?.toast(string.error_no_match) }
?: withUIContext { controller.presenter.view?.applicationContext?.toast(R.string.error_no_match) }
} catch (e: Exception) {
withUIContext { controller.presenter.view?.applicationContext?.toast(string.error_no_match) }
withUIContext { controller.presenter.view?.applicationContext?.toast(R.string.error_no_match) }
}
}
} else {
@ -128,18 +134,74 @@ class TrackSheet(
SetTrackScoreDialog(controller, this, item).showDialog(controller.router)
}
override fun onStartDateClick(position: Int) {
override fun onStartDateEditClick(position: Int) {
val item = adapter.getItem(position) ?: return
if (item.track == null) return
SetTrackReadingDatesDialog(controller, this, SetTrackReadingDatesDialog.ReadingDate.Start, item).showDialog(controller.router)
val selection = item.track.started_reading_date.toUtcCalendar()?.timeInMillis
?: MaterialDatePicker.todayInUtcMilliseconds()
// No time travellers allowed
val constraints = CalendarConstraints.Builder().apply {
val finishedMillis = item.track.finished_reading_date.toUtcCalendar()?.timeInMillis
if (finishedMillis != null) {
setValidator(DateValidatorPointBackward.before(finishedMillis))
}
}.build()
val picker = MaterialDatePicker.Builder.datePicker()
.setTitleText(R.string.track_started_reading_date)
.setSelection(selection)
.setCalendarConstraints(constraints)
.build()
picker.addOnPositiveButtonClickListener {
controller.presenter.setTrackerStartDate(item, it)
}
picker.show(fragmentManager, null)
}
override fun onFinishDateClick(position: Int) {
override fun onFinishDateEditClick(position: Int) {
val item = adapter.getItem(position) ?: return
if (item.track == null) return
SetTrackReadingDatesDialog(controller, this, SetTrackReadingDatesDialog.ReadingDate.Finish, item).showDialog(controller.router)
val selection = item.track.finished_reading_date.toUtcCalendar()?.timeInMillis
?: MaterialDatePicker.todayInUtcMilliseconds()
// No time travellers allowed
val constraints = CalendarConstraints.Builder().apply {
val startMillis = item.track.started_reading_date.toUtcCalendar()?.timeInMillis
if (startMillis != null) {
setValidator(DateValidatorPointForward.from(item.track.started_reading_date))
}
}.build()
val picker = MaterialDatePicker.Builder.datePicker()
.setTitleText(R.string.track_finished_reading_date)
.setSelection(selection)
.setCalendarConstraints(constraints)
.build()
picker.addOnPositiveButtonClickListener {
controller.presenter.setTrackerFinishDate(item, it)
}
picker.show(fragmentManager, null)
}
override fun onStartDateRemoveClick(position: Int) {
val item = adapter.getItem(position) ?: return
if (item.track == null) return
controller.presenter.setTrackerStartDate(item, 0)
}
override fun onFinishDateRemoveClick(position: Int) {
val item = adapter.getItem(position) ?: return
if (item.track == null) return
controller.presenter.setTrackerFinishDate(item, 0)
}
override fun onRemoveItemClick(position: Int) {
val item = adapter.getItem(position) ?: return
if (item.track == null) return
controller.presenter.unregisterTracking(item.service)
}
override fun setStatus(item: TrackItem, selection: Int) {
@ -154,13 +216,6 @@ class TrackSheet(
controller.presenter.setTrackerScore(item, score)
}
override fun setReadingDate(item: TrackItem, type: SetTrackReadingDatesDialog.ReadingDate, date: Long) {
when (type) {
SetTrackReadingDatesDialog.ReadingDate.Start -> controller.presenter.setTrackerStartDate(item, date)
SetTrackReadingDatesDialog.ReadingDate.Finish -> controller.presenter.setTrackerFinishDate(item, date)
}
}
fun getSearchDialog(): TrackSearchDialog? {
return controller.router.getControllerWithTag(TAG_SEARCH_CONTROLLER) as? TrackSearchDialog
}

View file

@ -4,7 +4,7 @@ import android.app.Dialog
import android.os.Bundle
import androidx.core.os.bundleOf
import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.aboutlibraries.LibsBuilder
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
@ -133,10 +133,10 @@ class AboutController : SettingsController(), NoToolbarElevationController {
)
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.title(res = R.string.update_check_notification_update_available)
.message(text = args.getString(BODY_KEY) ?: "")
.positiveButton(R.string.update_check_confirm) {
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.update_check_notification_update_available)
.setMessage(args.getString(BODY_KEY) ?: "")
.setPositiveButton(R.string.update_check_confirm) { _, _ ->
val appContext = applicationContext
if (appContext != null) {
// Start download
@ -144,7 +144,8 @@ class AboutController : SettingsController(), NoToolbarElevationController {
UpdaterService.start(appContext, url)
}
}
.negativeButton(R.string.update_check_ignore)
.setNegativeButton(R.string.update_check_ignore, null)
.create()
}
private companion object {

View file

@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.reader
import android.view.LayoutInflater
import android.view.View
import com.afollestad.materialdialogs.MaterialDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.ReaderPageSheetBinding
import eu.kanade.tachiyomi.source.model.Page
@ -35,13 +35,12 @@ class ReaderPageSheet(
private fun setAsCover() {
if (page.status != Page.READY) return
MaterialDialog(activity)
.message(R.string.confirm_set_image_as_cover)
.positiveButton(android.R.string.ok) {
MaterialAlertDialogBuilder(activity)
.setMessage(R.string.confirm_set_image_as_cover)
.setPositiveButton(android.R.string.ok) { _, _ ->
activity.setAsCover(page)
dismiss()
}
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.show()
}

View file

@ -9,7 +9,7 @@ import android.view.MenuItem
import android.view.View
import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.chrisbanes.insetter.applyInsetter
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
@ -221,12 +221,13 @@ class HistoryController :
class ClearHistoryDialogController : DialogController() {
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.message(R.string.clear_history_confirmation)
.positiveButton(android.R.string.ok) {
return MaterialAlertDialogBuilder(activity!!)
.setMessage(R.string.clear_history_confirmation)
.setPositiveButton(android.R.string.ok) { _, _ ->
(targetController as? HistoryController)?.clearHistory()
}
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
}

View file

@ -2,9 +2,8 @@ package eu.kanade.tachiyomi.ui.recent.history
import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga
@ -33,11 +32,12 @@ class RemoveHistoryDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
setOptionDescription(R.string.dialog_with_checkbox_reset)
}
return MaterialDialog(activity)
.title(R.string.action_remove)
.customView(view = dialogCheckboxView, horizontalPadding = true)
.positiveButton(R.string.action_remove) { onPositive(dialogCheckboxView.isChecked()) }
.negativeButton(android.R.string.cancel)
return MaterialAlertDialogBuilder(activity)
.setTitle(R.string.action_remove)
.setView(dialogCheckboxView)
.setPositiveButton(R.string.action_remove) { _, _ -> onPositive(dialogCheckboxView.isChecked()) }
.setNegativeButton(android.R.string.cancel, null)
.create()
}
private fun onPositive(checked: Boolean) {

View file

@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.recent.updates
import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.DialogController
@ -18,12 +18,13 @@ class ConfirmDeleteChaptersDialog<T>(bundle: Bundle? = null) : DialogController(
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.message(R.string.confirm_delete_chapters)
.positiveButton(android.R.string.ok) {
return MaterialAlertDialogBuilder(activity!!)
.setMessage(R.string.confirm_delete_chapters)
.setPositiveButton(android.R.string.ok) { _, _ ->
(targetController as? Listener)?.deleteChapters(chaptersToDelete)
}
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
interface Listener {

View file

@ -8,7 +8,7 @@ import android.os.Bundle
import android.provider.Settings
import androidx.core.net.toUri
import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.ChapterCache
@ -184,12 +184,13 @@ class SettingsAdvancedController : SettingsController() {
class ClearDatabaseDialogController : DialogController() {
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
.message(R.string.clear_database_confirmation)
.positiveButton(android.R.string.ok) {
return MaterialAlertDialogBuilder(activity!!)
.setMessage(R.string.clear_database_confirmation)
.setPositiveButton(android.R.string.ok) { _, _ ->
(targetController as? SettingsAdvancedController)?.clearDatabase()
}
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
}

View file

@ -9,12 +9,12 @@ import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri
import androidx.core.os.bundleOf
import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.BackupConst
@ -246,29 +246,34 @@ class SettingsBackupController : SettingsController() {
R.string.history
)
.map { activity.getString(it) }
val selected = options.map { true }.toBooleanArray()
return MaterialDialog(activity)
.title(R.string.pref_create_backup)
.message(R.string.backup_choice)
.listItemsMultiChoice(
items = options,
disabledIndices = intArrayOf(0),
initialSelection = intArrayOf(0, 1, 2, 3, 4)
) { _, positions, _ ->
return MaterialAlertDialogBuilder(activity)
.setTitle(R.string.backup_choice)
.setMultiChoiceItems(options.toTypedArray(), selected) { dialog, which, checked ->
if (which == 0) {
(dialog as AlertDialog).listView.setItemChecked(which, true)
} else {
selected[which] = checked
}
}
.setPositiveButton(R.string.action_create) { _, _ ->
var flags = 0
for (i in 1 until positions.size) {
when (positions[i]) {
selected.forEachIndexed { i, checked ->
if (checked) {
when (i) {
1 -> flags = flags or BackupCreateService.BACKUP_CATEGORY
2 -> flags = flags or BackupCreateService.BACKUP_CHAPTER
3 -> flags = flags or BackupCreateService.BACKUP_TRACK
4 -> flags = flags or BackupCreateService.BACKUP_HISTORY
}
}
}
(targetController as? SettingsBackupController)?.createBackup(flags)
}
.positiveButton(R.string.action_create)
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
}
@ -306,17 +311,19 @@ class SettingsBackupController : SettingsController() {
message += "\n\n${activity.getString(R.string.backup_restore_missing_trackers)}\n${results.missingTrackers.joinToString("\n") { "- $it" }}"
}
MaterialDialog(activity)
.title(R.string.pref_restore_backup)
.message(text = message)
.positiveButton(R.string.action_restore) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.pref_restore_backup)
.setMessage(message)
.setPositiveButton(R.string.action_restore) { _, _ ->
BackupRestoreService.start(activity, uri, type)
}
.create()
} catch (e: Exception) {
MaterialDialog(activity)
.title(R.string.invalid_backup_file)
.message(text = e.message)
.positiveButton(android.R.string.cancel)
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.invalid_backup_file)
.setMessage(e.message)
.setPositiveButton(android.R.string.cancel, null)
.create()
}
}

View file

@ -10,8 +10,7 @@ import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.text.buildSpannedString
import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsSingleChoice
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
@ -28,8 +27,8 @@ import eu.kanade.tachiyomi.util.preference.preferenceCategory
import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateCheckBox
import eu.kanade.tachiyomi.widget.materialdialogs.listItemsQuadStateMultiChoice
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
import eu.kanade.tachiyomi.widget.materialdialogs.setQuadStateMultiChoiceItems
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import uy.kohesive.injekt.Injekt
@ -194,20 +193,22 @@ class SettingsDownloadController : SettingsController() {
val activity = activity!!
val currentDir = preferences.downloadsDirectory().get()
val externalDirs = (getExternalDirs() + File(activity.getString(R.string.custom_dir))).map(File::toString)
val selectedIndex = externalDirs.indexOfFirst { it in currentDir }
var selectedIndex = externalDirs.indexOfFirst { it in currentDir }
return MaterialDialog(activity)
.listItemsSingleChoice(
items = externalDirs,
initialSelection = selectedIndex
) { _, position, text ->
return MaterialAlertDialogBuilder(activity)
.setTitle(R.string.pref_download_directory)
.setSingleChoiceItems(externalDirs.toTypedArray(), selectedIndex) { _, which ->
selectedIndex = which
}
.setPositiveButton(android.R.string.ok) { _, _ ->
val target = targetController as? SettingsDownloadController
if (position == externalDirs.lastIndex) {
if (selectedIndex == externalDirs.lastIndex) {
target?.customDirectorySelected()
} else {
target?.predefinedDirectorySelected(text.toString())
target?.predefinedDirectorySelected(externalDirs[selectedIndex])
}
}
.create()
}
private fun getExternalDirs(): List<File> {
@ -230,30 +231,33 @@ class SettingsDownloadController : SettingsController() {
val categories = listOf(Category.createDefault()) + dbCategories
val items = categories.map { it.name }
val preselected = categories
var selected = categories
.map {
when (it.id.toString()) {
in preferences.downloadNewCategories().get() -> QuadStateCheckBox.State.CHECKED.ordinal
in preferences.downloadNewCategoriesExclude().get() -> QuadStateCheckBox.State.INVERSED.ordinal
else -> QuadStateCheckBox.State.UNCHECKED.ordinal
in preferences.downloadNewCategories().get() -> QuadStateTextView.State.CHECKED.ordinal
in preferences.downloadNewCategoriesExclude().get() -> QuadStateTextView.State.INVERSED.ordinal
else -> QuadStateTextView.State.UNCHECKED.ordinal
}
}
.toIntArray()
return MaterialDialog(activity!!)
.title(R.string.categories)
.message(R.string.pref_download_new_categories_details)
.listItemsQuadStateMultiChoice(
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.categories)
.setMessage(R.string.pref_download_new_categories_details)
.setQuadStateMultiChoiceItems(
items = items,
initialSelected = preselected
initialSelected = selected
) { selections ->
val included = selections
.mapIndexed { index, value -> if (value == QuadStateCheckBox.State.CHECKED.ordinal) index else null }
selected = selections
}
.setPositiveButton(android.R.string.ok) { _, _ ->
val included = selected
.mapIndexed { index, value -> if (value == QuadStateTextView.State.CHECKED.ordinal) index else null }
.filterNotNull()
.map { categories[it].id.toString() }
.toSet()
val excluded = selections
.mapIndexed { index, value -> if (value == QuadStateCheckBox.State.INVERSED.ordinal) index else null }
val excluded = selected
.mapIndexed { index, value -> if (value == QuadStateTextView.State.INVERSED.ordinal) index else null }
.filterNotNull()
.map { categories[it].id.toString() }
.toSet()
@ -261,8 +265,8 @@ class SettingsDownloadController : SettingsController() {
preferences.downloadNewCategories().set(included)
preferences.downloadNewCategoriesExclude().set(excluded)
}
.positiveButton(android.R.string.ok)
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
}

View file

@ -2,12 +2,11 @@ package eu.kanade.tachiyomi.ui.setting
import android.app.Dialog
import android.os.Bundle
import android.view.View
import android.view.LayoutInflater
import androidx.core.content.ContextCompat
import androidx.core.text.buildSpannedString
import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
@ -17,6 +16,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.UNMETERED_NETWORK
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.databinding.PrefLibraryColumnsBinding
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.category.CategoryController
@ -32,9 +32,8 @@ import eu.kanade.tachiyomi.util.preference.summaryRes
import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.isTablet
import eu.kanade.tachiyomi.widget.MinMaxNumberPicker
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateCheckBox
import eu.kanade.tachiyomi.widget.materialdialogs.listItemsQuadStateMultiChoice
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
import eu.kanade.tachiyomi.widget.materialdialogs.setQuadStateMultiChoiceItems
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@ -299,21 +298,21 @@ class SettingsLibraryController : SettingsController() {
private var landscape = preferences.landscapeColumns().get()
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val dialog = MaterialDialog(activity!!)
.title(R.string.pref_library_columns)
.customView(R.layout.pref_library_columns, horizontalPadding = true)
.positiveButton(android.R.string.ok) {
val binding = PrefLibraryColumnsBinding.inflate(LayoutInflater.from(activity!!))
onViewCreated(binding)
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.pref_library_columns)
.setView(binding.root)
.setPositiveButton(android.R.string.ok) { _, _ ->
preferences.portraitColumns().set(portrait)
preferences.landscapeColumns().set(landscape)
}
.negativeButton(android.R.string.cancel)
onViewCreated(dialog.view)
return dialog
.setNegativeButton(android.R.string.cancel, null)
.create()
}
fun onViewCreated(view: View) {
with(view.findViewById(R.id.portrait_columns) as MinMaxNumberPicker) {
fun onViewCreated(binding: PrefLibraryColumnsBinding) {
with(binding.portraitColumns) {
displayedValues = arrayOf(context.getString(R.string.default_columns)) +
IntRange(1, 10).map(Int::toString)
value = portrait
@ -322,7 +321,7 @@ class SettingsLibraryController : SettingsController() {
portrait = newValue
}
}
with(view.findViewById(R.id.landscape_columns) as MinMaxNumberPicker) {
with(binding.landscapeColumns) {
displayedValues = arrayOf(context.getString(R.string.default_columns)) +
IntRange(1, 10).map(Int::toString)
value = landscape
@ -344,30 +343,30 @@ class SettingsLibraryController : SettingsController() {
val categories = listOf(Category.createDefault()) + dbCategories
val items = categories.map { it.name }
val preselected = categories
var selected = categories
.map {
when (it.id.toString()) {
in preferences.libraryUpdateCategories().get() -> QuadStateCheckBox.State.CHECKED.ordinal
in preferences.libraryUpdateCategoriesExclude().get() -> QuadStateCheckBox.State.INVERSED.ordinal
else -> QuadStateCheckBox.State.UNCHECKED.ordinal
in preferences.libraryUpdateCategories().get() -> QuadStateTextView.State.CHECKED.ordinal
in preferences.libraryUpdateCategoriesExclude().get() -> QuadStateTextView.State.INVERSED.ordinal
else -> QuadStateTextView.State.UNCHECKED.ordinal
}
}
.toIntArray()
return MaterialDialog(activity!!)
.title(R.string.categories)
.message(R.string.pref_library_update_categories_details)
.listItemsQuadStateMultiChoice(
items = items,
initialSelected = preselected
) { selections ->
val included = selections
.mapIndexed { index, value -> if (value == QuadStateCheckBox.State.CHECKED.ordinal) index else null }
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.categories)
.setMessage(R.string.pref_library_update_categories_details)
.setQuadStateMultiChoiceItems(items = items, initialSelected = selected) { selections ->
selected = selections
}
.setPositiveButton(android.R.string.ok) { _, _ ->
val included = selected
.mapIndexed { index, value -> if (value == QuadStateTextView.State.CHECKED.ordinal) index else null }
.filterNotNull()
.map { categories[it].id.toString() }
.toSet()
val excluded = selections
.mapIndexed { index, value -> if (value == QuadStateCheckBox.State.INVERSED.ordinal) index else null }
val excluded = selected
.mapIndexed { index, value -> if (value == QuadStateTextView.State.INVERSED.ordinal) index else null }
.filterNotNull()
.map { categories[it].id.toString() }
.toSet()
@ -375,8 +374,8 @@ class SettingsLibraryController : SettingsController() {
preferences.libraryUpdateCategories().set(included)
preferences.libraryUpdateCategoriesExclude().set(excluded)
}
.positiveButton(android.R.string.ok)
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
}
}

View file

@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.ui.setting.track
import android.app.Dialog
import android.os.Bundle
import androidx.core.os.bundleOf
import com.afollestad.materialdialogs.MaterialDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
@ -20,14 +20,15 @@ class TrackLogoutDialog(bundle: Bundle? = null) : DialogController(bundle) {
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val serviceName = activity!!.getString(service.nameRes())
return MaterialDialog(activity!!)
.title(text = activity!!.getString(R.string.logout_title, serviceName))
.positiveButton(R.string.logout) {
return MaterialAlertDialogBuilder(activity!!)
.setTitle(activity!!.getString(R.string.logout_title, serviceName))
.setPositiveButton(R.string.logout) { _, _ ->
service.logout()
(targetController as? Listener)?.trackLogoutDialogClosed(service)
activity?.toast(R.string.logout_success)
}
.negativeButton(android.R.string.cancel)
.setNegativeButton(android.R.string.cancel, null)
.create()
}
interface Listener {

View file

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.util.lang
import java.text.DateFormat
import java.util.Calendar
import java.util.Date
import java.util.TimeZone
fun Date.toDateTimestampString(dateFormatter: DateFormat): String {
val date = dateFormatter.format(this)
@ -43,3 +44,28 @@ fun Long.toCalendar(): Calendar? {
cal.timeInMillis = this
return cal
}
/**
* Convert epoch long to Calendar instance in UTC
*
* @return UTC Calendar instance at supplied epoch time. Null if epoch was 0.
*/
fun Long.toUtcCalendar(): Calendar? {
if (this == 0L) {
return null
}
val rawCalendar = Calendar.getInstance().apply {
timeInMillis = this@toUtcCalendar
}
return Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply {
clear()
set(
rawCalendar.get(Calendar.YEAR),
rawCalendar.get(Calendar.MONTH),
rawCalendar.get(Calendar.DAY_OF_MONTH),
rawCalendar.get(Calendar.HOUR_OF_DAY),
rawCalendar.get(Calendar.MINUTE),
rawCalendar.get(Calendar.SECOND)
)
}
}

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

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>

View file

@ -2,7 +2,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:orientation="vertical"
android:paddingHorizontal="24dp"
android:paddingTop="16dp">
<TextView
android:id="@+id/description"
@ -15,7 +17,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="-5dp"
android:layout_marginTop="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="0dp" />
</LinearLayout>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="?attr/materialAlertDialogBodyTextStyle"
android:id="@+id/quad_state_control"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:minHeight="?attr/listPreferredItemHeightSmall"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
android:paddingStart="@dimen/abc_select_dialog_padding_start_material"
android:paddingEnd="?attr/dialogPreferredPadding"
android:drawablePadding="20dp"
android:ellipsize="marquee"
app:drawableStartCompat="@drawable/ic_check_box_outline_blank_24dp"
tools:text="Quad-state item" />

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:scrollIndicators="none"
tools:listitem="@layout/dialog_quadstatemultichoice_item" />

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="24dp"
android:paddingVertical="16dp">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:id="@+id/text_field"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
</FrameLayout>

View file

@ -4,7 +4,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
android:orientation="horizontal"
android:paddingVertical="8dp">
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/btn_decrease_10"

View file

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/MD_ListItem.Choice">
<eu.kanade.tachiyomi.widget.materialdialogs.QuadStateCheckBox
android:id="@+id/md_quad_state_control"
style="@style/MD_ListItem_Control" />
<com.afollestad.materialdialogs.internal.rtl.RtlTextView
android:id="@+id/md_quad_state_title"
style="@style/MD_ListItemText.Choice"
tools:text="Item" />
</LinearLayout>

View file

@ -20,9 +20,6 @@
android:id="@+id/logo_container"
android:layout_width="48dp"
android:layout_height="48dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#2E51A2"
app:cardElevation="0dp"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MaterialCardView.Tracker">
@ -54,8 +51,9 @@
<TextView
android:id="@+id/track_title"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:ellipsize="end"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:gravity="center_vertical"
@ -64,6 +62,16 @@
android:textAppearance="?attr/textAppearanceSubtitle1"
tools:text="Title" />
<ImageButton
android:id="@+id/more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/label_more"
android:padding="8dp"
android:src="@drawable/ic_more_vert_24" />
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_open_in_browser"
android:title="@string/action_open_in_browser"
app:showAsAction="never" />
<item
android:id="@+id/action_remove"
android:title="@string/action_remove"
app:showAsAction="never" />
</menu>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_edit"
android:title="@string/action_edit"
app:showAsAction="never" />
<item
android:id="@+id/action_remove"
android:title="@string/action_remove"
app:showAsAction="never" />
</menu>

View file

@ -2,14 +2,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/remove"
android:icon="@drawable/ic_delete_24dp"
android:title="@string/action_remove"
android:visible="false"
app:iconTint="?attr/colorOnToolbar"
app:showAsAction="ifRoom" />
<item
android:id="@+id/done"
android:enabled="false"

View file

@ -17,7 +17,4 @@
<dimen name="screen_edge_margin">16dp</dimen>
<dimen name="tablet_horizontal_cover_margin">128dp</dimen>
<!-- material-dialogs button radius -->
<dimen name="md_action_button_corner_radius">4dp</dimen>
</resources>

View file

@ -300,21 +300,6 @@
<item name="android:textSize">15sp</item>
</style>
<!--================================-->
<!--material-dialogs style overrides-->
<!--================================-->
<style name="MD_Light" parent="ThemeOverlay.MaterialComponents.Dialog.Alert">
<item name="md_divider_color">@color/md_divider_light_theme</item>
<item name="md_item_selector">@drawable/md_item_selector</item>
<item name="md_button_selector">@drawable/md_btn_selector</item>
</style>
<style name="MD_Dark" parent="ThemeOverlay.MaterialComponents.Dialog.Alert">
<item name="md_divider_color">@color/md_divider_dark_theme</item>
<item name="md_item_selector">@drawable/md_item_selector_dark</item>
<item name="md_button_selector">@drawable/md_btn_selector_dark</item>
</style>
<!--================-->
<!--Shape Appearance-->
<!--================-->

View file

@ -67,14 +67,6 @@
<item name="elevationOverlayEnabled">false</item>
<item name="lightSystemBarsOnPrimary">false</item>
<!-- Material Dialogs -->
<item name="md_background_color">?attr/colorSurface</item>
<item name="md_color_title">?attr/colorOnSurface</item>
<item name="md_color_content">?attr/colorOnSurface</item>
<item name="md_color_button_text">?attr/colorPrimary</item>
<item name="md_button_casing">literal</item>
<item name="md_corner_radius">@dimen/dialog_radius</item>
<!-- Custom Attributes-->
<item name="colorFilterActive">@color/filter_light</item>
</style>