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("com.github.tachiyomiorg:DirectionalViewPager:1.0.0")
implementation("dev.chrisbanes.insetter:insetter:0.6.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 // Conductor
val conductorVersion = "3.0.0" val conductorVersion = "3.0.0"
implementation("com.bluelinelabs:conductor:$conductorVersion") implementation("com.bluelinelabs:conductor:$conductorVersion")

View File

@ -2,9 +2,6 @@ package eu.kanade.tachiyomi.data.track.anilist
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri 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.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
@ -315,9 +312,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
calendar.timeInMillis = dateValue calendar.timeInMillis = dateValue
return buildJsonObject { return buildJsonObject {
put("year", calendar.year) put("year", calendar.get(Calendar.YEAR))
put("month", calendar.month + 1) put("month", calendar.get(Calendar.MONTH) + 1)
put("day", calendar.dayOfMonth) 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.app.Dialog
import android.os.Bundle import android.os.Bundle
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import com.afollestad.materialdialogs.MaterialDialog
import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.DialogController 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 { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!) return MaterialAlertDialogBuilder(activity!!)
.title(R.string.untrusted_extension) .setTitle(R.string.untrusted_extension)
.message(R.string.untrusted_extension_message) .setMessage(R.string.untrusted_extension_message)
.positiveButton(R.string.ext_trust) { .setPositiveButton(R.string.ext_trust) { _, _ ->
(targetController as? Listener)?.trustSignature(args.getString(SIGNATURE_KEY)!!) (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)!!) (targetController as? Listener)?.uninstallExtension(args.getString(PKGNAME_KEY)!!)
} }
.create()
} }
private companion object { private companion object {

View File

@ -3,10 +3,9 @@ package eu.kanade.tachiyomi.ui.browse.migration.search
import android.app.Dialog import android.app.Dialog
import android.os.Bundle import android.os.Bundle
import androidx.core.view.isVisible 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.Controller
import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.RouterTransaction
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
@ -89,26 +88,26 @@ class SearchController(
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val prefValue = preferences.migrateFlags().get() 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 = return MaterialAlertDialogBuilder(activity!!)
MigrationFlags.getEnabledFlagsPositions( .setTitle(R.string.migration_dialog_what_to_include)
prefValue .setMultiChoiceItems(items, selected) { _, which, checked ->
) selected[which] = checked
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)
} }
.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 != null) {
if (callingController.javaClass == SourceSearchController::class.java) { if (callingController.javaClass == SourceSearchController::class.java) {
router.popController(callingController) router.popController(callingController)
@ -116,7 +115,7 @@ class SearchController(
} }
(targetController as? SearchController)?.migrateManga(manga, newManga) (targetController as? SearchController)?.migrateManga(manga, newManga)
} }
.negativeButton(R.string.copy) { .setNegativeButton(R.string.copy) { _, _, ->
if (callingController != null) { if (callingController != null) {
if (callingController.javaClass == SourceSearchController::class.java) { if (callingController.javaClass == SourceSearchController::class.java) {
router.popController(callingController) router.popController(callingController)
@ -124,7 +123,8 @@ class SearchController(
} }
(targetController as? SearchController)?.copyManga(manga, newManga) (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.MenuItem
import android.view.View import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager 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.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.ControllerChangeType
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.chrisbanes.insetter.applyInsetter import dev.chrisbanes.insetter.applyInsetter
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
@ -238,15 +237,13 @@ class SourceController :
} }
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!) return MaterialAlertDialogBuilder(activity!!)
.title(text = source) .setTitle(source)
.listItems( .setItems(items.map { it.first }.toTypedArray()) { dialog, which ->
items = items.map { it.first },
waitForPositiveButton = false
) { dialog, which, _ ->
items[which].second() items[which].second()
dialog.dismiss() dialog.dismiss()
} }
.create()
} }
} }

View File

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

View File

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

View File

