Cleanup unused files and strings
This commit is contained in:
parent
5d1d5778ad
commit
c67b7092fb
@ -11,7 +11,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||||||
import dev.chrisbanes.insetter.applyInsetter
|
import dev.chrisbanes.insetter.applyInsetter
|
||||||
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.databinding.LatestControllerBinding
|
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerBinding
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
@ -28,7 +28,7 @@ import exh.savedsearches.models.SavedSearch
|
|||||||
* [FeedCardAdapter.OnMangaClickListener] called when manga is clicked in global search
|
* [FeedCardAdapter.OnMangaClickListener] called when manga is clicked in global search
|
||||||
*/
|
*/
|
||||||
open class FeedController :
|
open class FeedController :
|
||||||
NucleusController<LatestControllerBinding, FeedPresenter>(),
|
NucleusController<GlobalSearchControllerBinding, FeedPresenter>(),
|
||||||
FeedCardAdapter.OnMangaClickListener,
|
FeedCardAdapter.OnMangaClickListener,
|
||||||
FeedAdapter.OnFeedClickListener {
|
FeedAdapter.OnFeedClickListener {
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ open class FeedController :
|
|||||||
onMangaClick(manga)
|
onMangaClick(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createBinding(inflater: LayoutInflater): LatestControllerBinding = LatestControllerBinding.inflate(inflater)
|
override fun createBinding(inflater: LayoutInflater): GlobalSearchControllerBinding = GlobalSearchControllerBinding.inflate(inflater)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the view is created
|
* Called when the view is created
|
||||||
|
@ -6,7 +6,7 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import eu.davidea.viewholders.FlexibleViewHolder
|
import eu.davidea.viewholders.FlexibleViewHolder
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.databinding.LatestControllerCardBinding
|
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerCardBinding
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
|
|||||||
class FeedHolder(view: View, val adapter: FeedAdapter) :
|
class FeedHolder(view: View, val adapter: FeedAdapter) :
|
||||||
FlexibleViewHolder(view, adapter) {
|
FlexibleViewHolder(view, adapter) {
|
||||||
|
|
||||||
private val binding = LatestControllerCardBinding.bind(view)
|
private val binding = GlobalSearchControllerCardBinding.bind(view)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter containing manga from search results.
|
* Adapter containing manga from search results.
|
||||||
|
@ -345,7 +345,7 @@ open class BrowseSourcePresenter(
|
|||||||
.forEach { service ->
|
.forEach { service ->
|
||||||
launchIO {
|
launchIO {
|
||||||
try {
|
try {
|
||||||
service.match(source, manga)?.let { track ->
|
service.match(manga)?.let { track ->
|
||||||
track.manga_id = manga.id!!
|
track.manga_id = manga.id!!
|
||||||
(service as TrackService).bind(track)
|
(service as TrackService).bind(track)
|
||||||
db.insertTrack(track).executeAsBlocking()
|
db.insertTrack(track).executeAsBlocking()
|
||||||
|
@ -13,7 +13,7 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
|
|||||||
import dev.chrisbanes.insetter.applyInsetter
|
import dev.chrisbanes.insetter.applyInsetter
|
||||||
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.databinding.LatestControllerBinding
|
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerBinding
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
@ -40,7 +40,7 @@ import xyz.nulldev.ts.api.http.serializer.FilterSerializer
|
|||||||
* [SourceFeedCardAdapter.OnMangaClickListener] called when manga is clicked in global search
|
* [SourceFeedCardAdapter.OnMangaClickListener] called when manga is clicked in global search
|
||||||
*/
|
*/
|
||||||
open class SourceFeedController :
|
open class SourceFeedController :
|
||||||
SearchableNucleusController<LatestControllerBinding, SourceFeedPresenter>,
|
SearchableNucleusController<GlobalSearchControllerBinding, SourceFeedPresenter>,
|
||||||
FabController,
|
FabController,
|
||||||
SourceFeedCardAdapter.OnMangaClickListener,
|
SourceFeedCardAdapter.OnMangaClickListener,
|
||||||
SourceFeedAdapter.OnFeedClickListener {
|
SourceFeedAdapter.OnFeedClickListener {
|
||||||
@ -131,7 +131,7 @@ open class SourceFeedController :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createBinding(inflater: LayoutInflater): LatestControllerBinding = LatestControllerBinding.inflate(inflater)
|
override fun createBinding(inflater: LayoutInflater): GlobalSearchControllerBinding = GlobalSearchControllerBinding.inflate(inflater)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the view is created
|
* Called when the view is created
|
||||||
|
@ -7,7 +7,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import eu.davidea.viewholders.FlexibleViewHolder
|
import eu.davidea.viewholders.FlexibleViewHolder
|
||||||
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.databinding.LatestControllerCardBinding
|
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerCardBinding
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holder that binds the [SourceFeedItem] containing catalogue cards.
|
* Holder that binds the [SourceFeedItem] containing catalogue cards.
|
||||||
@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.databinding.LatestControllerCardBinding
|
|||||||
class SourceFeedHolder(view: View, val adapter: SourceFeedAdapter) :
|
class SourceFeedHolder(view: View, val adapter: SourceFeedAdapter) :
|
||||||
FlexibleViewHolder(view, adapter) {
|
FlexibleViewHolder(view, adapter) {
|
||||||
|
|
||||||
private val binding = LatestControllerCardBinding.bind(view)
|
private val binding = GlobalSearchControllerCardBinding.bind(view)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter containing manga from search results.
|
* Adapter containing manga from search results.
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.browse.source.index
|
|
||||||
|
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter that holds the manga items from search results.
|
|
||||||
*
|
|
||||||
* @param controller instance of [IndexController].
|
|
||||||
*/
|
|
||||||
class IndexCardAdapter(controller: IndexController) :
|
|
||||||
FlexibleAdapter<IndexCardItem>(null, controller, true) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen for browse item clicks.
|
|
||||||
*/
|
|
||||||
val mangaClickListener: OnMangaClickListener = controller
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener which should be called when user clicks browse.
|
|
||||||
* Note: Should only be handled by [IndexController]
|
|
||||||
*/
|
|
||||||
interface OnMangaClickListener {
|
|
||||||
fun onMangaClick(manga: Manga)
|
|
||||||
fun onMangaLongClick(manga: Manga)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.browse.source.index
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import coil.dispose
|
|
||||||
import eu.davidea.viewholders.FlexibleViewHolder
|
|
||||||
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerCardItemBinding
|
|
||||||
import eu.kanade.tachiyomi.util.view.loadAutoPause
|
|
||||||
|
|
||||||
class IndexCardHolder(view: View, adapter: IndexCardAdapter) :
|
|
||||||
FlexibleViewHolder(view, adapter) {
|
|
||||||
|
|
||||||
private val binding = GlobalSearchControllerCardItemBinding.bind(view)
|
|
||||||
|
|
||||||
init {
|
|
||||||
// Call onMangaClickListener when item is pressed.
|
|
||||||
itemView.setOnClickListener {
|
|
||||||
val item = adapter.getItem(bindingAdapterPosition)
|
|
||||||
if (item != null) {
|
|
||||||
adapter.mangaClickListener.onMangaClick(item.manga)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
itemView.setOnLongClickListener {
|
|
||||||
val item = adapter.getItem(bindingAdapterPosition)
|
|
||||||
if (item != null) {
|
|
||||||
adapter.mangaClickListener.onMangaLongClick(item.manga)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bind(manga: Manga) {
|
|
||||||
binding.card.clipToOutline = true
|
|
||||||
|
|
||||||
// Set manga title
|
|
||||||
binding.title.text = manga.title
|
|
||||||
|
|
||||||
// Set alpha of thumbnail.
|
|
||||||
binding.cover.alpha = if (manga.favorite) 0.3f else 1.0f
|
|
||||||
|
|
||||||
// For rounded corners
|
|
||||||
binding.badges.clipToOutline = true
|
|
||||||
|
|
||||||
// Set favorite badge
|
|
||||||
binding.favoriteText.isVisible = manga.favorite
|
|
||||||
|
|
||||||
setImage(manga)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setImage(manga: Manga) {
|
|
||||||
binding.cover.dispose()
|
|
||||||
binding.cover.loadAutoPause(manga) {
|
|
||||||
setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.browse.source.index
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
|
|
||||||
class IndexCardItem(val manga: Manga) : AbstractFlexibleItem<IndexCardHolder>() {
|
|
||||||
|
|
||||||
override fun getLayoutRes(): Int {
|
|
||||||
return R.layout.global_search_controller_card_item
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): IndexCardHolder {
|
|
||||||
return IndexCardHolder(view, adapter as IndexCardAdapter)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bindViewHolder(
|
|
||||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
|
|
||||||
holder: IndexCardHolder,
|
|
||||||
position: Int,
|
|
||||||
payloads: List<Any?>?
|
|
||||||
) {
|
|
||||||
holder.bind(manga)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (other is IndexCardItem) {
|
|
||||||
return manga.id == other.manga.id
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
return manga.id?.toInt() ?: 0
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,346 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.browse.source.index
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuInflater
|
|
||||||
import android.view.View
|
|
||||||
import androidx.core.os.bundleOf
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.databinding.IndexControllerBinding
|
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.FabController
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.SearchableNucleusController
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
|
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterSheet
|
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController
|
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import exh.util.nullIfBlank
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import reactivecircus.flowbinding.android.view.clicks
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import xyz.nulldev.ts.api.http.serializer.FilterSerializer
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This controller shows and manages the different search result in global search.
|
|
||||||
* This controller should only handle UI actions, IO actions should be done by [IndexPresenter]
|
|
||||||
* [IndexCardAdapter.OnMangaClickListener] called when manga is clicked in global search
|
|
||||||
*/
|
|
||||||
open class IndexController :
|
|
||||||
SearchableNucleusController<IndexControllerBinding, IndexPresenter>,
|
|
||||||
FabController,
|
|
||||||
IndexCardAdapter.OnMangaClickListener {
|
|
||||||
|
|
||||||
constructor(source: CatalogueSource?) : super(
|
|
||||||
bundleOf(
|
|
||||||
SOURCE_EXTRA to (source?.id ?: 0)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.source = source
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(sourceId: Long) : this(
|
|
||||||
Injekt.get<SourceManager>().get(sourceId) as? CatalogueSource
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
constructor(bundle: Bundle) : this(bundle.getLong(SOURCE_EXTRA))
|
|
||||||
|
|
||||||
var source: CatalogueSource? = null
|
|
||||||
|
|
||||||
private var latestAdapter: IndexCardAdapter? = null
|
|
||||||
private var browseAdapter: IndexCardAdapter? = null
|
|
||||||
|
|
||||||
private var actionFab: ExtendedFloatingActionButton? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sheet containing filter items.
|
|
||||||
*/
|
|
||||||
private var filterSheet: SourceFilterSheet? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
setHasOptionsMenu(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getTitle(): String? {
|
|
||||||
return source!!.name
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the [IndexPresenter] used in controller.
|
|
||||||
*
|
|
||||||
* @return instance of [IndexPresenter]
|
|
||||||
*/
|
|
||||||
override fun createPresenter(): IndexPresenter {
|
|
||||||
return IndexPresenter(source!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when manga in global search is clicked, opens manga.
|
|
||||||
*
|
|
||||||
* @param manga clicked item containing manga information.
|
|
||||||
*/
|
|
||||||
override fun onMangaClick(manga: Manga) {
|
|
||||||
// Open MangaController.
|
|
||||||
router.pushController(MangaController(manga, true).withFadeTransaction())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when manga in global search is long clicked.
|
|
||||||
*
|
|
||||||
* @param manga clicked item containing manga information.
|
|
||||||
*/
|
|
||||||
override fun onMangaLongClick(manga: Manga) {
|
|
||||||
// Delegate to single click by default.
|
|
||||||
onMangaClick(manga)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds items to the options menu.
|
|
||||||
*
|
|
||||||
* @param menu menu containing options.
|
|
||||||
* @param inflater used to load the menu xml.
|
|
||||||
*/
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
|
||||||
createOptionsMenu(menu, inflater, R.menu.global_search, R.id.action_search)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSearchViewQueryTextSubmit(query: String?) {
|
|
||||||
onBrowseClick(query.nullIfBlank())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSearchViewQueryTextChange(newText: String?) {
|
|
||||||
if (router.backstack.lastOrNull()?.controller == this) {
|
|
||||||
presenter.query = newText ?: ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createBinding(inflater: LayoutInflater) = IndexControllerBinding.inflate(inflater)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the view is created
|
|
||||||
*
|
|
||||||
* @param view view of controller
|
|
||||||
*/
|
|
||||||
override fun onViewCreated(view: View) {
|
|
||||||
super.onViewCreated(view)
|
|
||||||
|
|
||||||
// Prepare filter sheet
|
|
||||||
initFilterSheet()
|
|
||||||
|
|
||||||
latestAdapter = IndexCardAdapter(this)
|
|
||||||
|
|
||||||
binding.latestRecycler.layoutManager = LinearLayoutManager(view.context, LinearLayoutManager.HORIZONTAL, false)
|
|
||||||
binding.latestRecycler.adapter = latestAdapter
|
|
||||||
|
|
||||||
browseAdapter = IndexCardAdapter(this)
|
|
||||||
|
|
||||||
binding.browseRecycler.layoutManager = LinearLayoutManager(view.context, LinearLayoutManager.HORIZONTAL, false)
|
|
||||||
binding.browseRecycler.adapter = browseAdapter
|
|
||||||
|
|
||||||
binding.latestBarWrapper.clicks()
|
|
||||||
.onEach {
|
|
||||||
onLatestClick()
|
|
||||||
}
|
|
||||||
.launchIn(viewScope)
|
|
||||||
|
|
||||||
binding.browseBarWrapper.clicks()
|
|
||||||
.onEach {
|
|
||||||
onBrowseClick()
|
|
||||||
}
|
|
||||||
.launchIn(viewScope)
|
|
||||||
|
|
||||||
presenter.latestItems
|
|
||||||
.onEach {
|
|
||||||
bind(it, true)
|
|
||||||
}
|
|
||||||
.launchIn(viewScope)
|
|
||||||
|
|
||||||
presenter.browseItems
|
|
||||||
.onEach {
|
|
||||||
bind(it, false)
|
|
||||||
}
|
|
||||||
.launchIn(viewScope)
|
|
||||||
|
|
||||||
presenter.getLatest()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val filterSerializer = FilterSerializer()
|
|
||||||
|
|
||||||
open fun initFilterSheet() {
|
|
||||||
if (presenter.sourceFilters.isEmpty()) {
|
|
||||||
actionFab?.text = activity!!.getString(R.string.saved_searches)
|
|
||||||
}
|
|
||||||
|
|
||||||
filterSheet = SourceFilterSheet(
|
|
||||||
activity!!,
|
|
||||||
// SY -->
|
|
||||||
this,
|
|
||||||
presenter.source,
|
|
||||||
presenter.loadSearches(),
|
|
||||||
// SY <--
|
|
||||||
onFilterClicked = {
|
|
||||||
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
|
|
||||||
filterSheet?.dismiss()
|
|
||||||
if (allDefault) {
|
|
||||||
onBrowseClick(
|
|
||||||
presenter.query.nullIfBlank()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
onBrowseClick(
|
|
||||||
presenter.query.nullIfBlank(),
|
|
||||||
filters = Json.encodeToString(filterSerializer.serialize(presenter.sourceFilters))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onResetClicked = {},
|
|
||||||
onSaveClicked = {},
|
|
||||||
onSavedSearchClicked = cb@{ idOfSearch ->
|
|
||||||
val search = presenter.loadSearch(idOfSearch)
|
|
||||||
|
|
||||||
if (search == null) {
|
|
||||||
filterSheet?.context?.let {
|
|
||||||
MaterialAlertDialogBuilder(it)
|
|
||||||
.setTitle(R.string.save_search_failed_to_load)
|
|
||||||
.setMessage(R.string.save_search_failed_to_load_message)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
return@cb
|
|
||||||
}
|
|
||||||
|
|
||||||
if (search.filterList == null) {
|
|
||||||
activity?.toast(R.string.save_search_invalid)
|
|
||||||
return@cb
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.sourceFilters = FilterList(search.filterList)
|
|
||||||
filterSheet?.setFilters(presenter.filterItems)
|
|
||||||
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
|
|
||||||
filterSheet?.dismiss()
|
|
||||||
|
|
||||||
if (!allDefault) {
|
|
||||||
onBrowseClick(
|
|
||||||
search = presenter.query.nullIfBlank(),
|
|
||||||
savedSearch = search.id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSavedSearchDeleteClicked = { _, _ -> }
|
|
||||||
)
|
|
||||||
filterSheet?.setFilters(presenter.filterItems)
|
|
||||||
|
|
||||||
// TODO: [ExtendedFloatingActionButton] hide/show methods don't work properly
|
|
||||||
filterSheet?.setOnShowListener { actionFab?.isVisible = false }
|
|
||||||
filterSheet?.setOnDismissListener { actionFab?.isVisible = true }
|
|
||||||
|
|
||||||
actionFab?.setOnClickListener { filterSheet?.show() }
|
|
||||||
|
|
||||||
actionFab?.isVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun configureFab(fab: ExtendedFloatingActionButton) {
|
|
||||||
actionFab = fab
|
|
||||||
|
|
||||||
// Controlled by initFilterSheet()
|
|
||||||
fab.isVisible = false
|
|
||||||
|
|
||||||
fab.setText(R.string.action_filter)
|
|
||||||
fab.setIconResource(R.drawable.ic_filter_list_24dp)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun cleanupFab(fab: ExtendedFloatingActionButton) {
|
|
||||||
fab.setOnClickListener(null)
|
|
||||||
actionFab = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bind(results: List<IndexCardItem>?, isLatest: Boolean) {
|
|
||||||
val progress = if (isLatest) binding.latestProgress else binding.browseProgress
|
|
||||||
when {
|
|
||||||
results == null -> {
|
|
||||||
progress.isVisible = true
|
|
||||||
showResultsHolder(isLatest)
|
|
||||||
}
|
|
||||||
results.isEmpty() -> {
|
|
||||||
progress.isVisible = false
|
|
||||||
showNoResults(isLatest)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
progress.isVisible = false
|
|
||||||
showResultsHolder(isLatest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val adapter = if (isLatest) {
|
|
||||||
latestAdapter
|
|
||||||
} else {
|
|
||||||
browseAdapter
|
|
||||||
}
|
|
||||||
adapter?.updateDataSet(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onError(e: Exception, isLatest: Boolean) {
|
|
||||||
e.message?.let {
|
|
||||||
val textView = if (isLatest) {
|
|
||||||
binding.latestNoResultsFound
|
|
||||||
} else {
|
|
||||||
binding.browseNoResultsFound
|
|
||||||
}
|
|
||||||
textView.text = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showResultsHolder(isLatest: Boolean) {
|
|
||||||
(if (isLatest) binding.latestNoResultsFound else binding.browseNoResultsFound).isVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showNoResults(isLatest: Boolean) {
|
|
||||||
(if (isLatest) binding.latestNoResultsFound else binding.browseNoResultsFound).isVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView(view: View) {
|
|
||||||
latestAdapter = null
|
|
||||||
browseAdapter = null
|
|
||||||
super.onDestroyView(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onBrowseClick(search: String? = null, savedSearch: Long? = null, filters: String? = null) {
|
|
||||||
router.replaceTopController(BrowseSourceController(presenter.source, search, savedSearch = savedSearch, filterList = filters).withFadeTransaction())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLatestClick() {
|
|
||||||
router.replaceTopController(LatestUpdatesController(presenter.source).withFadeTransaction())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called from the presenter when a manga is initialized.
|
|
||||||
*
|
|
||||||
* @param manga the initialized manga.
|
|
||||||
*/
|
|
||||||
fun onMangaInitialized(manga: Manga, isLatest: Boolean) {
|
|
||||||
val adapter = if (isLatest) latestAdapter else browseAdapter
|
|
||||||
adapter ?: return
|
|
||||||
|
|
||||||
adapter.allBoundViewHolders.forEach {
|
|
||||||
if (it !is IndexCardHolder) return@forEach
|
|
||||||
if (adapter.getItem(it.bindingAdapterPosition)?.manga?.id != manga.id) return@forEach
|
|
||||||
it.setImage(manga)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val SOURCE_EXTRA = "source"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,274 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.browse.source.index
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
|
||||||
import eu.kanade.tachiyomi.source.Source
|
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import eu.kanade.tachiyomi.source.model.toSManga
|
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter.Companion.toItems
|
|
||||||
import eu.kanade.tachiyomi.util.lang.awaitSingle
|
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
|
||||||
import exh.log.xLogE
|
|
||||||
import exh.savedsearches.EXHSavedSearch
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.asFlow
|
|
||||||
import kotlinx.coroutines.flow.buffer
|
|
||||||
import kotlinx.coroutines.flow.catch
|
|
||||||
import kotlinx.coroutines.flow.filter
|
|
||||||
import kotlinx.coroutines.flow.flatMapConcat
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.json.JsonArray
|
|
||||||
import logcat.LogPriority
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import xyz.nulldev.ts.api.http.serializer.FilterSerializer
|
|
||||||
import java.lang.RuntimeException
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Presenter of [IndexController]
|
|
||||||
* Function calls should be done from here. UI calls should be done from the controller.
|
|
||||||
*
|
|
||||||
* @param source the source.
|
|
||||||
* @param db manages the database calls.
|
|
||||||
* @param preferences manages the preference calls.
|
|
||||||
*/
|
|
||||||
open class IndexPresenter(
|
|
||||||
val source: CatalogueSource,
|
|
||||||
val db: DatabaseHelper = Injekt.get(),
|
|
||||||
val preferences: PreferencesHelper = Injekt.get()
|
|
||||||
) : BasePresenter<IndexController>() {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subject which fetches image of given manga.
|
|
||||||
*/
|
|
||||||
private val fetchImageFlow = MutableSharedFlow<Pair<List<Manga>, Boolean>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modifiable list of filters.
|
|
||||||
*/
|
|
||||||
var sourceFilters = FilterList()
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
filterItems = value.toItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
var filterItems: List<IFlexible<*>> = emptyList()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscription for fetching images of manga.
|
|
||||||
*/
|
|
||||||
private var fetchImageJob: Job? = null
|
|
||||||
|
|
||||||
val latestItems = MutableStateFlow<List<IndexCardItem>?>(null)
|
|
||||||
|
|
||||||
val browseItems = MutableStateFlow<List<IndexCardItem>?>(null)
|
|
||||||
|
|
||||||
init {
|
|
||||||
query = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
|
||||||
super.onCreate(savedState)
|
|
||||||
|
|
||||||
sourceFilters = source.getFilterList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initiates get latest per watching source.
|
|
||||||
*/
|
|
||||||
fun getLatest() {
|
|
||||||
// Create image fetch subscription
|
|
||||||
initializeFetchImageSubscription()
|
|
||||||
|
|
||||||
presenterScope.launch(Dispatchers.IO) {
|
|
||||||
if (latestItems.value != null) return@launch
|
|
||||||
val results = if (source.supportsLatest) {
|
|
||||||
try {
|
|
||||||
source.fetchLatestUpdates(1)
|
|
||||||
.awaitSingle()
|
|
||||||
.mangas
|
|
||||||
.map { networkToLocalManga(it, source.id) }
|
|
||||||
} catch (e: Exception) {
|
|
||||||
withUIContext {
|
|
||||||
view?.onError(e, true)
|
|
||||||
}
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
} else emptyList()
|
|
||||||
|
|
||||||
fetchImage(results, true)
|
|
||||||
|
|
||||||
latestItems.value = results.map { IndexCardItem(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
presenterScope.launch(Dispatchers.IO) {
|
|
||||||
if (browseItems.value != null) return@launch
|
|
||||||
val results = try {
|
|
||||||
source.fetchPopularManga(1)
|
|
||||||
.awaitSingle()
|
|
||||||
.mangas
|
|
||||||
.map { networkToLocalManga(it, source.id) }
|
|
||||||
} catch (e: Exception) {
|
|
||||||
withUIContext {
|
|
||||||
view?.onError(e, true)
|
|
||||||
}
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchImage(results, false)
|
|
||||||
|
|
||||||
browseItems.value = results.map { IndexCardItem(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a list of manga.
|
|
||||||
*
|
|
||||||
* @param manga the list of manga to initialize.
|
|
||||||
*/
|
|
||||||
private fun fetchImage(manga: List<Manga>, isLatest: Boolean) {
|
|
||||||
presenterScope.launchIO {
|
|
||||||
fetchImageFlow.emit(manga to isLatest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribes to the initializer of manga details and updates the view if needed.
|
|
||||||
*/
|
|
||||||
private fun initializeFetchImageSubscription() {
|
|
||||||
fetchImageJob?.cancel()
|
|
||||||
fetchImageFlow
|
|
||||||
.flatMapConcat { (manga, isLatest) ->
|
|
||||||
manga.asFlow()
|
|
||||||
.filter { it.thumbnail_url == null && !it.initialized }
|
|
||||||
.map {
|
|
||||||
getMangaDetailsFlow(it, source, isLatest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.buffer(Channel.RENDEZVOUS)
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.onEach { (manga, isLatest) ->
|
|
||||||
withUIContext {
|
|
||||||
view?.onMangaInitialized(manga, isLatest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.catch {
|
|
||||||
logcat(LogPriority.ERROR, it)
|
|
||||||
}
|
|
||||||
.launchIn(presenterScope)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an observable of manga that initializes the given manga.
|
|
||||||
*
|
|
||||||
* @param manga the manga to initialize.
|
|
||||||
* @return an observable of the manga to initialize
|
|
||||||
*/
|
|
||||||
private suspend fun getMangaDetailsFlow(manga: Manga, source: Source, isLatest: Boolean): Pair<Manga, Boolean> {
|
|
||||||
val networkManga = source.getMangaDetails(manga.toMangaInfo())
|
|
||||||
manga.copyFrom(networkManga.toSManga())
|
|
||||||
manga.initialized = true
|
|
||||||
db.insertManga(manga).executeAsBlocking()
|
|
||||||
return manga to isLatest
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a manga from the database for the given manga from network. It creates a new entry
|
|
||||||
* if the manga is not yet in the database.
|
|
||||||
*
|
|
||||||
* @param sManga the manga from the source.
|
|
||||||
* @return a manga from the database.
|
|
||||||
*/
|
|
||||||
protected open fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
|
|
||||||
var localManga = db.getManga(sManga.url, sourceId).executeAsBlocking()
|
|
||||||
if (localManga == null) {
|
|
||||||
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
|
||||||
newManga.copyFrom(sManga)
|
|
||||||
val result = db.insertManga(newManga).executeAsBlocking()
|
|
||||||
newManga.id = result.insertedId()
|
|
||||||
localManga = newManga
|
|
||||||
}
|
|
||||||
return localManga
|
|
||||||
}
|
|
||||||
|
|
||||||
private val filterSerializer = FilterSerializer()
|
|
||||||
|
|
||||||
fun loadSearch(searchId: Long): EXHSavedSearch? {
|
|
||||||
val search = db.getSavedSearch(searchId).executeAsBlocking() ?: return null
|
|
||||||
return EXHSavedSearch(
|
|
||||||
id = search.id!!,
|
|
||||||
name = search.name,
|
|
||||||
query = search.query.orEmpty(),
|
|
||||||
filterList = runCatching {
|
|
||||||
val originalFilters = source.getFilterList()
|
|
||||||
filterSerializer.deserialize(
|
|
||||||
filters = originalFilters,
|
|
||||||
json = search.filtersJson
|
|
||||||
?.let { Json.decodeFromString<JsonArray>(it) }
|
|
||||||
?: return@runCatching null
|
|
||||||
)
|
|
||||||
originalFilters
|
|
||||||
}.getOrNull()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadSearches(): List<EXHSavedSearch> {
|
|
||||||
return db.getSavedSearches(source.id).executeAsBlocking().map {
|
|
||||||
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
|
|
||||||
id = it.id!!,
|
|
||||||
name = it.name,
|
|
||||||
query = it.query.orEmpty(),
|
|
||||||
filterList = null
|
|
||||||
)
|
|
||||||
val filters = try {
|
|
||||||
Json.decodeFromString<JsonArray>(filtersJson)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
} ?: return@map EXHSavedSearch(
|
|
||||||
id = it.id!!,
|
|
||||||
name = it.name,
|
|
||||||
query = it.query.orEmpty(),
|
|
||||||
filterList = null
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val originalFilters = source.getFilterList()
|
|
||||||
filterSerializer.deserialize(originalFilters, filters)
|
|
||||||
EXHSavedSearch(
|
|
||||||
id = it.id!!,
|
|
||||||
name = it.name,
|
|
||||||
query = it.query.orEmpty(),
|
|
||||||
filterList = originalFilters
|
|
||||||
)
|
|
||||||
} catch (t: RuntimeException) {
|
|
||||||
// Load failed
|
|
||||||
xLogE("Failed to load saved search!", t)
|
|
||||||
EXHSavedSearch(
|
|
||||||
id = it.id!!,
|
|
||||||
name = it.name,
|
|
||||||
query = it.query.orEmpty(),
|
|
||||||
filterList = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/latest_bar_wrapper"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/selectableItemBackground">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/latest"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:text="@string/latest"
|
|
||||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/latest_bar_more_icon"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/latest_bar_more_icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:contentDescription="@string/label_more"
|
|
||||||
android:padding="16dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:srcCompat="@drawable/ic_arrow_forward_24dp"
|
|
||||||
app:tint="?android:attr/textColorPrimary" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/latest_no_results_found"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
android:text="@string/no_results_found"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
|
||||||
android:id="@+id/latest_progress"
|
|
||||||
style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:indeterminate="true" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/latest_recycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp"
|
|
||||||
tools:listitem="@layout/global_search_controller_card_item" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/browse_bar_wrapper"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/selectableItemBackground">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/browse"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:text="@string/browse"
|
|
||||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/browse_bar_more_icon"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/browse_bar_more_icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:contentDescription="@string/label_more"
|
|
||||||
android:padding="16dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:srcCompat="@drawable/ic_arrow_forward_24dp"
|
|
||||||
app:tint="?android:attr/textColorPrimary" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/browse_no_results_found"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
android:text="@string/no_results_found"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
|
||||||
android:id="@+id/browse_progress"
|
|
||||||
style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:indeterminate="true" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/browse_recycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp"
|
|
||||||
tools:listitem="@layout/global_search_controller_card_item" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -1,45 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout 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"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:paddingBottom="@dimen/action_toolbar_list_padding"
|
|
||||||
tools:listitem="@layout/latest_controller_card" />
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/progress"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:alpha="0.75"
|
|
||||||
android:background="?attr/colorSurface" />
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
|
||||||
style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:indeterminate="true" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<eu.kanade.tachiyomi.widget.EmptyView
|
|
||||||
android:id="@+id/empty_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
@ -1,85 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/title_wrapper"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/selectableItemBackground">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/subtitle"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/title_more_icon"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:text="Title" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/subtitle"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/title_more_icon"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
|
||||||
tools:text="English"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/title_more_icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:contentDescription="@string/label_more"
|
|
||||||
android:padding="16dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:srcCompat="@drawable/ic_arrow_forward_24dp"
|
|
||||||
app:tint="?android:attr/textColorPrimary" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/no_results_found"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
android:text="@string/no_results_found"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
|
||||||
android:id="@+id/progress"
|
|
||||||
style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:indeterminate="true" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp"
|
|
||||||
tools:listitem="@layout/global_search_controller_card_item" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -179,8 +179,6 @@
|
|||||||
<string name="library_group_updates_all">Lancer des mises à jour de catégorie tout le temps</string>
|
<string name="library_group_updates_all">Lancer des mises à jour de catégorie tout le temps</string>
|
||||||
|
|
||||||
<!-- Browse settings -->
|
<!-- Browse settings -->
|
||||||
<string name="pref_latest_position">Dernière position de l\'onglet</string>
|
|
||||||
<string name="pref_latest_position_summery">Voulez-vous que le dernier onglet soit le premier onglet de la navigation? Cela en fera l\'onglet par défaut lors de l\'ouverture de la navigation, non recommandé si vous êtes sur des données ou un réseau mesuré</string>
|
|
||||||
<string name="pref_source_navigation">Remplacer le dernier bouton</string>
|
<string name="pref_source_navigation">Remplacer le dernier bouton</string>
|
||||||
<string name="pref_source_navigation_summery">Remplacer le dernier bouton par une vue de navigation personnalisée qui inclut à la fois la dernière et la navigation</string>
|
<string name="pref_source_navigation_summery">Remplacer le dernier bouton par une vue de navigation personnalisée qui inclut à la fois la dernière et la navigation</string>
|
||||||
<string name="pref_local_source_hidden_folders">Dossiers cachés de la source locale</string>
|
<string name="pref_local_source_hidden_folders">Dossiers cachés de la source locale</string>
|
||||||
@ -267,12 +265,6 @@
|
|||||||
<string name="no_source_categories">Aucune catégorie de source disponible</string>
|
<string name="no_source_categories">Aucune catégorie de source disponible</string>
|
||||||
<string name="invalid_category_name">Nom de catégorie non valide</string>
|
<string name="invalid_category_name">Nom de catégorie non valide</string>
|
||||||
|
|
||||||
<!-- Latest Tab -->
|
|
||||||
<string name="watch">Regarder</string>
|
|
||||||
<string name="unwatch">Non regarder</string>
|
|
||||||
<string name="too_many_watched">Trop de sources regardées, impossible d\'en ajouter plus de 5</string>
|
|
||||||
<string name="latest_tab_empty">Vous n\'avez pas de sources regardées, allez dans l\'onglet sources et appuyez longuement sur une source pour la regarder</string>
|
|
||||||
|
|
||||||
<!-- Sort by tags -->
|
<!-- Sort by tags -->
|
||||||
<string name="pref_tag_sorting">Tag de tri des Tags</string>
|
<string name="pref_tag_sorting">Tag de tri des Tags</string>
|
||||||
<string name="tag_sorting">Tri des tags</string>
|
<string name="tag_sorting">Tri des tags</string>
|
||||||
|
@ -192,8 +192,6 @@
|
|||||||
<string name="library_group_updates_all">Hanya jalankan pembaruan kategori</string>
|
<string name="library_group_updates_all">Hanya jalankan pembaruan kategori</string>
|
||||||
|
|
||||||
<!-- Browse settings -->
|
<!-- Browse settings -->
|
||||||
<string name="pref_latest_position">Posisi tab Terbaru</string>
|
|
||||||
<string name="pref_latest_position_summery">Apakah Anda ingin tab Terbaru menjadi tab pertama dalam penelusuran? Ini akan menjadikannya tab bawaan saat membuka jelajah, tidak disarankan jika Anda menggunakan data atau sedang menghemat data</string>
|
|
||||||
<string name="pref_source_source_filtering">Filter Sumber dalam kategori</string>
|
<string name="pref_source_source_filtering">Filter Sumber dalam kategori</string>
|
||||||
<string name="pref_source_source_filtering_summery">Saring sumber-sumber yang ada dalam kategori, agar sumber-sumber tersebut tidak dimasukkan ke dalam kategori bahasa jika berada dalam suatu kategori</string>
|
<string name="pref_source_source_filtering_summery">Saring sumber-sumber yang ada dalam kategori, agar sumber-sumber tersebut tidak dimasukkan ke dalam kategori bahasa jika berada dalam suatu kategori</string>
|
||||||
<string name="pref_source_navigation">Ganti tombol Terbaru</string>
|
<string name="pref_source_navigation">Ganti tombol Terbaru</string>
|
||||||
@ -359,12 +357,6 @@
|
|||||||
<string name="no_source_categories">Tak ada kategori sumber yang tersedia</string>
|
<string name="no_source_categories">Tak ada kategori sumber yang tersedia</string>
|
||||||
<string name="invalid_category_name">Nama kategori tidak valid</string>
|
<string name="invalid_category_name">Nama kategori tidak valid</string>
|
||||||
|
|
||||||
<!-- Latest Tab -->
|
|
||||||
<string name="watch">Tonton</string>
|
|
||||||
<string name="unwatch">Berhenti tonton</string>
|
|
||||||
<string name="too_many_watched">Terlalu banyak sumber yang ditonton, batasnya adalah 5</string>
|
|
||||||
<string name="latest_tab_empty">Kamu tidak memiliki sumber yang ditonton, buka tab sumber dan tekan lama sumber untuk menontonnya</string>
|
|
||||||
|
|
||||||
<!-- Sort by tags -->
|
<!-- Sort by tags -->
|
||||||
<string name="pref_tag_sorting">Penyortir Tagar</string>
|
<string name="pref_tag_sorting">Penyortir Tagar</string>
|
||||||
<string name="tag_sorting">Menyortir tagar</string>
|
<string name="tag_sorting">Menyortir tagar</string>
|
||||||
|
@ -188,8 +188,6 @@
|
|||||||
<string name="library_group_updates_all">Iniciar atualizações de categoria o tempo todo</string>
|
<string name="library_group_updates_all">Iniciar atualizações de categoria o tempo todo</string>
|
||||||
|
|
||||||
<!-- Browse settings -->
|
<!-- Browse settings -->
|
||||||
<string name="pref_latest_position">Posição da aba Recentes</string>
|
|
||||||
<string name="pref_latest_position_summery">Deseja que a aba Recentes seja a primeira em Navegar? Isto a fará aba padrão ao abrir em Navegar, não recomendado se usa dados móveis ou uma rede medida.</string>
|
|
||||||
<string name="pref_source_source_filtering">Filtrar fontes nas categorias</string>
|
<string name="pref_source_source_filtering">Filtrar fontes nas categorias</string>
|
||||||
<string name="pref_source_source_filtering_summery">Filtre as fontes que estão em categorias, evitando de pô-las sob seu idioma se estiverem numa categoria</string>
|
<string name="pref_source_source_filtering_summery">Filtre as fontes que estão em categorias, evitando de pô-las sob seu idioma se estiverem numa categoria</string>
|
||||||
<string name="pref_source_navigation">Substituir o botão Recentes</string>
|
<string name="pref_source_navigation">Substituir o botão Recentes</string>
|
||||||
@ -330,12 +328,6 @@
|
|||||||
<string name="no_source_categories">Sem categorias de fonte disponíveis</string>
|
<string name="no_source_categories">Sem categorias de fonte disponíveis</string>
|
||||||
<string name="invalid_category_name">Nome de categoria inválido</string>
|
<string name="invalid_category_name">Nome de categoria inválido</string>
|
||||||
|
|
||||||
<!-- Latest Tab -->
|
|
||||||
<string name="watch">Seguir</string>
|
|
||||||
<string name="unwatch">Deixar de seguir</string>
|
|
||||||
<string name="too_many_watched">Muitas fontes seguidas, não pode seguir mais de 5</string>
|
|
||||||
<string name="latest_tab_empty">Você não tem fontes seguidas, vá à aba Fontes e pressione e segure uma fonte para seguí-la</string>
|
|
||||||
|
|
||||||
<!-- Sort by tags -->
|
<!-- Sort by tags -->
|
||||||
<string name="pref_tag_sorting">Tags de ordenação de tag</string>
|
<string name="pref_tag_sorting">Tags de ordenação de tag</string>
|
||||||
<string name="tag_sorting">Ordenação de tag</string>
|
<string name="tag_sorting">Ordenação de tag</string>
|
||||||
|
@ -191,8 +191,6 @@
|
|||||||
<string name="library_group_updates_all">Постоянно запускать обновления категорий</string>
|
<string name="library_group_updates_all">Постоянно запускать обновления категорий</string>
|
||||||
|
|
||||||
<!-- Browse settings -->
|
<!-- Browse settings -->
|
||||||
<string name="pref_latest_position">Позиция вкладки (Последняя)</string>
|
|
||||||
<string name="pref_latest_position_summery">Поместить вкладку «Последняя» на место первой вкладки в «Поисковик»? Это сделает её вкладкой по умолчанию при открытии «Поисковик», не рекомендуется, если вы используете ограниченные сети(Моб. Данные)</string>
|
|
||||||
<string name="pref_source_source_filtering">Фильтровать источники по категориям</string>
|
<string name="pref_source_source_filtering">Фильтровать источники по категориям</string>
|
||||||
<string name="pref_source_source_filtering_summery">Фильтровать источники которые находятся в категориях, чтобы источники которые находятся в категориях, не попадали под языковую фильтрацию</string>
|
<string name="pref_source_source_filtering_summery">Фильтровать источники которые находятся в категориях, чтобы источники которые находятся в категориях, не попадали под языковую фильтрацию</string>
|
||||||
<string name="pref_source_navigation">Убрать кнопку (Последняя)</string>
|
<string name="pref_source_navigation">Убрать кнопку (Последняя)</string>
|
||||||
@ -358,12 +356,6 @@
|
|||||||
<string name="no_source_categories">Отсутствуют категории для источников</string>
|
<string name="no_source_categories">Отсутствуют категории для источников</string>
|
||||||
<string name="invalid_category_name">Недопустимое имя категории!</string>
|
<string name="invalid_category_name">Недопустимое имя категории!</string>
|
||||||
|
|
||||||
<!-- Latest Tab -->
|
|
||||||
<string name="watch">Отслеживать</string>
|
|
||||||
<string name="unwatch">Не отслеживать</string>
|
|
||||||
<string name="too_many_watched">Слишком много отслеживаемых источников, нельзя добавить больше пяти</string>
|
|
||||||
<string name="latest_tab_empty">Нет отслеживаемых источников для отображения, перейдите во вкладку «Источники», после, нажмите на источник и удерживайте, затем выберите «Отслеживать».</string>
|
|
||||||
|
|
||||||
<!-- Sort by tags -->
|
<!-- Sort by tags -->
|
||||||
<string name="pref_tag_sorting">Сортировка тэгов</string>
|
<string name="pref_tag_sorting">Сортировка тэгов</string>
|
||||||
<string name="tag_sorting">Сортировка тэгов</string>
|
<string name="tag_sorting">Сортировка тэгов</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user