Add filter to the feed group dialog to show only ungrouped subscriptions

This commit is contained in:
Mauricio Colli 2020-04-09 13:07:02 -03:00
parent 9f3b35634a
commit 2e6e75cd4e
No known key found for this signature in database
GPG Key ID: F200BFD6F29DDD85
6 changed files with 107 additions and 27 deletions

View File

@ -22,10 +22,42 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
@Query(""" @Query("""
SELECT * FROM subscriptions SELECT * FROM subscriptions
WHERE name LIKE '%' || :filter || '%' WHERE name LIKE '%' || :filter || '%'
ORDER BY name COLLATE NOCASE ASC ORDER BY name COLLATE NOCASE ASC
""") """)
abstract fun filterByName(filter: String): Flowable<List<SubscriptionEntity>> abstract fun getSubscriptionsFiltered(filter: String): Flowable<List<SubscriptionEntity>>
@Query("""
SELECT * FROM subscriptions s
LEFT JOIN feed_group_subscription_join fgs
ON s.uid = fgs.subscription_id
WHERE (fgs.subscription_id IS NULL OR fgs.group_id = :currentGroupId)
ORDER BY name COLLATE NOCASE ASC
""")
abstract fun getSubscriptionsOnlyUngrouped(
currentGroupId: Long
): Flowable<List<SubscriptionEntity>>
@Query("""
SELECT * FROM subscriptions s
LEFT JOIN feed_group_subscription_join fgs
ON s.uid = fgs.subscription_id
WHERE (fgs.subscription_id IS NULL OR fgs.group_id = :currentGroupId)
AND s.name LIKE '%' || :filter || '%'
ORDER BY name COLLATE NOCASE ASC
""")
abstract fun getSubscriptionsOnlyUngroupedFiltered(
currentGroupId: Long,
filter: String
): Flowable<List<SubscriptionEntity>>
@Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId") @Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
abstract fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable<List<SubscriptionEntity>> abstract fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable<List<SubscriptionEntity>>
@ -59,7 +91,7 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
entity.uid = uidFromInsert entity.uid = uidFromInsert
} else { } else {
val subscriptionIdFromDb = getSubscriptionIdInternal(entity.serviceId, entity.url) val subscriptionIdFromDb = getSubscriptionIdInternal(entity.serviceId, entity.url)
?: throw IllegalStateException("Subscription cannot be null just after insertion.") ?: throw IllegalStateException("Subscription cannot be null just after insertion.")
entity.uid = subscriptionIdFromDb entity.uid = subscriptionIdFromDb
update(entity) update(entity)

View File

@ -2,9 +2,11 @@ package org.schabi.newpipe.local.subscription
import android.content.Context import android.content.Context
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.schabi.newpipe.NewPipeDatabase import org.schabi.newpipe.NewPipeDatabase
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
import org.schabi.newpipe.database.subscription.SubscriptionDAO import org.schabi.newpipe.database.subscription.SubscriptionDAO
import org.schabi.newpipe.database.subscription.SubscriptionEntity import org.schabi.newpipe.database.subscription.SubscriptionEntity
import org.schabi.newpipe.extractor.ListInfo import org.schabi.newpipe.extractor.ListInfo
@ -21,11 +23,28 @@ class SubscriptionManager(context: Context) {
fun subscriptionTable(): SubscriptionDAO = subscriptionTable fun subscriptionTable(): SubscriptionDAO = subscriptionTable
fun subscriptions() = subscriptionTable.all fun subscriptions() = subscriptionTable.all
fun filterByName(filter: String) = subscriptionTable.filterByName(filter) fun getSubscriptions(
currentGroupId: Long = FeedGroupEntity.GROUP_ALL_ID,
filterQuery: String = "",
showOnlyUngrouped: Boolean = false
): Flowable<List<SubscriptionEntity>> {
return when {
filterQuery.isNotEmpty() -> {
return if (showOnlyUngrouped) {
subscriptionTable.getSubscriptionsOnlyUngroupedFiltered(
currentGroupId, filterQuery)
} else {
subscriptionTable.getSubscriptionsFiltered(filterQuery)
}
}
showOnlyUngrouped -> subscriptionTable.getSubscriptionsOnlyUngrouped(currentGroupId)
else -> subscriptionTable.all
}
}
fun upsertAll(infoList: List<ChannelInfo>): List<SubscriptionEntity> { fun upsertAll(infoList: List<ChannelInfo>): List<SubscriptionEntity> {
val listEntities = subscriptionTable.upsertAll( val listEntities = subscriptionTable.upsertAll(
infoList.map { SubscriptionEntity.from(it) }) infoList.map { SubscriptionEntity.from(it) })
database.runInTransaction { database.runInTransaction {
infoList.forEachIndexed { index, info -> infoList.forEachIndexed { index, info ->
@ -37,13 +56,13 @@ class SubscriptionManager(context: Context) {
} }
fun updateChannelInfo(info: ChannelInfo): Completable = subscriptionTable.getSubscription(info.serviceId, info.url) fun updateChannelInfo(info: ChannelInfo): Completable = subscriptionTable.getSubscription(info.serviceId, info.url)
.flatMapCompletable { .flatMapCompletable {
Completable.fromRunnable { Completable.fromRunnable {
it.setData(info.name, info.avatarUrl, info.description, info.subscriberCount) it.setData(info.name, info.avatarUrl, info.description, info.subscriberCount)
subscriptionTable.update(it) subscriptionTable.update(it)
feedDatabaseManager.upsertAll(it.uid, info.relatedItems) feedDatabaseManager.upsertAll(it.uid, info.relatedItems)
}
} }
}
fun updateFromInfo(subscriptionId: Long, info: ListInfo<StreamInfoItem>) { fun updateFromInfo(subscriptionId: Long, info: ListInfo<StreamInfoItem>) {
val subscriptionEntity = subscriptionTable.getSubscription(subscriptionId) val subscriptionEntity = subscriptionTable.getSubscription(subscriptionId)
@ -59,8 +78,8 @@ class SubscriptionManager(context: Context) {
fun deleteSubscription(serviceId: Int, url: String): Completable { fun deleteSubscription(serviceId: Int, url: String): Completable {
return Completable.fromCallable { subscriptionTable.deleteSubscription(serviceId, url) } return Completable.fromCallable { subscriptionTable.deleteSubscription(serviceId, url) }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
} }
fun insertSubscription(subscriptionEntity: SubscriptionEntity, info: ChannelInfo) { fun insertSubscription(subscriptionEntity: SubscriptionEntity, info: ChannelInfo) {

View File

@ -65,6 +65,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
@State @JvmField var iconsListState: Parcelable? = null @State @JvmField var iconsListState: Parcelable? = null
@State @JvmField var wasSearchSubscriptionsVisible = false @State @JvmField var wasSearchSubscriptionsVisible = false
@State @JvmField var subscriptionsCurrentSearchQuery = "" @State @JvmField var subscriptionsCurrentSearchQuery = ""
@State @JvmField var subscriptionsShowOnlyUngrouped = false
private val subscriptionMainSection = Section() private val subscriptionMainSection = Section()
private val subscriptionEmptyFooter = Section() private val subscriptionEmptyFooter = Section()
@ -116,7 +117,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
viewModel = ViewModelProvider(this, viewModel = ViewModelProvider(this,
FeedGroupDialogViewModel.Factory(requireContext(), FeedGroupDialogViewModel.Factory(requireContext(),
groupId, subscriptionsCurrentSearchQuery) groupId, subscriptionsCurrentSearchQuery, subscriptionsShowOnlyUngrouped)
).get(FeedGroupDialogViewModel::class.java) ).get(FeedGroupDialogViewModel::class.java)
viewModel.groupLiveData.observe(viewLifecycleOwner, Observer(::handleGroup)) viewModel.groupLiveData.observe(viewLifecycleOwner, Observer(::handleGroup))
@ -216,6 +217,16 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
true true
} }
headerMenu.findItem(R.id.feed_group_toggle_show_only_ungrouped_subscriptions).apply {
isChecked = subscriptionsShowOnlyUngrouped
setOnMenuItemClickListener {
subscriptionsShowOnlyUngrouped = !subscriptionsShowOnlyUngrouped
it.isChecked = subscriptionsShowOnlyUngrouped
viewModel.toggleShowOnlyUngrouped(subscriptionsShowOnlyUngrouped)
true
}
}
toolbar_search_clear.setOnClickListener { toolbar_search_clear.setOnClickListener {
if (TextUtils.isEmpty(toolbar_search_edit_text.text)) { if (TextUtils.isEmpty(toolbar_search_edit_text.text)) {
hideSearch() hideSearch()

View File

@ -20,24 +20,25 @@ import org.schabi.newpipe.local.subscription.item.PickerSubscriptionItem
class FeedGroupDialogViewModel( class FeedGroupDialogViewModel(
applicationContext: Context, applicationContext: Context,
private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID, private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
initialQuery: String = "" initialQuery: String = "",
initialShowOnlyUngrouped: Boolean = false
) : ViewModel() { ) : ViewModel() {
private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(applicationContext) private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(applicationContext)
private var subscriptionManager = SubscriptionManager(applicationContext) private var subscriptionManager = SubscriptionManager(applicationContext)
private var filterSubscriptions = BehaviorProcessor.create<String>() private var filterSubscriptions = BehaviorProcessor.create<String>()
private var allSubscriptions = subscriptionManager.subscriptions() private var toggleShowOnlyUngrouped = BehaviorProcessor.create<Boolean>()
private var subscriptionsFlowable = filterSubscriptions private var subscriptionsFlowable = Flowable
.startWith(initialQuery) .combineLatest(
filterSubscriptions.startWith(initialQuery),
toggleShowOnlyUngrouped.startWith(initialShowOnlyUngrouped),
BiFunction { t1: String, t2: Boolean -> Filter(t1, t2) }
)
.distinctUntilChanged() .distinctUntilChanged()
.switchMap { query -> .switchMap { filter ->
if (query.isEmpty()) { subscriptionManager.getSubscriptions(groupId, filter.query, filter.showOnlyUngrouped)
allSubscriptions
} else {
subscriptionManager.filterByName(query)
}
}.map { list -> list.map { PickerSubscriptionItem(it) } } }.map { list -> list.map { PickerSubscriptionItem(it) } }
private val mutableGroupLiveData = MutableLiveData<FeedGroupEntity>() private val mutableGroupLiveData = MutableLiveData<FeedGroupEntity>()
@ -100,20 +101,27 @@ class FeedGroupDialogViewModel(
filterSubscriptions.onNext("") filterSubscriptions.onNext("")
} }
fun toggleShowOnlyUngrouped(showOnlyUngrouped: Boolean) {
toggleShowOnlyUngrouped.onNext(showOnlyUngrouped)
}
sealed class DialogEvent { sealed class DialogEvent {
object ProcessingEvent : DialogEvent() object ProcessingEvent : DialogEvent()
object SuccessEvent : DialogEvent() object SuccessEvent : DialogEvent()
} }
data class Filter(val query: String, val showOnlyUngrouped: Boolean)
class Factory( class Factory(
private val context: Context, private val context: Context,
private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID, private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
private val initialQuery: String = "" private val initialQuery: String = "",
private val initialShowOnlyUngrouped: Boolean = false
) : ViewModelProvider.Factory { ) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T { override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return FeedGroupDialogViewModel(context.applicationContext, return FeedGroupDialogViewModel(context.applicationContext,
groupId, initialQuery) as T groupId, initialQuery, initialShowOnlyUngrouped) as T
} }
} }
} }

View File

@ -1,10 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/action_search" android:id="@+id/action_search"
android:icon="?attr/ic_search" android:icon="?attr/ic_search"
android:title="@string/search" android:title="@string/search"
app:showAsAction="always" /> app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
<item
android:id="@+id/feed_group_toggle_show_only_ungrouped_subscriptions"
android:checkable="true"
android:checked="false"
android:title="@string/feed_group_show_only_ungrouped_subscriptions"
app:showAsAction="never" />
</menu> </menu>

View File

@ -637,6 +637,7 @@
<string name="feed_group_dialog_name_input">Name</string> <string name="feed_group_dialog_name_input">Name</string>
<string name="feed_group_dialog_delete_message">Do you want to delete this group?</string> <string name="feed_group_dialog_delete_message">Do you want to delete this group?</string>
<string name="feed_create_new_group_button_title">New</string> <string name="feed_create_new_group_button_title">New</string>
<string name="feed_group_show_only_ungrouped_subscriptions">Show only ungrouped subscriptions</string>
<string name="settings_category_feed_title">Feed</string> <string name="settings_category_feed_title">Feed</string>
<string name="feed_update_threshold_title">Feed update threshold</string> <string name="feed_update_threshold_title">Feed update threshold</string>
<string name="feed_update_threshold_summary">Time after last update before a subscription is considered outdated — %s</string> <string name="feed_update_threshold_summary">Time after last update before a subscription is considered outdated — %s</string>