diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationSourceHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationSourceHolder.kt index 169e902c5..010df7e24 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationSourceHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationSourceHolder.kt @@ -3,18 +3,17 @@ package eu.kanade.tachiyomi.ui.browse.migration.advanced.design import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG import android.view.View import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.databinding.MigrationSourceItemBinding import eu.kanade.tachiyomi.source.icon import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder -import kotlinx.android.synthetic.main.migration_source_item.image -import kotlinx.android.synthetic.main.migration_source_item.reorder -import kotlinx.android.synthetic.main.migration_source_item.title import uy.kohesive.injekt.injectLazy class MigrationSourceHolder(view: View, val adapter: MigrationSourceAdapter) : BaseFlexibleViewHolder(view, adapter) { + val binding = MigrationSourceItemBinding.bind(view) init { - setDragHandleView(reorder) + setDragHandleView(binding.reorder) } fun bind(source: HttpSource, sourceEnabled: Boolean) { @@ -22,23 +21,23 @@ class MigrationSourceHolder(view: View, val adapter: MigrationSourceAdapter) : val isMultiLanguage = preferences.enabledLanguages().get().size > 1 // Set capitalized title. val sourceName = if (isMultiLanguage) source.toString() else source.name.capitalize() - title.text = sourceName + binding.title.text = sourceName // Update circle letter image. itemView.post { val icon = source.icon() if (icon != null) { - image.setImageDrawable(icon) + binding.image.setImageDrawable(icon) } } if (sourceEnabled) { - title.alpha = 1.0f - image.alpha = 1.0f - title.paintFlags = title.paintFlags and STRIKE_THRU_TEXT_FLAG.inv() + binding.title.alpha = 1.0f + binding.image.alpha = 1.0f + binding.title.paintFlags = binding.title.paintFlags and STRIKE_THRU_TEXT_FLAG.inv() } else { - title.alpha = DISABLED_ALPHA - image.alpha = DISABLED_ALPHA - title.paintFlags = title.paintFlags or STRIKE_THRU_TEXT_FLAG + binding.title.alpha = DISABLED_ALPHA + binding.image.alpha = DISABLED_ALPHA + binding.title.paintFlags = binding.title.paintFlags or STRIKE_THRU_TEXT_FLAG } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessHolder.kt index b19e7d394..f6d0976ab 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessHolder.kt @@ -10,6 +10,8 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.toMangaThumbnail +import eu.kanade.tachiyomi.databinding.MigrationMangaCardBinding +import eu.kanade.tachiyomi.databinding.MigrationProcessItemBinding import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction @@ -20,17 +22,6 @@ import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.view.setVectorCompat import exh.MERGED_SOURCE_ID import exh.util.await -import kotlinx.android.synthetic.main.migration_manga_card.view.gradient -import kotlinx.android.synthetic.main.migration_manga_card.view.loading_group -import kotlinx.android.synthetic.main.migration_manga_card.view.manga_chapters -import kotlinx.android.synthetic.main.migration_manga_card.view.manga_last_chapter_label -import kotlinx.android.synthetic.main.migration_manga_card.view.manga_source_label -import kotlinx.android.synthetic.main.migration_manga_card.view.thumbnail -import kotlinx.android.synthetic.main.migration_manga_card.view.title -import kotlinx.android.synthetic.main.migration_process_item.migration_manga_card_from -import kotlinx.android.synthetic.main.migration_process_item.migration_manga_card_to -import kotlinx.android.synthetic.main.migration_process_item.migration_menu -import kotlinx.android.synthetic.main.migration_process_item.skip_manga import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -51,13 +42,14 @@ class MigrationProcessHolder( private var item: MigrationProcessItem? = null private val scope = CoroutineScope(Job() + Dispatchers.Main) + private val binding = MigrationProcessItemBinding.bind(view) init { // We need to post a Runnable to show the popup to make sure that the PopupMenu is // correctly positioned. The reason being that the view may change position before the // PopupMenu is shown. - migration_menu.setOnClickListener { it.post { showPopupMenu(it) } } - skip_manga.setOnClickListener { it.post { adapter.removeManga(bindingAdapterPosition) } } + binding.migrationMenu.setOnClickListener { it.post { showPopupMenu(it) } } + binding.skipManga.setOnClickListener { it.post { adapter.removeManga(bindingAdapterPosition) } } } fun bind(item: MigrationProcessItem) { @@ -66,25 +58,25 @@ class MigrationProcessHolder( val manga = item.manga.manga() val source = item.manga.mangaSource() - migration_menu.setVectorCompat( + binding.migrationMenu.setVectorCompat( R.drawable.ic_more_vert_24dp, view.context .getResourceColor(R.attr.colorOnPrimary) ) - skip_manga.setVectorCompat( + binding.skipManga.setVectorCompat( R.drawable.ic_close_24dp, view.context.getResourceColor( R .attr.colorOnPrimary ) ) - migration_menu.isInvisible = true - skip_manga.isVisible = true - migration_manga_card_to.resetManga() + binding.migrationMenu.isInvisible = true + binding.skipManga.isVisible = true + binding.migrationMangaCardTo.resetManga() if (manga != null) { withContext(Dispatchers.Main) { - migration_manga_card_from.attachManga(manga, source) - migration_manga_card_from.clicks() + binding.migrationMangaCardFrom.attachManga(manga, source) + binding.migrationMangaCardFrom.root.clicks() .onEach { adapter.controller.router.pushController( MangaController( @@ -119,8 +111,8 @@ class MigrationProcessHolder( return@withContext } if (searchResult != null && resultSource != null) { - migration_manga_card_to.attachManga(searchResult, resultSource) - migration_manga_card_to.clicks() + binding.migrationMangaCardTo.attachManga(searchResult, resultSource) + binding.migrationMangaCardTo.root.clicks() .onEach { adapter.controller.router.pushController( MangaController( @@ -130,30 +122,30 @@ class MigrationProcessHolder( ) }.launchIn(scope) } else { - migration_manga_card_to.loading_group.isVisible = false - migration_manga_card_to.title.text = view.context.applicationContext + binding.migrationMangaCardTo.loadingGroup.isVisible = false + binding.migrationMangaCardTo.title.text = view.context.applicationContext .getString(R.string.no_alternatives_found) } - migration_menu.isVisible = true - skip_manga.isVisible = false + binding.migrationMenu.isVisible = true + binding.skipManga.isVisible = false adapter.sourceFinished() } } } } - private fun View.resetManga() { - loading_group.isVisible = true + private fun MigrationMangaCardBinding.resetManga() { + loadingGroup.isVisible = true thumbnail.setImageDrawable(null) title.text = "" - manga_source_label.text = "" - manga_chapters.text = "" - manga_chapters.isVisible = false - manga_last_chapter_label.text = "" + mangaSourceLabel.text = "" + mangaChapters.text = "" + mangaChapters.isVisible = false + mangaLastChapterLabel.text = "" } - private suspend fun View.attachManga(manga: Manga, source: Source) { - loading_group.isVisible = false + private suspend fun MigrationMangaCardBinding.attachManga(manga: Manga, source: Source) { + loadingGroup.isVisible = false GlideApp.with(view.context.applicationContext) .load(manga.toMangaThumbnail()) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) @@ -168,7 +160,7 @@ class MigrationProcessHolder( } gradient.isVisible = true - manga_source_label.text = if (source.id == MERGED_SOURCE_ID) { + mangaSourceLabel.text = if (source.id == MERGED_SOURCE_ID) { db.getMergedMangaReferences(manga.id!!).await().map { sourceManager.getOrStub(it.mangaSourceId).toString() }.distinct().joinToString() @@ -176,20 +168,20 @@ class MigrationProcessHolder( source.toString() } - val mangaChapters = db.getChapters(manga).executeAsBlocking() - manga_chapters.isVisible = true - manga_chapters.text = mangaChapters.size.toString() - val latestChapter = mangaChapters.maxByOrNull { it.chapter_number }?.chapter_number ?: -1f + val chapters = db.getChapters(manga).executeAsBlocking() + mangaChapters.isVisible = true + mangaChapters.text = chapters.size.toString() + val latestChapter = chapters.maxByOrNull { it.chapter_number }?.chapter_number ?: -1f if (latestChapter > 0f) { - manga_last_chapter_label.text = context.getString( + mangaLastChapterLabel.text = root.context.getString( R.string.latest_, DecimalFormat("#.#").format(latestChapter) ) } else { - manga_last_chapter_label.text = context.getString( + mangaLastChapterLabel.text = root.context.getString( R.string.latest_, - context.getString(R.string.unknown) + root.context.getString(R.string.unknown) ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MangaHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MangaHolder.kt index 0198bcb39..1e0dea1b9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MangaHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MangaHolder.kt @@ -9,27 +9,28 @@ import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.toMangaThumbnail +import eu.kanade.tachiyomi.databinding.SourceListItemBinding import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder -import kotlinx.android.synthetic.main.source_list_item.thumbnail -import kotlinx.android.synthetic.main.source_list_item.title class MangaHolder( view: View, adapter: FlexibleAdapter<*> ) : BaseFlexibleViewHolder(view, adapter) { + val binding = SourceListItemBinding.bind(view) + fun bind(item: MangaItem) { // Update the title of the manga. - title.text = item.manga.originalTitle + binding.title.text = item.manga.originalTitle // Create thumbnail onclick to simulate long click - thumbnail.setOnClickListener { + binding.thumbnail.setOnClickListener { // Simulate long click on this view to enter selection mode onLongClick(itemView) } // Update the cover. - GlideApp.with(itemView.context).clear(thumbnail) + GlideApp.with(itemView.context).clear(binding.thumbnail) val radius = itemView.context.resources.getDimensionPixelSize(R.dimen.card_radius) val requestOptions = RequestOptions().transform(CenterCrop(), RoundedCorners(radius)) @@ -38,6 +39,6 @@ class MangaHolder( .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .apply(requestOptions) .dontAnimate() - .into(thumbnail) + .into(binding.thumbnail) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/SelectionHeader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/SelectionHeader.kt index 8619cdfbf..2c08db628 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/SelectionHeader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/SelectionHeader.kt @@ -6,8 +6,8 @@ import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractHeaderItem import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.databinding.SourceMainControllerCardHeaderBinding import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder -import kotlinx.android.synthetic.main.source_main_controller_card_header.title /** * Item that contains the selection header. @@ -46,8 +46,9 @@ class SelectionHeader : AbstractHeaderItem() { } class Holder(view: View, adapter: FlexibleAdapter */ IFlexible /* SY <-- */>) : BaseFlexibleViewHolder(view, adapter) { + val binding = SourceMainControllerCardHeaderBinding.bind(view) init { - title.text = view.context.getString(/* SY --> */ R.string.select_a_source_to_migrate_from /* SY <-- */) + binding.title.text = view.context.getString(/* SY --> */ R.string.select_a_source_to_migrate_from /* SY <-- */) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/SourceHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/SourceHolder.kt index da5f5fbd5..7c35dd346 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/SourceHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/SourceHolder.kt @@ -3,20 +3,20 @@ package eu.kanade.tachiyomi.ui.browse.migration.sources import android.view.View import androidx.core.view.isVisible import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.databinding.SourceMainControllerCardItemBinding import eu.kanade.tachiyomi.source.icon import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder -import kotlinx.android.synthetic.main.source_main_controller_card_item.image -import kotlinx.android.synthetic.main.source_main_controller_card_item.source_latest -import kotlinx.android.synthetic.main.source_main_controller_card_item.title class SourceHolder(view: View, val adapter: SourceAdapter) : BaseFlexibleViewHolder(view, adapter) { + val binding = SourceMainControllerCardItemBinding.bind(view) + // SY --> init { - source_latest.isVisible = true - source_latest.text = view.context.getString(R.string.all) - source_latest.setOnClickListener { + binding.sourceLatest.isVisible = true + binding.sourceLatest.text = view.context.getString(R.string.all) + binding.sourceLatest.setOnClickListener { adapter.allClickListener?.onAllClick(bindingAdapterPosition) } } @@ -26,11 +26,11 @@ class SourceHolder(view: View, val adapter: SourceAdapter) : val source = item.source // Set source name - title.text = source.name + binding.title.text = source.name // Set source icon itemView.post { - image.setImageDrawable(source.icon()) + binding.image.setImageDrawable(source.icon()) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt index abb4dfa46..5467dcbb5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt @@ -4,7 +4,7 @@ import android.app.Dialog import android.net.Uri import android.os.Bundle import android.text.InputType -import android.view.View +import androidx.core.content.ContextCompat import androidx.core.view.children import androidx.core.view.isVisible import com.afollestad.materialdialogs.MaterialDialog @@ -19,21 +19,13 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.toMangaThumbnail +import eu.kanade.tachiyomi.databinding.EditMangaDialogBinding import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.util.lang.chop import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.toast import exh.util.trimOrNull -import kotlinx.android.synthetic.main.edit_manga_dialog.view.cover_layout -import kotlinx.android.synthetic.main.edit_manga_dialog.view.manga_artist -import kotlinx.android.synthetic.main.edit_manga_dialog.view.manga_author -import kotlinx.android.synthetic.main.edit_manga_dialog.view.manga_cover -import kotlinx.android.synthetic.main.edit_manga_dialog.view.manga_description -import kotlinx.android.synthetic.main.edit_manga_dialog.view.manga_genres_tags -import kotlinx.android.synthetic.main.edit_manga_dialog.view.reset_cover -import kotlinx.android.synthetic.main.edit_manga_dialog.view.reset_tags -import kotlinx.android.synthetic.main.edit_manga_dialog.view.title import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import reactivecircus.flowbinding.android.view.clicks @@ -42,7 +34,7 @@ import uy.kohesive.injekt.api.get class EditMangaDialog : DialogController { - private var dialogView: View? = null + private lateinit var binding: EditMangaDialogBinding private val manga: Manga @@ -75,8 +67,8 @@ class EditMangaDialog : DialogController { negativeButton(android.R.string.cancel) positiveButton(R.string.action_save) { onPositiveButtonClick() } } - dialogView = dialog.view - onViewCreated(dialog.view) + binding = EditMangaDialogBinding.bind(dialog.view) + onViewCreated() dialog.setOnShowListener { val dView = (it as? MaterialDialog)?.view dView?.contentLayout?.scrollView?.scrollTo(0, 0) @@ -84,101 +76,96 @@ class EditMangaDialog : DialogController { return dialog } - fun onViewCreated(view: View) { + fun onViewCreated() { val mangaThumbnail = manga.toMangaThumbnail() - GlideApp.with(view.context) + GlideApp.with(binding.root.context) .load(mangaThumbnail) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .centerCrop() - .into(view.manga_cover) + .into(binding.mangaCover) val isLocal = manga.source == LocalSource.ID if (isLocal) { if (manga.title != manga.url) { - view.title.append(manga.title) + binding.title.append(manga.title) } - view.title.hint = "${resources?.getString(R.string.title)}: ${manga.url}" - view.manga_author.append(manga.author ?: "") - view.manga_artist.append(manga.artist ?: "") - view.manga_description.append(manga.description ?: "") - view.manga_genres_tags.setChips(manga.getGenres()) + binding.title.hint = "${resources?.getString(R.string.title)}: ${manga.url}" + binding.mangaAuthor.append(manga.author ?: "") + binding.mangaArtist.append(manga.artist ?: "") + binding.mangaDescription.append(manga.description ?: "") + binding.mangaGenresTags.setChips(manga.getGenres()) } else { if (manga.title != manga.originalTitle) { - view.title.append(manga.title) + binding.title.append(manga.title) } if (manga.author != manga.originalAuthor) { - view.manga_author.append(manga.author ?: "") + binding.mangaAuthor.append(manga.author ?: "") } if (manga.artist != manga.originalArtist) { - view.manga_artist.append(manga.artist ?: "") + binding.mangaArtist.append(manga.artist ?: "") } if (manga.description != manga.originalDescription) { - view.manga_description.append(manga.description ?: "") + binding.mangaDescription.append(manga.description ?: "") } - view.manga_genres_tags.setChips(manga.getGenres()) + binding.mangaGenresTags.setChips(manga.getGenres()) - view.title.hint = "${resources?.getString(R.string.title)}: ${manga.originalTitle}" + binding.title.hint = "${resources?.getString(R.string.title)}: ${manga.originalTitle}" if (manga.originalAuthor != null) { - view.manga_author.hint = "Author: ${manga.originalAuthor}" + binding.mangaAuthor.hint = "Author: ${manga.originalAuthor}" } if (manga.originalArtist != null) { - view.manga_artist.hint = "Artist: ${manga.originalArtist}" + binding.mangaArtist.hint = "Artist: ${manga.originalArtist}" } if (manga.originalDescription != null) { - view.manga_description.hint = + binding.mangaDescription.hint = "${resources?.getString(R.string.description)}: ${manga.originalDescription?.replace( "\n", " " )?.chop(20)}" } } - view.manga_genres_tags.clearFocus() - view.cover_layout.clicks() + binding.mangaGenresTags.clearFocus() + binding.coverLayout.clicks() .onEach { infoController.changeCover() } .launchIn(infoController.scope) - view.reset_tags.clicks() + binding.resetTags.clicks() .onEach { resetTags() } .launchIn(infoController.scope) - view.reset_cover.isVisible = !isLocal - view.reset_cover.clicks() + binding.resetCover.isVisible = !isLocal + binding.resetCover.clicks() .onEach { - view.context.toast(R.string.cover_reset_toast) + binding.root.context.toast(R.string.cover_reset_toast) customCoverUri = null willResetCover = true }.launchIn(infoController.scope) } private fun resetTags() { - if (manga.genre.isNullOrBlank() || manga.source == LocalSource.ID) dialogView?.manga_genres_tags?.setChips( + if (manga.genre.isNullOrBlank() || manga.source == LocalSource.ID) binding.mangaGenresTags.setChips( emptyList() ) - else dialogView?.manga_genres_tags?.setChips(manga.getOriginalGenres()) + else binding.mangaGenresTags.setChips(manga.getOriginalGenres()) } fun updateCover(uri: Uri) { willResetCover = false - GlideApp.with(dialogView!!.context) + GlideApp.with(binding.root.context) .load(uri) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .centerCrop() - .into(dialogView!!.manga_cover) + .into(binding.mangaCover) customCoverUri = uri } - override fun onDestroyView(view: View) { - super.onDestroyView(view) - dialogView = null - } - private fun onPositiveButtonClick() { infoController.presenter.updateMangaInfo( - dialogView?.title?.text.toString(), - dialogView?.manga_author?.text.toString(), - dialogView?.manga_artist?.text.toString(), - dialogView?.manga_description?.text.toString(), - dialogView?.manga_genres_tags?.getTextStrings(), + binding.title.text.toString(), + binding.mangaAuthor.text.toString(), + binding.mangaArtist.text.toString(), + binding.mangaDescription.text.toString(), + binding.mangaGenresTags.getTextStrings(), customCoverUri, willResetCover ) @@ -204,7 +191,7 @@ class EditMangaDialog : DialogController { val addTagChip = Chip(context).apply { setText(R.string.add_tag) - chipIcon = context.getDrawable(R.drawable.ic_add_24dp) + chipIcon = ContextCompat.getDrawable(context, R.drawable.ic_add_24dp) chipIcon?.setTint(context.getResourceColor(R.attr.colorAccent)) textStartPadding = 0F diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/merged/EditMergedMangaHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/merged/EditMergedMangaHolder.kt index 9d2ae9c19..ed592a60d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/merged/EditMergedMangaHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/merged/EditMergedMangaHolder.kt @@ -5,33 +5,28 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.toMangaThumbnail +import eu.kanade.tachiyomi.databinding.EditMergedSettingsItemBinding import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.util.system.getResourceColor import exh.merged.sql.models.MergedMangaReference -import kotlinx.android.synthetic.main.edit_merged_settings_item.cover -import kotlinx.android.synthetic.main.edit_merged_settings_item.download -import kotlinx.android.synthetic.main.edit_merged_settings_item.get_chapter_updates -import kotlinx.android.synthetic.main.edit_merged_settings_item.remove -import kotlinx.android.synthetic.main.edit_merged_settings_item.reorder -import kotlinx.android.synthetic.main.edit_merged_settings_item.subtitle -import kotlinx.android.synthetic.main.edit_merged_settings_item.title import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get class EditMergedMangaHolder(view: View, val adapter: EditMergedMangaAdapter) : BaseFlexibleViewHolder(view, adapter) { lateinit var reference: MergedMangaReference + var binding = EditMergedSettingsItemBinding.bind(view) init { - setDragHandleView(reorder) - remove.setOnClickListener { + setDragHandleView(binding.reorder) + binding.remove.setOnClickListener { adapter.editMergedMangaItemListener.onDeleteClick(bindingAdapterPosition) } - get_chapter_updates.setOnClickListener { + binding.getChapterUpdates.setOnClickListener { adapter.editMergedMangaItemListener.onToggleChapterUpdatesClicked(bindingAdapterPosition) } - download.setOnClickListener { + binding.download.setOnClickListener { adapter.editMergedMangaItemListener.onToggleChapterDownloadsClicked(bindingAdapterPosition) } setHandelAlpha(adapter.isPriorityOrder) @@ -49,17 +44,17 @@ class EditMergedMangaHolder(view: View, val adapter: EditMergedMangaAdapter) : B .load(it) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .centerCrop() - .into(cover) + .into(binding.cover) } - title.text = Injekt.get().getOrStub(item.mergedMangaReference.mangaSourceId).toString() - subtitle.text = item.mergedManga?.title + binding.title.text = Injekt.get().getOrStub(item.mergedMangaReference.mangaSourceId).toString() + binding.subtitle.text = item.mergedManga?.title updateDownloadChaptersIcon(item.mergedMangaReference.downloadChapters) updateChapterUpdatesIcon(item.mergedMangaReference.getChapterUpdates) } fun setHandelAlpha(isPriorityOrder: Boolean) { - reorder.alpha = when (isPriorityOrder) { + binding.reorder.alpha = when (isPriorityOrder) { true -> 1F false -> 0.5F } @@ -70,7 +65,7 @@ class EditMergedMangaHolder(view: View, val adapter: EditMergedMangaAdapter) : B itemView.context.getResourceColor(R.attr.colorAccent) } else itemView.context.getResourceColor(R.attr.colorOnSurface) - download.drawable.setTint(color) + binding.download.drawable.setTint(color) } fun updateChapterUpdatesIcon(setTint: Boolean) { @@ -78,6 +73,6 @@ class EditMergedMangaHolder(view: View, val adapter: EditMergedMangaAdapter) : B itemView.context.getResourceColor(R.attr.colorAccent) } else itemView.context.getResourceColor(R.attr.colorOnSurface) - get_chapter_updates.drawable.setTint(color) + binding.getChapterUpdates.drawable.setTint(color) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/merged/EditMergedSettingsDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/merged/EditMergedSettingsDialog.kt index 0a4b96669..90ae1c242 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/merged/EditMergedSettingsDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/merged/EditMergedSettingsDialog.kt @@ -10,12 +10,12 @@ import com.afollestad.materialdialogs.customview.customView import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.databinding.EditMergedSettingsDialogBinding import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.system.toast import exh.MERGED_SOURCE_ID import exh.merged.sql.models.MergedMangaReference -import kotlinx.android.synthetic.main.edit_merged_settings_dialog.view.recycler import uy.kohesive.injekt.injectLazy class EditMergedSettingsDialog : DialogController, EditMergedMangaAdapter.EditMergedMangaItemListener { @@ -28,6 +28,8 @@ class EditMergedSettingsDialog : DialogController, EditMergedMangaAdapter.EditMe var mergeReference: MergedMangaReference? = null + lateinit var binding: EditMergedSettingsDialogBinding + private val db: DatabaseHelper by injectLazy() private val mangaController @@ -68,6 +70,7 @@ class EditMergedSettingsDialog : DialogController, EditMergedMangaAdapter.EditMe } fun onViewCreated(view: View) { + binding = EditMergedSettingsDialogBinding.bind(view) val mergedManga = db.getMergedMangas(manga.id!!).executeAsBlocking() val mergedReferences = db.getMergedMangaReferences(manga.id!!).executeAsBlocking() if (mergedReferences.isEmpty() || mergedReferences.size == 1) { @@ -82,8 +85,8 @@ class EditMergedSettingsDialog : DialogController, EditMergedMangaAdapter.EditMe mergedMangaAdapter = EditMergedMangaAdapter(this, isPriorityOrder) mergedHeaderAdapter = EditMergedSettingsHeaderAdapter(this, mergedMangaAdapter!!) - view.recycler.adapter = ConcatAdapter(mergedHeaderAdapter, mergedMangaAdapter) - view.recycler.layoutManager = LinearLayoutManager(view.context) + binding.recycler.adapter = ConcatAdapter(mergedHeaderAdapter, mergedMangaAdapter) + binding.recycler.layoutManager = LinearLayoutManager(view.context) mergedMangaAdapter?.isHandleDragEnabled = isPriorityOrder diff --git a/app/src/main/java/exh/ui/batchadd/BatchAddController.kt b/app/src/main/java/exh/ui/batchadd/BatchAddController.kt index de2fe05f0..eddb4f46f 100755 --- a/app/src/main/java/exh/ui/batchadd/BatchAddController.kt +++ b/app/src/main/java/exh/ui/batchadd/BatchAddController.kt @@ -10,8 +10,6 @@ import eu.kanade.tachiyomi.databinding.EhFragmentBatchAddBinding import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.util.lang.combineLatest import eu.kanade.tachiyomi.util.lang.plusAssign -import kotlinx.android.synthetic.main.eh_fragment_batch_add.view.galleries_box -import kotlinx.android.synthetic.main.eh_fragment_batch_add.view.progress_log import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import reactivecircus.flowbinding.android.view.clicks @@ -55,7 +53,7 @@ class BatchAddController : NucleusController.visibility: Int get() = throw UnsupportedOperationException() set(v) { forEach { it.visibility = v } } - private fun showProgress(target: View? = view) { - target?.apply { + private fun showProgress(target: EhFragmentBatchAddBinding = binding) { + target.apply { progressViews.visibility = View.VISIBLE inputViews.visibility = View.GONE - }?.progress_log?.text = "" + }.progressLog.text = "" } - private fun hideProgress(target: View? = view) { - target?.apply { + private fun hideProgress(target: EhFragmentBatchAddBinding = binding) { + target.apply { progressViews.visibility = View.GONE inputViews.visibility = View.VISIBLE - }?.galleries_box?.setText("", TextView.BufferType.EDITABLE) + }.galleriesBox.setText("", TextView.BufferType.EDITABLE) } private fun formatProgress(progress: Int, total: Int) = "$progress/$total" diff --git a/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt b/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt index 57e80d160..887347345 100644 --- a/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt +++ b/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt @@ -15,6 +15,7 @@ import com.afollestad.materialdialogs.MaterialDialog import com.elvishew.xlog.XLog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.databinding.EhActivityCaptchaBinding import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.source.Source @@ -23,8 +24,6 @@ import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.system.setDefaultSettings import exh.source.DelegatedHttpSource import exh.util.melt -import kotlinx.android.synthetic.main.eh_activity_captcha.toolbar -import kotlinx.android.synthetic.main.eh_activity_captcha.webview import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -59,11 +58,14 @@ class BrowserActionActivity : AppCompatActivity() { private var strictValidationStartTime: Long? = null private lateinit var credentialsObservable: Observable + private lateinit var binding: EhActivityCaptchaBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.eh_activity_captcha) + binding = EhActivityCaptchaBinding.inflate(layoutInflater) + + setContentView(binding.root) val sourceId = intent.getLongExtra(SOURCE_ID_EXTRA, -1) val originalSource = if (sourceId != -1L) sourceManager.get(sourceId) else null @@ -102,7 +104,7 @@ class BrowserActionActivity : AppCompatActivity() { val actionStr = actionName ?: "Solve captcha" - toolbar.title = if (source != null) { + binding.toolbar.title = if (source != null) { "${source.name}: $actionStr" } else actionStr @@ -115,14 +117,14 @@ class BrowserActionActivity : AppCompatActivity() { cm.setCookie(url, cookieString) } - webview.setDefaultSettings() + binding.webview.setDefaultSettings() headers.entries.find { it.key.equals("user-agent", true) }?.let { - webview.settings.userAgentString = it.value + binding.webview.settings.userAgentString = it.value } var loadedInners = 0 - webview.webChromeClient = object : WebChromeClient() { + binding.webview.webChromeClient = object : WebChromeClient() { override fun onJsAlert(view: WebView?, url: String?, message: String, result: JsResult): Boolean { if (message.startsWith("exh-")) { loadedInners++ @@ -130,13 +132,13 @@ class BrowserActionActivity : AppCompatActivity() { if (loadedInners >= 2) { // Attempt to autosolve captcha if (preferencesHelper.eh_autoSolveCaptchas().get()) { - webview.post { + binding.webview.post { // 10 seconds to auto-solve captcha strictValidationStartTime = System.currentTimeMillis() + 1000 * 10 beginSolveLoop() beginValidateCaptchaLoop() - webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE) { - webview.evaluateJavascript(SOLVE_UI_SCRIPT_SHOW, null) + binding.webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE) { + binding.webview.evaluateJavascript(SOLVE_UI_SCRIPT_SHOW, null) } } } @@ -148,7 +150,7 @@ class BrowserActionActivity : AppCompatActivity() { } } - webview.webViewClient = if (actionName == null && preferencesHelper.eh_autoSolveCaptchas().get()) { + binding.webview.webViewClient = if (actionName == null && preferencesHelper.eh_autoSolveCaptchas().get()) { // Fetch auto-solve credentials early for speed credentialsObservable = httpClient.newCall( Request.Builder() @@ -164,15 +166,15 @@ class BrowserActionActivity : AppCompatActivity() { json["token"]!!.jsonPrimitive.content }.melt() - webview.addJavascriptInterface(this@BrowserActionActivity, "exh") + binding.webview.addJavascriptInterface(this@BrowserActionActivity, "exh") AutoSolvingWebViewClient(this, verifyComplete, script, headers) } else { HeadersInjectingWebViewClient(this, verifyComplete, script, headers) } - webview.loadUrl(url, headers) + binding.webview.loadUrl(url, headers) - setSupportActionBar(toolbar) + setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) } @@ -187,7 +189,7 @@ class BrowserActionActivity : AppCompatActivity() { validateCurrentLoopId = null XLog.e(IllegalStateException("Captcha solve failure!")) withContext(Dispatchers.Main) { - webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null) + binding.webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null) MaterialDialog(this@BrowserActionActivity) .title(R.string.captcha_solve_failure) .message(R.string.captcha_solve_failure_message) @@ -205,14 +207,14 @@ class BrowserActionActivity : AppCompatActivity() { when (stage) { STAGE_CHECKBOX -> { if (result!!.toBoolean()) { - webview.postDelayed( + binding.webview.postDelayed( { getAudioButtonLocation(loopId) }, 250 ) } else { - webview.postDelayed( + binding.webview.postDelayed( { doStageCheckbox(loopId) }, @@ -227,15 +229,15 @@ class BrowserActionActivity : AppCompatActivity() { val origY = splitResult[1] val iw = splitResult[2] val ih = splitResult[3] - val x = webview.x + origX / iw * webview.width - val y = webview.y + origY / ih * webview.height + val x = binding.webview.x + origX / iw * binding.webview.width + val y = binding.webview.y + origY / ih * binding.webview.height XLog.nst().d("Found audio button coords: %f %f", x, y) simulateClick(x + 50, y + 50) - webview.post { + binding.webview.post { doStageDownloadAudio(loopId) } } else { - webview.postDelayed( + binding.webview.postDelayed( { getAudioButtonLocation(loopId) }, @@ -251,7 +253,7 @@ class BrowserActionActivity : AppCompatActivity() { .subscribe( { XLog.nst().d("Got audio transcript: $it") - webview.post { + binding.webview.post { typeResult( loopId, it!! @@ -266,7 +268,7 @@ class BrowserActionActivity : AppCompatActivity() { } ) } else { - webview.postDelayed( + binding.webview.postDelayed( { doStageDownloadAudio(loopId) }, @@ -330,7 +332,7 @@ class BrowserActionActivity : AppCompatActivity() { private fun doStageCheckbox(loopId: String) { if (loopId != currentLoopId) return - webview.evaluateJavascript( + binding.webview.evaluateJavascript( """ (function() { $CROSS_WINDOW_SCRIPT_OUTER @@ -359,7 +361,7 @@ class BrowserActionActivity : AppCompatActivity() { } private fun getAudioButtonLocation(loopId: String) { - webview.evaluateJavascript( + binding.webview.evaluateJavascript( """ (function() { $CROSS_WINDOW_SCRIPT_OUTER @@ -394,7 +396,7 @@ class BrowserActionActivity : AppCompatActivity() { } private fun doStageDownloadAudio(loopId: String) { - webview.evaluateJavascript( + binding.webview.evaluateJavascript( """ (function() { $CROSS_WINDOW_SCRIPT_OUTER @@ -422,7 +424,7 @@ class BrowserActionActivity : AppCompatActivity() { } private fun typeResult(loopId: String, result: String) { - webview.evaluateJavascript( + binding.webview.evaluateJavascript( """ (function() { $CROSS_WINDOW_SCRIPT_OUTER @@ -464,13 +466,13 @@ class BrowserActionActivity : AppCompatActivity() { if (result) { XLog.nst().d("Captcha solved!") - webview.post { - webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null) + binding.webview.post { + binding.webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null) } val asbtn = intent.getStringExtra(ASBTN_EXTRA) if (asbtn != null) { - webview.post { - webview.evaluateJavascript("(function() {document.querySelector('$asbtn').click();})();", null) + binding.webview.post { + binding.webview.evaluateJavascript("(function() {document.querySelector('$asbtn').click();})();", null) } } } else { @@ -480,7 +482,7 @@ class BrowserActionActivity : AppCompatActivity() { ) { captchaSolveFail() } else { - webview.postDelayed( + binding.webview.postDelayed( { runValidateCaptcha(loopId) }, @@ -493,7 +495,7 @@ class BrowserActionActivity : AppCompatActivity() { private fun runValidateCaptcha(loopId: String) { if (loopId != validateCurrentLoopId) return - webview.evaluateJavascript( + binding.webview.evaluateJavascript( """ (function() { $CROSS_WINDOW_SCRIPT_OUTER diff --git a/app/src/main/java/exh/widget/preference/MangaDexLoginPreference.kt b/app/src/main/java/exh/widget/preference/MangaDexLoginPreference.kt index 97ff61712..9bad7039f 100644 --- a/app/src/main/java/exh/widget/preference/MangaDexLoginPreference.kt +++ b/app/src/main/java/exh/widget/preference/MangaDexLoginPreference.kt @@ -6,9 +6,9 @@ import androidx.core.view.isVisible import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.databinding.PrefItemMangadexBinding import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.util.system.getResourceColor -import kotlinx.android.synthetic.main.pref_item_mangadex.view.* class MangaDexLoginPreference @JvmOverloads constructor( context: Context, @@ -20,25 +20,28 @@ class MangaDexLoginPreference @JvmOverloads constructor( layoutResource = R.layout.pref_item_mangadex } + var binding: PrefItemMangadexBinding? = null + private var onLoginClick: () -> Unit = {} override fun onBindViewHolder(holder: PreferenceViewHolder) { super.onBindViewHolder(holder) + binding = PrefItemMangadexBinding.bind(holder.itemView) holder.itemView.setOnClickListener { onLoginClick() } - val loginFrame = holder.itemView.login_frame + val loginFrame = binding?.loginFrame val color = if (source.isLogged()) { context.getResourceColor(R.attr.colorAccent) } else { context.getResourceColor(R.attr.colorSecondary) } - holder.itemView.login.setImageResource(R.drawable.ic_outline_people_alt_24dp) - holder.itemView.login.drawable.setTint(color) + binding?.login?.setImageResource(R.drawable.ic_outline_people_alt_24dp) + binding?.login?.drawable?.setTint(color) - loginFrame.isVisible = true - loginFrame.setOnClickListener { + loginFrame?.isVisible = true + loginFrame?.setOnClickListener { onLoginClick() } } diff --git a/app/src/main/java/exh/widget/preference/MangadexLoginDialog.kt b/app/src/main/java/exh/widget/preference/MangadexLoginDialog.kt index 74438cd51..6c54649f2 100644 --- a/app/src/main/java/exh/widget/preference/MangadexLoginDialog.kt +++ b/app/src/main/java/exh/widget/preference/MangadexLoginDialog.kt @@ -9,18 +9,13 @@ import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.customview.customView import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.track.TrackManager +import eu.kanade.tachiyomi.databinding.PrefSiteLoginTwoFactorAuthBinding import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.widget.preference.LoginDialogPreference import exh.source.getMainSource -import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.login -import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.password -import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.two_factor_check -import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.two_factor_edit -import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.two_factor_holder -import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.username import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -36,6 +31,8 @@ class MangadexLoginDialog(bundle: Bundle? = null) : LoginDialogPreference(bundle val scope = CoroutineScope(Job() + Dispatchers.Main) + var binding: PrefSiteLoginTwoFactorAuthBinding? = null + constructor(source: MangaDex) : this( bundleOf( "key" to source.id @@ -54,23 +51,24 @@ class MangadexLoginDialog(bundle: Bundle? = null) : LoginDialogPreference(bundle override fun onViewCreated(view: View) { super.onViewCreated(view) - v?.apply { - two_factor_check?.setOnCheckedChangeListener { _, isChecked -> - two_factor_holder.isVisible = isChecked + v?.let { binding = PrefSiteLoginTwoFactorAuthBinding.bind(it) } + binding?.apply { + twoFactorCheck.setOnCheckedChangeListener { _, isChecked -> + twoFactorHolder.isVisible = isChecked } } } - override fun setCredentialsOnView(view: View) = with(view) { - username.setText(service.getUsername()) - password.setText(service.getPassword()) + override fun setCredentialsOnView(view: View) { + binding?.username?.setText(service.getUsername()) + binding?.password?.setText(service.getPassword()) } override fun checkLogin() { - v?.apply { - if (username.text.isNullOrBlank() || password.text.isNullOrBlank() || (two_factor_check.isChecked && two_factor_edit.text.isNullOrBlank())) { + binding?.apply { + if (username.text.isNullOrBlank() || password.text.isNullOrBlank() || (twoFactorCheck.isChecked && twoFactorEdit.text.isNullOrBlank())) { errorResult() - context.toast(R.string.fields_cannot_be_blank) + root.context.toast(R.string.fields_cannot_be_blank) return } @@ -84,25 +82,25 @@ class MangadexLoginDialog(bundle: Bundle? = null) : LoginDialogPreference(bundle val result = source?.login( username.text.toString(), password.text.toString(), - two_factor_edit.text.toString() + twoFactorEdit.text.toString() ) ?: false if (result) { dialog?.dismiss() preferences.setTrackCredentials(Injekt.get().mdList, username.toString(), password.toString()) - context.toast(R.string.login_success) + root.context.toast(R.string.login_success) } else { errorResult() } } catch (error: Exception) { errorResult() - error.message?.let { context.toast(it) } + error.message?.let { root.context.toast(it) } } } } } private fun errorResult() { - v?.apply { + binding?.apply { dialog?.setCancelable(true) dialog?.setCanceledOnTouchOutside(true) login.progress = -1 diff --git a/app/src/main/res/layout/migration_process_item.xml b/app/src/main/res/layout/migration_process_item.xml index 66fe0842e..53d5bf950 100644 --- a/app/src/main/res/layout/migration_process_item.xml +++ b/app/src/main/res/layout/migration_process_item.xml @@ -24,13 +24,13 @@ android:contentDescription="@string/migrating_to" android:scaleType="center" android:layout_marginBottom="45dp" - android:tint="?attr/colorOnPrimary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/migration_manga_card_to" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/migration_manga_card_from" app:layout_constraintTop_toTopOf="parent" - app:srcCompat="@drawable/ic_arrow_forward_24dp" /> + app:srcCompat="@drawable/ic_arrow_forward_24dp" + app:tint="?attr/colorOnPrimary" /> + app:srcCompat="@drawable/ic_close_24dp" + app:tint="?attr/colorOnPrimary" /> \ No newline at end of file