@ -2,12 +2,12 @@ package eu.kanade.tachiyomi.ui.category
import android.app.Dialog import android.app.Dialog
import android.os.Bundle import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.input.input
import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.widget.materialdialogs.setTextInput
/** /**
* Dialog to rename an existing category of the library. * 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. * @return a new dialog instance.
*/ */
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!) return MaterialAlertDialogBuilder(activity!!)
.title(R.string.action_rename_category) .setTitle(R.string.action_rename_category)
.negativeButton(android.R.string.cancel) .setTextInput(prefill = currentName) {
.input( currentName = it
hint = resources?.getString(R.string.name),
prefill = currentName
) { _, input ->
currentName = input.toString()
} }
.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.app.Dialog
import android.os.Bundle import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
@ -32,23 +31,24 @@ class ChangeMangaCategoriesDialog<T>(bundle: Bundle? = null) :
} }
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!) return MaterialAlertDialogBuilder(activity!!)
.title(R.string.action_move_category) .setTitle(R.string.action_move_category)
.negativeButton(android.R.string.cancel) .setNegativeButton(android.R.string.cancel, null)
.apply { .apply {
if (categories.isNotEmpty()) { if (categories.isNotEmpty()) {
listItemsMultiChoice( val selected = categories
items = categories.map { it.name }, .mapIndexed { i, _ -> preselected.contains(i) }
initialSelection = preselected.toIntArray(), .toBooleanArray()
allowEmptySelection = true setMultiChoiceItems(categories.map { it.name }.toTypedArray(), selected) { _, which, checked ->
) { _, selections, _ -> selected[which] = checked
val newCategories = selections.map { categories[it] } }
setPositiveButton(android.R.string.ok) { _, _ ->
val newCategories = categories.filterIndexed { i, _ -> selected[i] }
(targetController as? Listener)?.updateCategoriesForMangas(mangas, newCategories) (targetController as? Listener)?.updateCategoriesForMangas(mangas, newCategories)
} }
.positiveButton(android.R.string.ok)
} else { } else {
message(R.string.information_empty_category_dialog) setMessage(R.string.information_empty_category_dialog)
.positiveButton(R.string.action_edit_categories) { setPositiveButton(R.string.action_edit_categories) { _, _ ->
if (targetController is LibraryController) { if (targetController is LibraryController) {
val libController = targetController as LibraryController val libController = targetController as LibraryController
libController.clearSelection() libController.clearSelection()
@ -58,6 +58,7 @@ class ChangeMangaCategoriesDialog<T>(bundle: Bundle? = null) :
} }
} }
} }
.create()
} }
interface Listener { interface Listener {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,15 +2,14 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.app.Dialog import android.app.Dialog
import android.os.Bundle import android.os.Bundle
import android.widget.NumberPicker import android.view.LayoutInflater
import androidx.core.os.bundleOf 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.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.databinding.TrackChaptersDialogBinding
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -38,23 +37,9 @@ class SetTrackChaptersDialog<T> : DialogController
} }
override fun onCreateDialog(savedViewState: Bundle?): Dialog { 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 // Set initial value
np.value = item.track?.last_chapter_read ?: 0 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 // Don't allow to go from 0 to 9999
np.wrapSelectorWheel = false 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 { 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.app.Dialog
import android.os.Bundle import android.os.Bundle
import android.widget.NumberPicker import android.view.LayoutInflater
import androidx.core.os.bundleOf 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.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.databinding.TrackScoreDialogBinding
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -38,23 +37,9 @@ class SetTrackScoreDialog<T> : DialogController
} }
override fun onCreateDialog(savedViewState: Bundle?): Dialog { 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() val scores = item.service.getScoreList().toTypedArray()
np.maxValue = scores.size - 1 np.maxValue = scores.size - 1
np.displayedValues = scores np.displayedValues = scores
@ -66,7 +51,15 @@ class SetTrackScoreDialog<T> : DialogController
np.value = if (index != -1) index else 0 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 { interface Listener {

View File

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

View File

@ -5,7 +5,6 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.databinding.TrackItemBinding import eu.kanade.tachiyomi.databinding.TrackItemBinding
import eu.kanade.tachiyomi.util.view.applyElevationOverlay import eu.kanade.tachiyomi.util.view.applyElevationOverlay
import uy.kohesive.injekt.api.get
class TrackAdapter(listener: OnClickListener) : RecyclerView.Adapter<TrackHolder>() { class TrackAdapter(listener: OnClickListener) : RecyclerView.Adapter<TrackHolder>() {
@ -40,13 +39,16 @@ class TrackAdapter(listener: OnClickListener) : RecyclerView.Adapter<TrackHolder
} }
interface OnClickListener { interface OnClickListener {
fun onLogoClick(position: Int) fun onOpenInBrowserClick(position: Int)
fun onSetClick(position: Int) fun onSetClick(position: Int)
fun onTitleLongClick(position: Int) fun onTitleLongClick(position: Int)
fun onStatusClick(position: Int) fun onStatusClick(position: Int)
fun onChaptersClick(position: Int) fun onChaptersClick(position: Int)
fun onScoreClick(position: Int) fun onScoreClick(position: Int)
fun onStartDateClick(position: Int) fun onStartDateEditClick(position: Int)
fun onFinishDateClick(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.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.TrackItemBinding import eu.kanade.tachiyomi.databinding.TrackItemBinding
import eu.kanade.tachiyomi.util.view.popupMenu
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.DateFormat import java.text.DateFormat
@ -17,10 +18,9 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
preferences.dateFormat() preferences.dateFormat()
} }
init { private val listener = adapter.rowClickListener
val listener = adapter.rowClickListener
binding.logoContainer.setOnClickListener { listener.onLogoClick(bindingAdapterPosition) } init {
binding.trackSet.setOnClickListener { listener.onSetClick(bindingAdapterPosition) } binding.trackSet.setOnClickListener { listener.onSetClick(bindingAdapterPosition) }
binding.trackTitle.setOnClickListener { listener.onSetClick(bindingAdapterPosition) } binding.trackTitle.setOnClickListener { listener.onSetClick(bindingAdapterPosition) }
binding.trackTitle.setOnLongClickListener { binding.trackTitle.setOnLongClickListener {
@ -30,8 +30,6 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
binding.trackStatus.setOnClickListener { listener.onStatusClick(bindingAdapterPosition) } binding.trackStatus.setOnClickListener { listener.onStatusClick(bindingAdapterPosition) }
binding.trackChapters.setOnClickListener { listener.onChaptersClick(bindingAdapterPosition) } binding.trackChapters.setOnClickListener { listener.onChaptersClick(bindingAdapterPosition) }
binding.trackScore.setOnClickListener { listener.onScoreClick(bindingAdapterPosition) } binding.trackScore.setOnClickListener { listener.onScoreClick(bindingAdapterPosition) }
binding.trackStartDate.setOnClickListener { listener.onStartDateClick(bindingAdapterPosition) }
binding.trackFinishDate.setOnClickListener { listener.onFinishDateClick(bindingAdapterPosition) }
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -42,6 +40,7 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
binding.trackSet.isVisible = track == null binding.trackSet.isVisible = track == null
binding.trackTitle.isVisible = track != null binding.trackTitle.isVisible = track != null
binding.more.isVisible = track != null
binding.middleRow.isVisible = track != null binding.middleRow.isVisible = track != null
binding.bottomDivider.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) { if (track.started_reading_date != 0L) {
binding.trackStartDate.text = dateFormat.format(track.started_reading_date) binding.trackStartDate.text = dateFormat.format(track.started_reading_date)
binding.trackStartDate.alpha = SET_STATUS_TEXT_ALPHA 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 { } else {
binding.trackStartDate.text = ctx.getString(R.string.track_started_reading_date) binding.trackStartDate.text = ctx.getString(R.string.track_started_reading_date)
binding.trackStartDate.alpha = UNSET_STATUS_TEXT_ALPHA binding.trackStartDate.alpha = UNSET_STATUS_TEXT_ALPHA
binding.trackStartDate.setOnClickListener {
listener.onStartDateEditClick(bindingAdapterPosition)
}
} }
if (track.finished_reading_date != 0L) { if (track.finished_reading_date != 0L) {
binding.trackFinishDate.text = dateFormat.format(track.finished_reading_date) binding.trackFinishDate.text = dateFormat.format(track.finished_reading_date)
binding.trackFinishDate.alpha = SET_STATUS_TEXT_ALPHA 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 { } else {
binding.trackFinishDate.text = ctx.getString(R.string.track_finished_reading_date) binding.trackFinishDate.text = ctx.getString(R.string.track_finished_reading_date)
binding.trackFinishDate.alpha = UNSET_STATUS_TEXT_ALPHA binding.trackFinishDate.alpha = UNSET_STATUS_TEXT_ALPHA
binding.trackFinishDate.setOnClickListener {
listener.onFinishDateEditClick(bindingAdapterPosition)
}
} }
} }
binding.bottomDivider.isVisible = supportsReadingDates binding.bottomDivider.isVisible = supportsReadingDates
binding.bottomRow.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() dialog?.dismiss()
} }
} }
R.id.remove -> {
trackController.presenter.unregisterTracking(service)
dialog?.dismiss()
}
} }
true true
} }
binding!!.toolbar.menu.findItem(R.id.remove).isVisible = currentTrackUrl != null
// Create adapter // Create adapter
adapter = TrackSearchAdapter(currentTrackUrl) { which -> adapter = TrackSearchAdapter(currentTrackUrl) { which ->

View File

@ -3,9 +3,14 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior 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.database.models.Manga
import eu.kanade.tachiyomi.data.track.UnattendedTrackService import eu.kanade.tachiyomi.data.track.UnattendedTrackService
import eu.kanade.tachiyomi.databinding.TrackControllerBinding 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.base.controller.openInBrowser
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.lang.launchIO 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.lang.withUIContext
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
@ -23,13 +29,13 @@ import uy.kohesive.injekt.api.get
class TrackSheet( class TrackSheet(
val controller: MangaController, val controller: MangaController,
val manga: Manga, val manga: Manga,
val fragmentManager: FragmentManager,
private val sourceManager: SourceManager = Injekt.get() private val sourceManager: SourceManager = Injekt.get()
) : BaseBottomSheetDialog(controller.activity!!), ) : BaseBottomSheetDialog(controller.activity!!),
TrackAdapter.OnClickListener, TrackAdapter.OnClickListener,
SetTrackStatusDialog.Listener, SetTrackStatusDialog.Listener,
SetTrackChaptersDialog.Listener, SetTrackChaptersDialog.Listener,
SetTrackScoreDialog.Listener, SetTrackScoreDialog.Listener {
SetTrackReadingDatesDialog.Listener {
private lateinit var binding: TrackControllerBinding 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 val track = adapter.getItem(position)?.track ?: return
if (track.tracking_url.isNotBlank()) { if (track.tracking_url.isNotBlank()) {
@ -81,7 +87,7 @@ class TrackSheet(
} }
if (!item.service.accept(sourceManager.getOrStub(manga.source))) { 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 return
} }
@ -90,9 +96,9 @@ class TrackSheet(
item.service.match(manga)?.let { track -> item.service.match(manga)?.let { track ->
controller.presenter.registerTracking(track, item.service) 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) { } 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 { } else {
@ -128,18 +134,74 @@ class TrackSheet(
SetTrackScoreDialog(controller, this, item).showDialog(controller.router) SetTrackScoreDialog(controller, this, item).showDialog(controller.router)
} }
override fun onStartDateClick(position: Int) { override fun onStartDateEditClick(position: Int) {
val item = adapter.getItem(position) ?: return val item = adapter.getItem(position) ?: return
if (item.track == null) 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 val item = adapter.getItem(position) ?: return
if (item.track == null) 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) { override fun setStatus(item: TrackItem, selection: Int) {
@ -154,13 +216,6 @@ class TrackSheet(
controller.presenter.setTrackerScore(item, score) 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? { fun getSearchDialog(): TrackSearchDialog? {
return controller.router.getControllerWithTag(TAG_SEARCH_CONTROLLER) as? 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 android.os.Bundle
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -133,10 +133,10 @@ class AboutController : SettingsController(), NoToolbarElevationController {
) )
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!) return MaterialAlertDialogBuilder(activity!!)
.title(res = R.string.update_check_notification_update_available) .setTitle(R.string.update_check_notification_update_available)
.message(text = args.getString(BODY_KEY) ?: "") .setMessage(args.getString(BODY_KEY) ?: "")
.positiveButton(R.string.update_check_confirm) { .setPositiveButton(R.string.update_check_confirm) { _, _ ->
val appContext = applicationContext val appContext = applicationContext
if (appContext != null) { if (appContext != null) {
// Start download // Start download
@ -144,7 +144,8 @@ class AboutController : SettingsController(), NoToolbarElevationController {
UpdaterService.start(appContext, url) UpdaterService.start(appContext, url)
} }
} }
.negativeButton(R.string.update_check_ignore) .setNegativeButton(R.string.update_check_ignore, null)
.create()
} }
private companion object { private companion object {

View File

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

View File

@ -9,7 +9,7 @@ import android.view.MenuItem
import android.view.View import android.view.View
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.chrisbanes.insetter.applyInsetter import dev.chrisbanes.insetter.applyInsetter
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -221,12 +221,13 @@ class HistoryController :
class ClearHistoryDialogController : DialogController() { class ClearHistoryDialogController : DialogController() {
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!) return MaterialAlertDialogBuilder(activity!!)
.message(R.string.clear_history_confirmation) .setMessage(R.string.clear_history_confirmation)
.positiveButton(android.R.string.ok) { .setPositiveButton(android.R.string.ok) { _, _ ->
(targetController as? HistoryController)?.clearHistory() (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.app.Dialog
import android.os.Bundle import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Controller
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.History import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga 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) setOptionDescription(R.string.dialog_with_checkbox_reset)
} }
return MaterialDialog(activity) return MaterialAlertDialogBuilder(activity)
.title(R.string.action_remove) .setTitle(R.string.action_remove)
.customView(view = dialogCheckboxView, horizontalPadding = true) .setView(dialogCheckboxView)
.positiveButton(R.string.action_remove) { onPositive(dialogCheckboxView.isChecked()) } .setPositiveButton(R.string.action_remove) { _, _ -> onPositive(dialogCheckboxView.isChecked()) }
.negativeButton(android.R.string.cancel) .setNegativeButton(android.R.string.cancel, null)
.create()
} }
private fun onPositive(checked: Boolean) { private fun onPositive(checked: Boolean) {

View File

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

View File

@ -8,7 +8,7 @@ import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.preference.PreferenceScreen 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.BuildConfig
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.cache.ChapterCache
@ -184,12 +184,13 @@ class SettingsAdvancedController : SettingsController() {
class ClearDatabaseDialogController : DialogController() { class ClearDatabaseDialogController : DialogController() {
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!) return MaterialAlertDialogBuilder(activity!!)
.message(R.string.clear_database_confirmation) .setMessage(R.string.clear_database_confirmation)
.positiveButton(android.R.string.ok) { .setPositiveButton(android.R.string.ok) { _, _ ->
(targetController as? SettingsAdvancedController)?.clearDatabase() (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.os.Bundle
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.BackupConst import eu.kanade.tachiyomi.data.backup.BackupConst
@ -246,29 +246,34 @@ class SettingsBackupController : SettingsController() {
R.string.history R.string.history
) )
.map { activity.getString(it) } .map { activity.getString(it) }
val selected = options.map { true }.toBooleanArray()
return MaterialDialog(activity) return MaterialAlertDialogBuilder(activity)
.title(R.string.pref_create_backup) .setTitle(R.string.backup_choice)
.message(R.string.backup_choice) .setMultiChoiceItems(options.toTypedArray(), selected) { dialog, which, checked ->
.listItemsMultiChoice( if (which == 0) {
items = options, (dialog as AlertDialog).listView.setItemChecked(which, true)
disabledIndices = intArrayOf(0), } else {
initialSelection = intArrayOf(0, 1, 2, 3, 4) selected[which] = checked
) { _, positions, _ -> }
}
.setPositiveButton(R.string.action_create) { _, _ ->
var flags = 0 var flags = 0
for (i in 1 until positions.size) { selected.forEachIndexed { i, checked ->
when (positions[i]) { if (checked) {
when (i) {
1 -> flags = flags or BackupCreateService.BACKUP_CATEGORY 1 -> flags = flags or BackupCreateService.BACKUP_CATEGORY
2 -> flags = flags or BackupCreateService.BACKUP_CHAPTER 2 -> flags = flags or BackupCreateService.BACKUP_CHAPTER
3 -> flags = flags or BackupCreateService.BACKUP_TRACK 3 -> flags = flags or BackupCreateService.BACKUP_TRACK
4 -> flags = flags or BackupCreateService.BACKUP_HISTORY 4 -> flags = flags or BackupCreateService.BACKUP_HISTORY
} }
} }
}
(targetController as? SettingsBackupController)?.createBackup(flags) (targetController as? SettingsBackupController)?.createBackup(flags)
} }
.positiveButton(R.string.action_create) .setNegativeButton(android.R.string.cancel, null)
.negativeButton(android.R.string.cancel) .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" }}" message += "\n\n${activity.getString(R.string.backup_restore_missing_trackers)}\n${results.missingTrackers.joinToString("\n") { "- $it" }}"
} }
MaterialDialog(activity) MaterialAlertDialogBuilder(activity)
.title(R.string.pref_restore_backup) .setTitle(R.string.pref_restore_backup)
.message(text = message) .setMessage(message)
.positiveButton(R.string.action_restore) { .setPositiveButton(R.string.action_restore) { _, _ ->
BackupRestoreService.start(activity, uri, type) BackupRestoreService.start(activity, uri, type)
} }
.create()
} catch (e: Exception) { } catch (e: Exception) {
MaterialDialog(activity) MaterialAlertDialogBuilder(activity)
.title(R.string.invalid_backup_file) .setTitle(R.string.invalid_backup_file)
.message(text = e.message) .setMessage(e.message)
.positiveButton(android.R.string.cancel) .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.net.toUri
import androidx.core.text.buildSpannedString import androidx.core.text.buildSpannedString
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.afollestad.materialdialogs.list.listItemsSingleChoice
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper 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.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateCheckBox import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
import eu.kanade.tachiyomi.widget.materialdialogs.listItemsQuadStateMultiChoice import eu.kanade.tachiyomi.widget.materialdialogs.setQuadStateMultiChoiceItems
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -194,20 +193,22 @@ class SettingsDownloadController : SettingsController() {
val activity = activity!! val activity = activity!!
val currentDir = preferences.downloadsDirectory().get() val currentDir = preferences.downloadsDirectory().get()
val externalDirs = (getExternalDirs() + File(activity.getString(R.string.custom_dir))).map(File::toString) 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) return MaterialAlertDialogBuilder(activity)
.listItemsSingleChoice( .setTitle(R.string.pref_download_directory)
items = externalDirs, .setSingleChoiceItems(externalDirs.toTypedArray(), selectedIndex) { _, which ->
initialSelection = selectedIndex selectedIndex = which
) { _, position, text -> }
.setPositiveButton(android.R.string.ok) { _, _ ->
val target = targetController as? SettingsDownloadController val target = targetController as? SettingsDownloadController
if (position == externalDirs.lastIndex) { if (selectedIndex == externalDirs.lastIndex) {
target?.customDirectorySelected() target?.customDirectorySelected()
} else { } else {
target?.predefinedDirectorySelected(text.toString()) target?.predefinedDirectorySelected(externalDirs[selectedIndex])
} }
} }
.create()
} }
private fun getExternalDirs(): List<File> { private fun getExternalDirs(): List<File> {
@ -230,30 +231,33 @@ class SettingsDownloadController : SettingsController() {
val categories = listOf(Category.createDefault()) + dbCategories val categories = listOf(Category.createDefault()) + dbCategories
val items = categories.map { it.name } val items = categories.map { it.name }
val preselected = categories var selected = categories
.map { .map {
when (it.id.toString()) { when (it.id.toString()) {
in preferences.downloadNewCategories().get() -> QuadStateCheckBox.State.CHECKED.ordinal in preferences.downloadNewCategories().get() -> QuadStateTextView.State.CHECKED.ordinal
in preferences.downloadNewCategoriesExclude().get() -> QuadStateCheckBox.State.INVERSED.ordinal in preferences.downloadNewCategoriesExclude().get() -> QuadStateTextView.State.INVERSED.ordinal
else -> QuadStateCheckBox.State.UNCHECKED.ordinal else -> QuadStateTextView.State.UNCHECKED.ordinal
} }
} }
.toIntArray() .toIntArray()
return MaterialDialog(activity!!) return MaterialAlertDialogBuilder(activity!!)
.title(R.string.categories) .setTitle(R.string.categories)
.message(R.string.pref_download_new_categories_details) .setMessage(R.string.pref_download_new_categories_details)
.listItemsQuadStateMultiChoice( .setQuadStateMultiChoiceItems(
items = items, items = items,
initialSelected = preselected initialSelected = selected
) { selections -> ) { selections ->
val included = selections selected = selections
.mapIndexed { index, value -> if (value == QuadStateCheckBox.State.CHECKED.ordinal) index else null } }
.setPositiveButton(android.R.string.ok) { _, _ ->
val included = selected
.mapIndexed { index, value -> if (value == QuadStateTextView.State.CHECKED.ordinal) index else null }
.filterNotNull() .filterNotNull()
.map { categories[it].id.toString() } .map { categories[it].id.toString() }
.toSet() .toSet()
val excluded = selections val excluded = selected
.mapIndexed { index, value -> if (value == QuadStateCheckBox.State.INVERSED.ordinal) index else null } .mapIndexed { index, value -> if (value == QuadStateTextView.State.INVERSED.ordinal) index else null }
.filterNotNull() .filterNotNull()
.map { categories[it].id.toString() } .map { categories[it].id.toString() }
.toSet() .toSet()
@ -261,8 +265,8 @@ class SettingsDownloadController : SettingsController() {
preferences.downloadNewCategories().set(included) preferences.downloadNewCategories().set(included)
preferences.downloadNewCategoriesExclude().set(excluded) preferences.downloadNewCategoriesExclude().set(excluded)
} }
.positiveButton(android.R.string.ok) .setNegativeButton(android.R.string.cancel, null)
.negativeButton(android.R.string.cancel) .create()
} }
} }

View File

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

View File

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

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.util.lang
import java.text.DateFormat import java.text.DateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Date import java.util.Date
import java.util.TimeZone
fun Date.toDateTimestampString(dateFormatter: DateFormat): String { fun Date.toDateTimestampString(dateFormatter: DateFormat): String {
val date = dateFormatter.format(this) val date = dateFormatter.format(this)
@ -43,3 +44,28 @@ fun Long.toCalendar(): Calendar? {
cal.timeInMillis = this cal.timeInMillis = this
return cal 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 package eu.kanade.tachiyomi.widget.materialdialogs
import android.view.View import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.MaterialDialog import eu.kanade.tachiyomi.databinding.DialogQuadstatemultichoiceItemBinding
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
private object CheckPayload private object CheckPayload
private object InverseCheckPayload private object InverseCheckPayload
@ -17,15 +12,13 @@ private object UncheckPayload
typealias QuadStateMultiChoiceListener = (indices: IntArray) -> Unit typealias QuadStateMultiChoiceListener = (indices: IntArray) -> Unit
internal class QuadStateMultiChoiceDialogAdapter( internal class QuadStateMultiChoiceDialogAdapter(
private var dialog: MaterialDialog,
internal var items: List<CharSequence>, internal var items: List<CharSequence>,
disabledItems: IntArray?, disabledItems: IntArray?,
initialSelected: IntArray, initialSelected: IntArray,
internal var selection: QuadStateMultiChoiceListener internal var listener: QuadStateMultiChoiceListener
) : RecyclerView.Adapter<QuadStateMultiChoiceViewHolder>(), ) : RecyclerView.Adapter<QuadStateMultiChoiceViewHolder>() {
DialogAdapter<CharSequence, QuadStateMultiChoiceListener> {
private val states = QuadStateCheckBox.State.values() private val states = QuadStateTextView.State.values()
private var currentSelection: IntArray = initialSelected private var currentSelection: IntArray = initialSelected
set(value) { set(value) {
@ -34,15 +27,15 @@ internal class QuadStateMultiChoiceDialogAdapter(
previousSelection.forEachIndexed { index, previous -> previousSelection.forEachIndexed { index, previous ->
val current = value[index] val current = value[index]
when { 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 // This value was selected
notifyItemChanged(index, CheckPayload) 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 // This value was inverse selected
notifyItemChanged(index, InverseCheckPayload) 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 // This value was unselected
notifyItemChanged(index, UncheckPayload) notifyItemChanged(index, UncheckPayload)
} }
@ -54,26 +47,24 @@ internal class QuadStateMultiChoiceDialogAdapter(
internal fun itemClicked(index: Int) { internal fun itemClicked(index: Int) {
val newSelection = this.currentSelection.toMutableList() val newSelection = this.currentSelection.toMutableList()
newSelection[index] = when (currentSelection[index]) { newSelection[index] = when (currentSelection[index]) {
QuadStateCheckBox.State.CHECKED.ordinal -> QuadStateCheckBox.State.INVERSED.ordinal QuadStateTextView.State.CHECKED.ordinal -> QuadStateTextView.State.INVERSED.ordinal
QuadStateCheckBox.State.INVERSED.ordinal -> QuadStateCheckBox.State.UNCHECKED.ordinal QuadStateTextView.State.INVERSED.ordinal -> QuadStateTextView.State.UNCHECKED.ordinal
// INDETERMINATE or UNCHECKED // INDETERMINATE or UNCHECKED
else -> QuadStateCheckBox.State.CHECKED.ordinal else -> QuadStateTextView.State.CHECKED.ordinal
} }
this.currentSelection = newSelection.toIntArray() this.currentSelection = newSelection.toIntArray()
listener(currentSelection)
} }
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
): QuadStateMultiChoiceViewHolder { ): QuadStateMultiChoiceViewHolder {
val listItemView: View = parent.inflate(dialog.windowContext, R.layout.md_listitem_quadstatemultichoice) return QuadStateMultiChoiceViewHolder(
val viewHolder = QuadStateMultiChoiceViewHolder( itemBinding = DialogQuadstatemultichoiceItemBinding
itemView = listItemView, .inflate(LayoutInflater.from(parent.context), parent, false),
adapter = this adapter = this
) )
viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content)
return viewHolder
} }
override fun getItemCount() = items.size override fun getItemCount() = items.size
@ -83,14 +74,8 @@ internal class QuadStateMultiChoiceDialogAdapter(
position: Int position: Int
) { ) {
holder.isEnabled = !disabledIndices.contains(position) holder.isEnabled = !disabledIndices.contains(position)
holder.controlView.state = states[currentSelection[position]] holder.controlView.state = states[currentSelection[position]]
holder.titleView.text = items[position] holder.controlView.text = items[position]
holder.itemView.background = dialog.getItemSelector()
if (dialog.bodyFont != null) {
holder.titleView.typeface = dialog.bodyFont
}
} }
override fun onBindViewHolder( override fun onBindViewHolder(
@ -100,88 +85,18 @@ internal class QuadStateMultiChoiceDialogAdapter(
) { ) {
when (payloads.firstOrNull()) { when (payloads.firstOrNull()) {
CheckPayload -> { CheckPayload -> {
holder.controlView.state = QuadStateCheckBox.State.CHECKED holder.controlView.state = QuadStateTextView.State.CHECKED
return return
} }
InverseCheckPayload -> { InverseCheckPayload -> {
holder.controlView.state = QuadStateCheckBox.State.INVERSED holder.controlView.state = QuadStateTextView.State.INVERSED
return return
} }
UncheckPayload -> { UncheckPayload -> {
holder.controlView.state = QuadStateCheckBox.State.UNCHECKED holder.controlView.state = QuadStateTextView.State.UNCHECKED
return return
} }
} }
super.onBindViewHolder(holder, position, payloads) 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 package eu.kanade.tachiyomi.widget.materialdialogs
import android.view.View import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.databinding.DialogQuadstatemultichoiceItemBinding
internal class QuadStateMultiChoiceViewHolder( internal class QuadStateMultiChoiceViewHolder(
itemView: View, itemBinding: DialogQuadstatemultichoiceItemBinding,
private val adapter: QuadStateMultiChoiceDialogAdapter private val adapter: QuadStateMultiChoiceDialogAdapter
) : RecyclerView.ViewHolder(itemView), View.OnClickListener { ) : RecyclerView.ViewHolder(itemBinding.root), View.OnClickListener {
init { init {
itemView.setOnClickListener(this) itemView.setOnClickListener(this)
} }
val controlView: QuadStateCheckBox = itemView.findViewById(R.id.md_quad_state_control) val controlView = itemBinding.quadStateControl
val titleView: TextView = itemView.findViewById(R.id.md_quad_state_title)
var isEnabled: Boolean var isEnabled: Boolean
get() = itemView.isEnabled get() = itemView.isEnabled
set(value) { set(value) {
itemView.isEnabled = value itemView.isEnabled = value
controlView.isEnabled = value controlView.isEnabled = value
titleView.isEnabled = value
} }
override fun onClick(view: View) = adapter.itemClicked(bindingAdapterPosition) 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.LayoutInflater
import android.view.View import android.view.View
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.ControllerChangeType
import com.dd.processbutton.iml.ActionProcessButton import com.dd.processbutton.iml.ActionProcessButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.PrefAccountLoginBinding import eu.kanade.tachiyomi.databinding.PrefAccountLoginBinding
@ -28,15 +27,13 @@ abstract class LoginDialogPreference(
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
binding = PrefAccountLoginBinding.inflate(LayoutInflater.from(activity!!)) binding = PrefAccountLoginBinding.inflate(LayoutInflater.from(activity!!))
onViewCreated(binding!!.root)
val titleName = activity!!.getString(getTitleName()) val titleName = activity!!.getString(getTitleName())
val dialog = MaterialDialog(activity!!) return MaterialAlertDialogBuilder(activity!!)
.title(text = activity!!.getString(R.string.login_title, titleName)) .setTitle(activity!!.getString(R.string.login_title, titleName))
.customView(view = binding!!.root) .setView(binding!!.root)
.negativeButton(android.R.string.cancel) .setNegativeButton(android.R.string.cancel, null)
.create()
onViewCreated(dialog.view)
return dialog
} }
fun onViewCreated(view: View) { 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" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical"
android:paddingHorizontal="24dp"
android:paddingTop="16dp">
<TextView <TextView
android:id="@+id/description" android:id="@+id/description"
@ -15,7 +17,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="-5dp" android:layout_marginStart="-5dp"
android:layout_marginTop="24dp" android:layout_marginTop="16dp"
android:layout_marginEnd="0dp" /> android:layout_marginEnd="0dp" />
</LinearLayout> </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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:orientation="horizontal"> android:orientation="horizontal"
android:paddingVertical="8dp">
<androidx.appcompat.widget.AppCompatImageButton <androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/btn_decrease_10" 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:id="@+id/logo_container"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#2E51A2" app:cardBackgroundColor="#2E51A2"
app:cardElevation="0dp" app:cardElevation="0dp"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MaterialCardView.Tracker"> app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MaterialCardView.Tracker">
@ -54,8 +51,9 @@
<TextView <TextView
android:id="@+id/track_title" android:id="@+id/track_title"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_weight="1"
android:ellipsize="end" android:ellipsize="end"
android:foreground="?attr/selectableItemBackgroundBorderless" android:foreground="?attr/selectableItemBackgroundBorderless"
android:gravity="center_vertical" android:gravity="center_vertical"
@ -64,6 +62,16 @@
android:textAppearance="?attr/textAppearanceSubtitle1" android:textAppearance="?attr/textAppearanceSubtitle1"
tools:text="Title" /> 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>
</LinearLayout> </LinearLayout>
@ -123,7 +131,7 @@
android:id="@+id/vert_divider_2" android:id="@+id/vert_divider_2"
android:layout_width="1dp" android:layout_width="1dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?android:divider"/> android:background="?android:divider" />
<TextView <TextView
android:id="@+id/track_score" android:id="@+id/track_score"

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" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> 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 <item
android:id="@+id/done" android:id="@+id/done"
android:enabled="false" android:enabled="false"

View File

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

View File

@ -300,21 +300,6 @@
<item name="android:textSize">15sp</item> <item name="android:textSize">15sp</item>
</style> </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--> <!--Shape Appearance-->
<!--================--> <!--================-->

View File

@ -67,14 +67,6 @@
<item name="elevationOverlayEnabled">false</item> <item name="elevationOverlayEnabled">false</item>
<item name="lightSystemBarsOnPrimary">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--> <!-- Custom Attributes-->
<item name="colorFilterActive">@color/filter_light</item> <item name="colorFilterActive">@color/filter_light</item>
</style> </style>