Merge pull request #3404 from mauriciocolli/feed-add-filter-sub-list
Add filter to the feed group dialog to show only ungrouped subscriptions
This commit is contained in:
commit
07cead7e99
6 changed files with 107 additions and 27 deletions
|
@ -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)
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue