mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 21:01:51 +01:00
Add source pinning (closes #2322)
This commit is contained in:
parent
849cb4208f
commit
1373a0d61c
@ -207,6 +207,8 @@ class PreferencesHelper(val context: Context) {
|
|||||||
|
|
||||||
fun hiddenCatalogues() = rxPrefs.getStringSet("hidden_catalogues", mutableSetOf())
|
fun hiddenCatalogues() = rxPrefs.getStringSet("hidden_catalogues", mutableSetOf())
|
||||||
|
|
||||||
|
fun pinnedCatalogues() = rxPrefs.getStringSet("pinned_catalogues", emptySet())
|
||||||
|
|
||||||
fun downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false)
|
fun downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false)
|
||||||
|
|
||||||
fun downloadNewCategories() = rxPrefs.getStringSet(Keys.downloadNewCategories, emptySet())
|
fun downloadNewCategories() = rxPrefs.getStringSet(Keys.downloadNewCategories, emptySet())
|
||||||
|
@ -10,7 +10,6 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.afollestad.materialdialogs.list.listItems
|
import com.afollestad.materialdialogs.list.listItems
|
||||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||||
@ -25,7 +24,6 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.online.LoginSource
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
|
import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
@ -40,7 +38,6 @@ import eu.kanade.tachiyomi.ui.setting.SettingsSourcesController
|
|||||||
import eu.kanade.tachiyomi.util.view.applyWindowInsetsForRootController
|
import eu.kanade.tachiyomi.util.view.applyWindowInsetsForRootController
|
||||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||||
import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog
|
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.catalogue_main_controller.*
|
import kotlinx.android.synthetic.main.catalogue_main_controller.*
|
||||||
import kotlinx.android.synthetic.main.extensions_bottom_sheet.*
|
import kotlinx.android.synthetic.main.extensions_bottom_sheet.*
|
||||||
@ -52,12 +49,10 @@ import kotlin.math.max
|
|||||||
/**
|
/**
|
||||||
* This controller shows and manages the different catalogues enabled by the user.
|
* This controller shows and manages the different catalogues enabled by the user.
|
||||||
* This controller should only handle UI actions, IO actions should be done by [CataloguePresenter]
|
* This controller should only handle UI actions, IO actions should be done by [CataloguePresenter]
|
||||||
* [SourceLoginDialog.Listener] refreshes the adapter on successful login of catalogues.
|
|
||||||
* [CatalogueAdapter.OnBrowseClickListener] call function data on browse item click.
|
* [CatalogueAdapter.OnBrowseClickListener] call function data on browse item click.
|
||||||
* [CatalogueAdapter.OnLatestClickListener] call function data on latest item click
|
* [CatalogueAdapter.OnLatestClickListener] call function data on latest item click
|
||||||
*/
|
*/
|
||||||
class CatalogueController : NucleusController<CataloguePresenter>(),
|
class CatalogueController : NucleusController<CataloguePresenter>(),
|
||||||
SourceLoginDialog.Listener,
|
|
||||||
FlexibleAdapter.OnItemClickListener,
|
FlexibleAdapter.OnItemClickListener,
|
||||||
FlexibleAdapter.OnItemLongClickListener,
|
FlexibleAdapter.OnItemLongClickListener,
|
||||||
CatalogueAdapter.OnBrowseClickListener,
|
CatalogueAdapter.OnBrowseClickListener,
|
||||||
@ -86,26 +81,15 @@ class CatalogueController : NucleusController<CataloguePresenter>(),
|
|||||||
* Called when controller is initialized.
|
* Called when controller is initialized.
|
||||||
*/
|
*/
|
||||||
init {
|
init {
|
||||||
// Enable the option menu
|
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the title of controller.
|
|
||||||
*
|
|
||||||
* @return title.
|
|
||||||
*/
|
|
||||||
override fun getTitle(): String? {
|
override fun getTitle(): String? {
|
||||||
return if (showingExtenions)
|
return if (showingExtenions)
|
||||||
applicationContext?.getString(R.string.extensions)
|
applicationContext?.getString(R.string.extensions)
|
||||||
else applicationContext?.getString(R.string.sources)
|
else applicationContext?.getString(R.string.sources)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the [CataloguePresenter] used in controller.
|
|
||||||
*
|
|
||||||
* @return instance of [CataloguePresenter]
|
|
||||||
*/
|
|
||||||
override fun createPresenter(): CataloguePresenter {
|
override fun createPresenter(): CataloguePresenter {
|
||||||
return CataloguePresenter()
|
return CataloguePresenter()
|
||||||
}
|
}
|
||||||
@ -121,11 +105,6 @@ class CatalogueController : NucleusController<CataloguePresenter>(),
|
|||||||
return inflater.inflate(R.layout.catalogue_main_controller, container, false)
|
return inflater.inflate(R.layout.catalogue_main_controller, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the view is created
|
|
||||||
*
|
|
||||||
* @param view view of controller
|
|
||||||
*/
|
|
||||||
override fun onViewCreated(view: View) {
|
override fun onViewCreated(view: View) {
|
||||||
super.onViewCreated(view)
|
super.onViewCreated(view)
|
||||||
view.applyWindowInsetsForRootController(activity!!.bottom_nav)
|
view.applyWindowInsetsForRootController(activity!!.bottom_nav)
|
||||||
@ -229,44 +208,28 @@ class CatalogueController : NucleusController<CataloguePresenter>(),
|
|||||||
ext_bottom_sheet?.presenter?.refreshExtensions()
|
ext_bottom_sheet?.presenter?.refreshExtensions()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when login dialog is closed, refreshes the adapter.
|
|
||||||
*
|
|
||||||
* @param source clicked item containing source information.
|
|
||||||
*/
|
|
||||||
override fun loginDialogClosed(source: LoginSource) {
|
|
||||||
if (source.isLogged()) {
|
|
||||||
adapter?.clear()
|
|
||||||
presenter.loadSources()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemClick(view: View, position: Int): Boolean {
|
override fun onItemClick(view: View, position: Int): Boolean {
|
||||||
val item = adapter?.getItem(position) as? SourceItem ?: return false
|
val item = adapter?.getItem(position) as? SourceItem ?: return false
|
||||||
val source = item.source
|
val source = item.source
|
||||||
if (source is LoginSource && !source.isLogged()) {
|
|
||||||
val dialog = SourceLoginDialog(source)
|
|
||||||
dialog.targetController = this
|
|
||||||
dialog.showDialog(router)
|
|
||||||
} else {
|
|
||||||
// Open the catalogue view.
|
// Open the catalogue view.
|
||||||
openCatalogue(source, BrowseCatalogueController(source))
|
openCatalogue(source, BrowseCatalogueController(source))
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemLongClick(position: Int) {
|
override fun onItemLongClick(position: Int) {
|
||||||
val activity = activity ?: return
|
val activity = activity ?: return
|
||||||
val item = adapter?.getItem(position) as? SourceItem ?: return
|
val item = adapter?.getItem(position) as? SourceItem ?: return
|
||||||
|
val isPinned = item.header?.code?.equals(CataloguePresenter.PINNED_KEY) ?: false
|
||||||
|
|
||||||
MaterialDialog(activity)
|
MaterialDialog(activity)
|
||||||
.title(text = item.source.name)
|
.title(text = item.source.name)
|
||||||
.listItems(items = listOf(activity.getString(R.string.hide)),
|
.listItems(items = listOf(
|
||||||
waitForPositiveButton = false, selection = { _, index, _ ->
|
activity.getString(R.string.hide),
|
||||||
|
activity.getString(if (isPinned) R.string.unpin else R.string.pin)
|
||||||
|
), waitForPositiveButton = false, selection = { _, index, _ ->
|
||||||
when (index) {
|
when (index) {
|
||||||
0 -> {
|
0 -> hideCatalogue(item.source)
|
||||||
hideCatalogue(item.source)
|
1 -> pinCatalogue(item.source, isPinned)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}).show()
|
}).show()
|
||||||
}
|
}
|
||||||
@ -278,6 +241,17 @@ class CatalogueController : NucleusController<CataloguePresenter>(),
|
|||||||
presenter.updateSources()
|
presenter.updateSources()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun pinCatalogue(source: Source, isPinned: Boolean) {
|
||||||
|
val current = preferences.pinnedCatalogues().getOrDefault()
|
||||||
|
if (isPinned) {
|
||||||
|
preferences.pinnedCatalogues().set(current - source.id.toString())
|
||||||
|
} else {
|
||||||
|
preferences.pinnedCatalogues().set(current + source.id.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
presenter.updateSources()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when browse is clicked in [CatalogueAdapter]
|
* Called when browse is clicked in [CatalogueAdapter]
|
||||||
*/
|
*/
|
||||||
|
@ -27,9 +27,6 @@ class CataloguePresenter(
|
|||||||
private val preferences: PreferencesHelper = Injekt.get()
|
private val preferences: PreferencesHelper = Injekt.get()
|
||||||
) : BasePresenter<CatalogueController>() {
|
) : BasePresenter<CatalogueController>() {
|
||||||
|
|
||||||
/**
|
|
||||||
* Enabled sources.
|
|
||||||
*/
|
|
||||||
var sources = getEnabledSources()
|
var sources = getEnabledSources()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,9 +45,12 @@ class CataloguePresenter(
|
|||||||
/**
|
/**
|
||||||
* Unsubscribe and create a new subscription to fetch enabled sources.
|
* Unsubscribe and create a new subscription to fetch enabled sources.
|
||||||
*/
|
*/
|
||||||
fun loadSources() {
|
private fun loadSources() {
|
||||||
sourceSubscription?.unsubscribe()
|
sourceSubscription?.unsubscribe()
|
||||||
|
|
||||||
|
val pinnedSources = mutableListOf<SourceItem>()
|
||||||
|
val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault()
|
||||||
|
|
||||||
val map = TreeMap<String, MutableList<CatalogueSource>> { d1, d2 ->
|
val map = TreeMap<String, MutableList<CatalogueSource>> { d1, d2 ->
|
||||||
// Catalogues without a lang defined will be placed at the end
|
// Catalogues without a lang defined will be placed at the end
|
||||||
when {
|
when {
|
||||||
@ -60,9 +60,19 @@ class CataloguePresenter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val byLang = sources.groupByTo(map, { it.lang })
|
val byLang = sources.groupByTo(map, { it.lang })
|
||||||
val sourceItems = byLang.flatMap {
|
var sourceItems = byLang.flatMap {
|
||||||
val langItem = LangItem(it.key)
|
val langItem = LangItem(it.key)
|
||||||
it.value.map { source -> SourceItem(source, langItem) }
|
it.value.map { source ->
|
||||||
|
if (source.id.toString() in pinnedCatalogues) {
|
||||||
|
pinnedSources.add(SourceItem(source, LangItem(PINNED_KEY)))
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceItem(source, langItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pinnedSources.isNotEmpty()) {
|
||||||
|
sourceItems = pinnedSources + sourceItems
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSubscription = Observable.just(sourceItems)
|
sourceSubscription = Observable.just(sourceItems)
|
||||||
@ -101,4 +111,8 @@ class CataloguePresenter(
|
|||||||
.sortedBy { "(${it.lang}) ${it.name}" } +
|
.sortedBy { "(${it.lang}) ${it.name}" } +
|
||||||
sourceManager.get(LocalSource.ID) as LocalSource
|
sourceManager.get(LocalSource.ID) as LocalSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PINNED_KEY = "pinned"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,9 @@ import android.os.Build
|
|||||||
import android.view.ContextThemeWrapper
|
import android.view.ContextThemeWrapper
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import uy.kohesive.injekt.injectLazy
|
import eu.kanade.tachiyomi.ui.catalogue.CataloguePresenter
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to change the application's language in runtime.
|
* Utility class to change the application's language in runtime.
|
||||||
@ -56,6 +57,7 @@ object LocaleHelper {
|
|||||||
return when (lang) {
|
return when (lang) {
|
||||||
null -> ""
|
null -> ""
|
||||||
"" -> context.getString(R.string.other)
|
"" -> context.getString(R.string.other)
|
||||||
|
CataloguePresenter.PINNED_KEY -> context.getString(R.string.pinned)
|
||||||
"all" -> context.getString(R.string.all)
|
"all" -> context.getString(R.string.all)
|
||||||
else -> {
|
else -> {
|
||||||
val locale = getLocale(lang)
|
val locale = getLocale(lang)
|
||||||
|
@ -655,6 +655,8 @@
|
|||||||
<string name="options">Options</string>
|
<string name="options">Options</string>
|
||||||
<string name="pause">Pause</string>
|
<string name="pause">Pause</string>
|
||||||
<string name="picture_saved">Picture saved</string>
|
<string name="picture_saved">Picture saved</string>
|
||||||
|
<string name="pin">Pin</string>
|
||||||
|
<string name="pinned">Pinned</string>
|
||||||
<string name="refresh">Refresh</string>
|
<string name="refresh">Refresh</string>
|
||||||
<string name="refreshing">Refreshing</string>
|
<string name="refreshing">Refreshing</string>
|
||||||
<string name="remove">Remove</string>
|
<string name="remove">Remove</string>
|
||||||
@ -677,6 +679,7 @@
|
|||||||
<string name="undo">Undo</string>
|
<string name="undo">Undo</string>
|
||||||
<string name="unknown_error">Unknown error</string>
|
<string name="unknown_error">Unknown error</string>
|
||||||
<string name="un_select_all">Un-select all</string>
|
<string name="un_select_all">Un-select all</string>
|
||||||
|
<string name="unpin">Unpin</string>
|
||||||
<string name="use_default">Use default</string>
|
<string name="use_default">Use default</string>
|
||||||
<string name="view_all_errors">View all errors</string>
|
<string name="view_all_errors">View all errors</string>
|
||||||
<string name="view_chapters">View chapters</string>
|
<string name="view_chapters">View chapters</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user