Add source pinning (closes #2322)

This commit is contained in:
arkon 2020-04-10 18:38:24 -04:00 committed by Jay
parent 849cb4208f
commit 1373a0d61c
5 changed files with 48 additions and 53 deletions

View File

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

View File

@ -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]
*/ */

View File

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

View File

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

View File

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