Cleanup unused files and strings

This commit is contained in:
Jobobby04 2022-03-27 20:24:13 -04:00
parent 5d1d5778ad
commit c67b7092fb
17 changed files with 11 additions and 1057 deletions

View File

@ -11,7 +11,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.tachiyomi.R
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.ui.base.controller.NucleusController
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
*/
open class FeedController :
NucleusController<LatestControllerBinding, FeedPresenter>(),
NucleusController<GlobalSearchControllerBinding, FeedPresenter>(),
FeedCardAdapter.OnMangaClickListener,
FeedAdapter.OnFeedClickListener {
@ -123,7 +123,7 @@ open class FeedController :
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

View File

@ -6,7 +6,7 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import eu.davidea.viewholders.FlexibleViewHolder
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
/**
@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
class FeedHolder(view: View, val adapter: FeedAdapter) :
FlexibleViewHolder(view, adapter) {
private val binding = LatestControllerCardBinding.bind(view)
private val binding = GlobalSearchControllerCardBinding.bind(view)
/**
* Adapter containing manga from search results.

View File

@ -345,7 +345,7 @@ open class BrowseSourcePresenter(
.forEach { service ->
launchIO {
try {
service.match(source, manga)?.let { track ->
service.match(manga)?.let { track ->
track.manga_id = manga.id!!
(service as TrackService).bind(track)
db.insertTrack(track).executeAsBlocking()

View File

@ -13,7 +13,7 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.tachiyomi.R
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.SourceManager
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
*/
open class SourceFeedController :
SearchableNucleusController<LatestControllerBinding, SourceFeedPresenter>,
SearchableNucleusController<GlobalSearchControllerBinding, SourceFeedPresenter>,
FabController,
SourceFeedCardAdapter.OnMangaClickListener,
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

View File

@ -7,7 +7,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
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.
@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.databinding.LatestControllerCardBinding
class SourceFeedHolder(view: View, val adapter: SourceFeedAdapter) :
FlexibleViewHolder(view, adapter) {
private val binding = LatestControllerCardBinding.bind(view)
private val binding = GlobalSearchControllerCardBinding.bind(view)
/**
* Adapter containing manga from search results.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -179,8 +179,6 @@
<string name="library_group_updates_all">Lancer des mises à jour de catégorie tout le temps</string>
<!-- 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_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>
@ -267,12 +265,6 @@
<string name="no_source_categories">Aucune catégorie de source disponible</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 -->
<string name="pref_tag_sorting">Tag de tri des Tags</string>
<string name="tag_sorting">Tri des tags</string>

View File

@ -192,8 +192,6 @@
<string name="library_group_updates_all">Hanya jalankan pembaruan kategori</string>
<!-- 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_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>
@ -359,12 +357,6 @@
<string name="no_source_categories">Tak ada kategori sumber yang tersedia</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 -->
<string name="pref_tag_sorting">Penyortir Tagar</string>
<string name="tag_sorting">Menyortir tagar</string>

View File

@ -188,8 +188,6 @@
<string name="library_group_updates_all">Iniciar atualizações de categoria o tempo todo</string>
<!-- 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_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>
@ -330,12 +328,6 @@
<string name="no_source_categories">Sem categorias de fonte disponíveis</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 -->
<string name="pref_tag_sorting">Tags de ordenação de tag</string>
<string name="tag_sorting">Ordenação de tag</string>

View File

@ -191,8 +191,6 @@
<string name="library_group_updates_all">Постоянно запускать обновления категорий</string>
<!-- 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_summery">Фильтровать источники которые находятся в категориях, чтобы источники которые находятся в категориях, не попадали под языковую фильтрацию</string>
<string name="pref_source_navigation">Убрать кнопку (Последняя)</string>
@ -358,12 +356,6 @@
<string name="no_source_categories">Отсутствуют категории для источников</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 -->
<string name="pref_tag_sorting">Сортировка тэгов</string>
<string name="tag_sorting">Сортировка тэгов</string>