From b7f7944e00cdb49463826a169f16e581a07fdd6c Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Sun, 10 Sep 2017 23:32:12 +0800 Subject: [PATCH] added scope for filters --- .../twidere/annotation/FilterScope.java | 41 +++++++++ .../mariotaku/twidere/model/FiltersData.java | 30 +++++++ .../twidere/provider/TwidereDataStore.java | 13 +-- .../java/org/mariotaku/twidere/Constants.java | 2 +- .../twidere/activity/HomeActivity.kt | 3 +- .../model/api/mastodon/AccountExtensions.kt | 2 +- .../fragment/CursorActivitiesFragment.kt | 6 +- .../fragment/CursorStatusesFragment.kt | 7 +- .../twidere/fragment/HomeTimelineFragment.kt | 4 + .../fragment/InteractionsTimelineFragment.kt | 4 + .../twidere/util/DataStoreFunctions.kt | 66 +++++++++----- .../mariotaku/twidere/util/DataStoreUtils.kt | 85 +++++++++++++------ .../ContentNotificationManager.kt | 15 ++-- 13 files changed, 206 insertions(+), 72 deletions(-) create mode 100644 twidere.component.common/src/main/java/org/mariotaku/twidere/annotation/FilterScope.java diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/annotation/FilterScope.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/annotation/FilterScope.java new file mode 100644 index 000000000..b4488d327 --- /dev/null +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/annotation/FilterScope.java @@ -0,0 +1,41 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.annotation; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@IntDef(value = {FilterScope.HOME, FilterScope.INTERACTIONS, FilterScope.MESSAGES, + FilterScope.SEARCH_RESULT, FilterScope.LIST_GROUP_TIMELINE, FilterScope.FAVORITES, + FilterScope.ALL}, flag = true) +@Retention(RetentionPolicy.SOURCE) +public @interface FilterScope { + int HOME = 0x1; + int INTERACTIONS = 0x2; + int MESSAGES = 0x4; + int SEARCH_RESULT = 0x8; + int LIST_GROUP_TIMELINE = 0x16; + int FAVORITES = 0x32; + + // Contains all flags + int ALL = 0xFFFFFFFF; +} diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/FiltersData.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/FiltersData.java index ba7325018..6b84a75c1 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/FiltersData.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/FiltersData.java @@ -25,6 +25,7 @@ import com.bluelinelabs.logansquare.annotation.JsonObject; import org.mariotaku.library.objectcursor.annotation.CursorField; import org.mariotaku.library.objectcursor.annotation.CursorObject; +import org.mariotaku.twidere.annotation.FilterScope; import org.mariotaku.twidere.model.util.UserKeyConverter; import org.mariotaku.twidere.model.util.UserKeyCursorFieldConverter; import org.mariotaku.twidere.provider.TwidereDataStore; @@ -109,6 +110,10 @@ public class FiltersData { @CursorField(value = Filters.Users.SOURCE, type = "INTEGER DEFAULT -1") @JsonField(name = "source") long source = -1; + @CursorField(value = Filters.Users.SCOPE, type = "INTEGER DEFAULT 0") + @JsonField(name = "scope") + @FilterScope + int scope = 0; public UserKey getUserKey() { return userKey; @@ -142,6 +147,15 @@ public class FiltersData { this.source = source; } + @FilterScope + public int getScope() { + return scope; + } + + public void setScope(@FilterScope int scope) { + this.scope = scope; + } + @Override public String toString() { return "UserItem{" + @@ -150,6 +164,7 @@ public class FiltersData { ", name='" + name + '\'' + ", screenName='" + screenName + '\'' + ", source=" + source + + ", scope=" + scope + '}'; } @@ -192,6 +207,11 @@ public class FiltersData { @Nullable UserKey userKey = null; + @CursorField(value = Filters.SCOPE, type = "INTEGER DEFAULT 0") + @JsonField(name = "scope") + @FilterScope + int scope = 0; + public long getId() { return _id; } @@ -221,6 +241,15 @@ public class FiltersData { this.userKey = userKey; } + @FilterScope + public int getScope() { + return scope; + } + + public void setScope(@FilterScope int scope) { + this.scope = scope; + } + @Override public String toString() { return "BaseItem{" + @@ -228,6 +257,7 @@ public class FiltersData { ", value='" + value + '\'' + ", source=" + source + ", userKey=" + userKey + + ", scope=" + scope + '}'; } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java index 3e22c8f85..b0ca10e89 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java @@ -498,11 +498,7 @@ public interface TwidereDataStore { String USER_KEY = "user_key"; - String ENABLE_IN_HOME_TIMELINE = "enable_in_home_timeline"; - - String ENABLE_IN_MENTIONS = "enable_in_mentions"; - - String ENABLE_FOR_RETWEETS = "enable_for_retweets"; + String SCOPE = "scope"; String[] COLUMNS = FiltersData$BaseItemTableInfo.COLUMNS; @@ -543,6 +539,7 @@ public interface TwidereDataStore { String NAME = "name"; String SCREEN_NAME = "screen_name"; String SOURCE = "source"; + String SCOPE = "scope"; String[] COLUMNS = FiltersData$UserItemTableInfo.COLUMNS; @@ -733,12 +730,6 @@ public interface TwidereDataStore { String ACCOUNT_COLOR = "account_color"; - String USER_NICKNAME = "user_nickname"; - - String QUOTED_USER_NICKNAME = "quoted_user_nickname"; - String RETWEET_USER_NICKNAME = "retweet_user_nickname"; - String IN_REPLY_TO_USER_NICKNAME = "in_reply_to_user_nickname"; - String FILTER_FLAGS = "filter_flags"; String DEFAULT_SORT_ORDER = TIMESTAMP + " DESC, " + SORT_ID + " DESC, " + ID diff --git a/twidere/src/main/java/org/mariotaku/twidere/Constants.java b/twidere/src/main/java/org/mariotaku/twidere/Constants.java index ee49dd74d..19ae85279 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/Constants.java +++ b/twidere/src/main/java/org/mariotaku/twidere/Constants.java @@ -30,7 +30,7 @@ import org.mariotaku.twidere.model.UserKey; public interface Constants extends TwidereConstants { String DATABASES_NAME = "twidere.sqlite"; - int DATABASES_VERSION = 184; + int DATABASES_VERSION = 185; int EXTRA_FEATURES_NOTICE_VERSION = 2; diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt index d40103e77..0a7f87467 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt @@ -76,6 +76,7 @@ import org.mariotaku.twidere.R import org.mariotaku.twidere.activity.iface.IControlBarActivity.ControlBarShowHideHelper import org.mariotaku.twidere.adapter.SupportTabsAdapter import org.mariotaku.twidere.annotation.CustomTabType +import org.mariotaku.twidere.annotation.FilterScope import org.mariotaku.twidere.annotation.NavbarStyle import org.mariotaku.twidere.annotation.ReadPositionTag import org.mariotaku.twidere.constant.* @@ -946,7 +947,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp }.fold(0L, Math::max) val count = DataStoreUtils.getStatusesCount(context, preferences, Statuses.CONTENT_URI, spec.args, Statuses.TIMESTAMP, position, - true, accountKeys) + true, accountKeys, FilterScope.HOME) result.put(i, count) publishProgress(TabBadge(i, count)) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/api/mastodon/AccountExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/api/mastodon/AccountExtensions.kt index 01496c6ea..0c3f53ff5 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/api/mastodon/AccountExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/api/mastodon/AccountExtensions.kt @@ -52,7 +52,7 @@ fun Account.toParcelable(accountKey: UserKey, position: Long = 0, obj.is_protected = isLocked obj.name = name obj.screen_name = username - if (note?.isHtml ?: false) { + if (note?.isHtml == true) { val descriptionHtml = HtmlSpanBuilder.fromHtml(note, note, MastodonSpanProcessor) obj.description_unescaped = descriptionHtml?.toString() obj.description_plain = obj.description_unescaped diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorActivitiesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorActivitiesFragment.kt index 317a46c09..720161796 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorActivitiesFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorActivitiesFragment.kt @@ -37,6 +37,7 @@ import org.mariotaku.sqliteqb.library.Expression import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition +import org.mariotaku.twidere.annotation.FilterScope import org.mariotaku.twidere.constant.IntentConstants.EXTRA_FROM_USER import org.mariotaku.twidere.extension.queryOne import org.mariotaku.twidere.loader.ExtendedObjectCursorLoader @@ -68,6 +69,9 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() { protected abstract val isFilterEnabled: Boolean + @FilterScope + protected abstract val filterScopes: Int + private var contentObserver: ContentObserver? = null private val accountListener: OnAccountsUpdateListener = OnAccountsUpdateListener { @@ -213,7 +217,7 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() { protected fun getFiltersWhere(table: String): Expression? { if (!isFilterEnabled) return null - return DataStoreUtils.buildActivityFilterWhereClause(table, null) + return DataStoreUtils.buildActivityFilterWhereClause(table, null, filterScopes) } protected open fun processWhere(where: Expression, whereArgs: Array): ParameterizedExpression { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorStatusesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorStatusesFragment.kt index a1e96172f..5079f7265 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorStatusesFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorStatusesFragment.kt @@ -38,6 +38,7 @@ import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.ListParcelableStatusesAdapter import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition +import org.mariotaku.twidere.annotation.FilterScope import org.mariotaku.twidere.constant.IntentConstants.EXTRA_FROM_USER import org.mariotaku.twidere.loader.ExtendedObjectCursorLoader import org.mariotaku.twidere.model.ParameterizedExpression @@ -75,6 +76,8 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() { abstract val isFilterEnabled: Boolean abstract val notificationType: Int abstract val contentUri: Uri + @FilterScope + abstract val filterScopes: Int private var contentObserver: ContentObserver? = null private val accountListener: OnAccountsUpdateListener = OnAccountsUpdateListener { @@ -208,7 +211,7 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() { protected fun getFiltersWhere(table: String): Expression? { if (!isFilterEnabled) return null - return buildStatusFilterWhereClause(preferences, table, null) + return buildStatusFilterWhereClause(preferences, table, null, filterScopes) } protected open fun processWhere(where: Expression, whereArgs: Array): ParameterizedExpression { @@ -316,6 +319,6 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() { companion object { private val statusColumnsLite = Statuses.COLUMNS - arrayOf(Statuses.MENTIONS_JSON, - Statuses.CARD) + Statuses.CARD, Statuses.FILTER_FLAGS) } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/HomeTimelineFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/HomeTimelineFragment.kt index e6becb321..4a547fd61 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/HomeTimelineFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/HomeTimelineFragment.kt @@ -21,6 +21,7 @@ package org.mariotaku.twidere.fragment import org.mariotaku.sqliteqb.library.Expression import org.mariotaku.twidere.TwidereConstants.NOTIFICATION_ID_HOME_TIMELINE +import org.mariotaku.twidere.annotation.FilterScope import org.mariotaku.twidere.annotation.ReadPositionTag import org.mariotaku.twidere.constant.IntentConstants.EXTRA_EXTRAS import org.mariotaku.twidere.model.ParameterizedExpression @@ -50,6 +51,9 @@ class HomeTimelineFragment : CursorStatusesFragment() { override val timelineSyncTag: String? get() = getTimelineSyncTag(accountKeys) + override val filterScopes: Int + get() = FilterScope.HOME + override fun updateRefreshState() { val twitter = twitterWrapper refreshing = twitter.isStatusTimelineRefreshing(contentUri) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/InteractionsTimelineFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/InteractionsTimelineFragment.kt index ccaea438a..ca5e2b1fc 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/InteractionsTimelineFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/InteractionsTimelineFragment.kt @@ -25,6 +25,7 @@ import org.mariotaku.microblog.library.twitter.model.Activity import org.mariotaku.sqliteqb.library.Expression import org.mariotaku.twidere.TwidereConstants.NOTIFICATION_ID_INTERACTIONS_TIMELINE import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter +import org.mariotaku.twidere.annotation.FilterScope import org.mariotaku.twidere.annotation.ReadPositionTag import org.mariotaku.twidere.constant.IntentConstants.EXTRA_EXTRAS import org.mariotaku.twidere.model.ParameterizedExpression @@ -50,6 +51,9 @@ class InteractionsTimelineFragment : CursorActivitiesFragment() { override val timelineSyncTag: String? get() = getTimelineSyncTag(accountKeys) + override val filterScopes: Int + get() = FilterScope.INTERACTIONS + override fun onCreateAdapter(context: Context, requestManager: RequestManager): ParcelableActivitiesAdapter { val adapter = ParcelableActivitiesAdapter(context, requestManager) val extras: InteractionsTabExtras? = arguments.getParcelable(EXTRA_EXTRAS) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt index 92e2a8c9c..7733edc58 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt @@ -17,6 +17,7 @@ import org.mariotaku.ktextension.useCursor import org.mariotaku.library.objectcursor.ObjectCursor import org.mariotaku.sqliteqb.library.* import org.mariotaku.sqliteqb.library.Columns.Column +import org.mariotaku.twidere.annotation.FilterScope import org.mariotaku.twidere.constant.filterPossibilitySensitiveStatusesKey import org.mariotaku.twidere.constant.filterUnavailableQuoteStatusesKey import org.mariotaku.twidere.extension.model.component1 @@ -32,21 +33,55 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations import org.mariotaku.twidere.util.DataStoreUtils.ACTIVITIES_URIS import java.io.IOException -/** - * Created by mariotaku on 2016/12/24. - */ - fun buildStatusFilterWhereClause(preferences: SharedPreferences, table: String, - extraSelection: Expression?): Expression { + extraSelection: Expression?, @FilterScope filterScopes: Int = FilterScope.ALL): Expression { val filteredUsersQuery = SQLQueryBuilder .select(Column(Table(Filters.Users.TABLE_NAME), Filters.Users.USER_KEY)) .from(Tables(Filters.Users.TABLE_NAME)) + .where(Expression.or( + Expression.equals(Column(Table(Filters.Users.TABLE_NAME), Filters.Users.SCOPE), 0), + Expression.notEquals("${Filters.Users.TABLE_NAME}.${Filters.Users.SCOPE} & $filterScopes", 0) + )) .build() val filteredUsersWhere = Expression.or( Expression.`in`(Column(Table(table), Statuses.USER_KEY), filteredUsersQuery), Expression.`in`(Column(Table(table), Statuses.RETWEETED_BY_USER_KEY), filteredUsersQuery), Expression.`in`(Column(Table(table), Statuses.QUOTED_USER_KEY), filteredUsersQuery) ) + val filteredSourcesWhere = Expression.and( + Expression.or( + Expression.equals(Column(Table(Filters.Sources.TABLE_NAME), Filters.Sources.SCOPE), 0), + Expression.notEquals("${Filters.Sources.TABLE_NAME}.${Filters.Sources.SCOPE} & $filterScopes", 0) + ), + Expression.or( + Expression.likeRaw(Column(Table(table), Statuses.SOURCE), + "'%>'||${Filters.Sources.TABLE_NAME}.${Filters.Sources.VALUE}||'%'"), + Expression.likeRaw(Column(Table(table), Statuses.QUOTED_SOURCE), + "'%>'||${Filters.Sources.TABLE_NAME}.${Filters.Sources.VALUE}||'%'") + ) + ) + val filteredKeywordsWhere = Expression.and( + Expression.or( + Expression.equals(Column(Table(Filters.Keywords.TABLE_NAME), Filters.Keywords.SCOPE), 0), + Expression.notEquals("${Filters.Keywords.TABLE_NAME}.${Filters.Keywords.SCOPE} & $filterScopes", 0) + ), + Expression.or(Expression.likeRaw(Column(Table(table), Statuses.TEXT_PLAIN), + "'%'||${Filters.Keywords.TABLE_NAME}.${Filters.Keywords.VALUE}||'%'"), + Expression.likeRaw(Column(Table(table), Statuses.QUOTED_TEXT_PLAIN), + "'%'||${Filters.Keywords.TABLE_NAME}.${Filters.Keywords.VALUE}||'%'")) + ) + val filteredLinksWhere = Expression.and( + Expression.or( + Expression.equals(Column(Table(Filters.Links.TABLE_NAME), Filters.Links.SCOPE), 0), + Expression.notEquals("${Filters.Links.TABLE_NAME}.${Filters.Links.SCOPE} & $filterScopes", 0) + ), + Expression.or( + Expression.likeRaw(Column(Table(table), Statuses.SPANS), + "'%'||${Filters.Links.TABLE_NAME}.${Filters.Links.VALUE}||'%'"), + Expression.likeRaw(Column(Table(table), Statuses.QUOTED_SPANS), + "'%'||${Filters.Links.TABLE_NAME}.${Filters.Links.VALUE}||'%'") + ) + ) val filteredIdsQueryBuilder = SQLQueryBuilder .select(Column(Table(table), Statuses._ID)) .from(Tables(table)) @@ -54,30 +89,15 @@ fun buildStatusFilterWhereClause(preferences: SharedPreferences, table: String, .union() .select(Columns(Column(Table(table), Statuses._ID))) .from(Tables(table, Filters.Sources.TABLE_NAME)) - .where(Expression.or( - Expression.likeRaw(Column(Table(table), Statuses.SOURCE), - "'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'%'"), - Expression.likeRaw(Column(Table(table), Statuses.QUOTED_SOURCE), - "'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'%'") - )) + .where(filteredSourcesWhere) .union() .select(Columns(Column(Table(table), Statuses._ID))) .from(Tables(table, Filters.Keywords.TABLE_NAME)) - .where(Expression.or( - Expression.likeRaw(Column(Table(table), Statuses.TEXT_PLAIN), - "'%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%'"), - Expression.likeRaw(Column(Table(table), Statuses.QUOTED_TEXT_PLAIN), - "'%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%'") - )) + .where(filteredKeywordsWhere) .union() .select(Columns(Column(Table(table), Statuses._ID))) .from(Tables(table, Filters.Links.TABLE_NAME)) - .where(Expression.or( - Expression.likeRaw(Column(Table(table), Statuses.SPANS), - "'%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%'"), - Expression.likeRaw(Column(Table(table), Statuses.QUOTED_SPANS), - "'%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%'") - )) + .where(filteredLinksWhere) var filterFlags: Long = 0 if (preferences[filterUnavailableQuoteStatusesKey]) { filterFlags = filterFlags or FilterFlags.QUOTE_NOT_AVAILABLE diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt index 1b7102dd0..fc642518f 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt @@ -43,6 +43,7 @@ import org.mariotaku.sqliteqb.library.query.SQLSelectQuery import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants.* import org.mariotaku.twidere.annotation.AccountType +import org.mariotaku.twidere.annotation.FilterScope import org.mariotaku.twidere.constant.IntentConstants import org.mariotaku.twidere.constant.databaseItemLimitKey import org.mariotaku.twidere.extension.model.* @@ -325,7 +326,7 @@ object DataStoreUtils { fun getStatusesCount(context: Context, preferences: SharedPreferences, uri: Uri, extraArgs: Bundle?, compareColumn: String, compare: Long, greaterThan: Boolean, - accountKeys: Array?): Int { + accountKeys: Array?, @FilterScope filterScopes: Int = FilterScope.ALL): Int { val keys = accountKeys ?: getActivatedAccountKeys(context) val expressions = ArrayList() @@ -342,7 +343,8 @@ object DataStoreUtils { expressions.add(Expression.lesserThan(compareColumn, compare)) } - expressions.add(buildStatusFilterWhereClause(preferences, getTableNameByUri(uri)!!, null)) + expressions.add(buildStatusFilterWhereClause(preferences, getTableNameByUri(uri)!!, + null, filterScopes)) if (extraArgs != null) { val extras = extraArgs.getParcelable(EXTRA_EXTRAS) @@ -356,7 +358,8 @@ object DataStoreUtils { } fun getActivitiesCount(context: Context, uri: Uri, compareColumn: String, - compare: Long, greaterThan: Boolean, accountKeys: Array?): Int { + compare: Long, greaterThan: Boolean, accountKeys: Array?, + @FilterScope filterScopes: Int = FilterScope.ALL): Int { val keys = accountKeys ?: getActivatedAccountKeys(context) val selection = Expression.and( Expression.inArgs(Column(Activities.ACCOUNT_KEY), keys.size), @@ -365,7 +368,8 @@ object DataStoreUtils { } else { Expression.lesserThan(compareColumn, compare) }, - buildActivityFilterWhereClause(getTableNameByUri(uri)!!, null) + buildActivityFilterWhereClause(getTableNameByUri(uri)!!, null, + filterScopes) ) val whereArgs = arrayListOf() keys.mapTo(whereArgs) { it.toString() } @@ -375,12 +379,13 @@ object DataStoreUtils { fun getActivitiesCount(context: Context, uri: Uri, extraWhere: Expression?, extraWhereArgs: Array?, sinceColumn: String, since: Long, followingOnly: Boolean, - accountKeys: Array?): Int { + accountKeys: Array?, @FilterScope filterScopes: Int = FilterScope.ALL): Int { val keys = (accountKeys ?: getActivatedAccountKeys(context)).mapToArray { it.toString() } val expressions = ArrayList() expressions.add(Expression.inArgs(Column(Activities.ACCOUNT_KEY), keys.size)) expressions.add(Expression.greaterThan(sinceColumn, since)) - expressions.add(buildActivityFilterWhereClause(getTableNameByUri(uri)!!, null)) + expressions.add(buildActivityFilterWhereClause(getTableNameByUri(uri)!!, null, + filterScopes)) if (extraWhere != null) { expressions.add(extraWhere) } @@ -457,16 +462,57 @@ object DataStoreUtils { return getTableNameById(getTableId(uri)) } - fun buildActivityFilterWhereClause(table: String, extraSelection: Expression?): Expression { + fun buildActivityFilterWhereClause(table: String, extraSelection: Expression?, + @FilterScope filterScopes: Int = FilterScope.ALL): Expression { val filteredUsersQuery = SQLQueryBuilder .select(Column(Table(Filters.Users.TABLE_NAME), Filters.Users.USER_KEY)) .from(Tables(Filters.Users.TABLE_NAME)) + .where(Expression.or( + Expression.equals(Column(Table(Filters.Users.TABLE_NAME), Filters.Users.SCOPE), 0), + Expression.notEquals("${Filters.Users.TABLE_NAME}.${Filters.Users.SCOPE} & $filterScopes", 0) + )) .build() val filteredUsersWhere = Expression.or( Expression.`in`(Column(Table(table), Activities.USER_KEY), filteredUsersQuery), Expression.`in`(Column(Table(table), Activities.RETWEETED_BY_USER_KEY), filteredUsersQuery), Expression.`in`(Column(Table(table), Activities.QUOTED_USER_KEY), filteredUsersQuery) ) + val filteredSourcesWhere = Expression.and( + Expression.or( + Expression.equals(Column(Table(Filters.Sources.TABLE_NAME), Filters.Sources.SCOPE), 0), + Expression.notEquals("${Filters.Sources.TABLE_NAME}.${Filters.Sources.SCOPE} & $filterScopes", 0) + ), + Expression.or( + Expression.likeRaw(Column(Table(table), Activities.SOURCE), + "'%>'||${Filters.Sources.TABLE_NAME}.${Filters.Sources.VALUE}||'%'"), + Expression.likeRaw(Column(Table(table), Activities.QUOTED_SOURCE), + "'%>'||${Filters.Sources.TABLE_NAME}.${Filters.Sources.VALUE}||'%'") + ) + ) + val filteredKeywordsWhere = Expression.and( + Expression.or( + Expression.equals(Column(Table(Filters.Keywords.TABLE_NAME), Filters.Keywords.SCOPE), 0), + Expression.notEquals("${Filters.Keywords.TABLE_NAME}.${Filters.Keywords.SCOPE} & $filterScopes", 0) + ), + Expression.or( + Expression.likeRaw(Column(Table(table), Activities.TEXT_PLAIN), + "'%'||${Filters.Keywords.TABLE_NAME}.${Filters.Keywords.VALUE}||'%'"), + Expression.likeRaw(Column(Table(table), Activities.QUOTED_TEXT_PLAIN), + "'%'||${Filters.Keywords.TABLE_NAME}.${Filters.Keywords.VALUE}||'%'") + ) + ) + val filteredLinksWhere = Expression.and( + Expression.or( + Expression.equals(Column(Table(Filters.Links.TABLE_NAME), Filters.Links.SCOPE), 0), + Expression.notEquals("${Filters.Links.TABLE_NAME}.${Filters.Links.SCOPE} & $filterScopes", 0) + ), + Expression.or( + Expression.likeRaw(Column(Table(table), Activities.SPANS), + "'%'||${Filters.Links.TABLE_NAME}.${Filters.Links.VALUE}||'%'"), + Expression.likeRaw(Column(Table(table), Activities.QUOTED_SPANS), + "'%'||${Filters.Links.TABLE_NAME}.${Filters.Links.VALUE}||'%'") + ) + ) val filteredIdsQueryBuilder = SQLQueryBuilder .select(Column(Table(table), Activities._ID)) .from(Tables(table)) @@ -474,30 +520,15 @@ object DataStoreUtils { .union() .select(Columns(Column(Table(table), Activities._ID))) .from(Tables(table, Filters.Sources.TABLE_NAME)) - .where(Expression.or( - Expression.likeRaw(Column(Table(table), Activities.SOURCE), - "'%>'||${Filters.Sources.TABLE_NAME}.${Filters.Sources.VALUE}||'%'"), - Expression.likeRaw(Column(Table(table), Activities.QUOTED_SOURCE), - "'%>'||${Filters.Sources.TABLE_NAME}.${Filters.Sources.VALUE}||'%'") - )) + .where(filteredSourcesWhere) .union() .select(Columns(Column(Table(table), Activities._ID))) .from(Tables(table, Filters.Keywords.TABLE_NAME)) - .where(Expression.or( - Expression.likeRaw(Column(Table(table), Activities.TEXT_PLAIN), - "'%'||${Filters.Keywords.TABLE_NAME}.${Filters.Keywords.VALUE}||'%'"), - Expression.likeRaw(Column(Table(table), Activities.QUOTED_TEXT_PLAIN), - "'%'||${Filters.Keywords.TABLE_NAME}.${Filters.Keywords.VALUE}||'%'") - )) + .where(filteredKeywordsWhere) .union() .select(Columns(Column(Table(table), Activities._ID))) .from(Tables(table, Filters.Links.TABLE_NAME)) - .where(Expression.or( - Expression.likeRaw(Column(Table(table), Activities.SPANS), - "'%'||${Filters.Links.TABLE_NAME}.${Filters.Links.VALUE}||'%'"), - Expression.likeRaw(Column(Table(table), Activities.QUOTED_SPANS), - "'%'||${Filters.Links.TABLE_NAME}.${Filters.Links.VALUE}||'%'") - )) + .where(filteredLinksWhere) val filterExpression = Expression.or( Expression.notIn(Column(Table(table), Activities._ID), filteredIdsQueryBuilder.build()), Expression.equals(Column(Table(table), Activities.IS_GAP), 1) @@ -802,7 +833,7 @@ object DataStoreUtils { } fun getInteractionsCount(context: Context, extraArgs: Bundle?, accountKeys: Array, - since: Long, sinceColumn: String): Int { + since: Long, sinceColumn: String, @FilterScope filterScopes: Int = FilterScope.ALL): Int { var extraWhere: Expression? = null var extraWhereArgs: Array? = null var followingOnly = false @@ -819,7 +850,7 @@ object DataStoreUtils { } } return getActivitiesCount(context, Activities.AboutMe.CONTENT_URI, extraWhere, extraWhereArgs, - sinceColumn, since, followingOnly, accountKeys) + sinceColumn, since, followingOnly, accountKeys, filterScopes) } fun addToFilter(context: Context, users: Collection, filterAnywhere: Boolean) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/ContentNotificationManager.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/ContentNotificationManager.kt index b1b9e86c9..ec0c5856a 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/ContentNotificationManager.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/ContentNotificationManager.kt @@ -42,6 +42,7 @@ import org.mariotaku.twidere.TwidereConstants.* import org.mariotaku.twidere.activity.HomeActivity import org.mariotaku.twidere.activity.LinkHandlerActivity import org.mariotaku.twidere.annotation.CustomTabType +import org.mariotaku.twidere.annotation.FilterScope import org.mariotaku.twidere.annotation.NotificationType import org.mariotaku.twidere.constant.IntentConstants import org.mariotaku.twidere.constant.iWantMyStarsBackKey @@ -88,7 +89,7 @@ class ContentNotificationManager( ) val selectionArgs = arrayOf(accountKey.toString()) val filteredSelection = buildStatusFilterWhereClause(preferences, Statuses.TABLE_NAME, - selection) + selection, FilterScope.HOME) val userProjection = arrayOf(Statuses.USER_KEY, Statuses.USER_NAME, Statuses.USER_SCREEN_NAME) val statusProjection = arrayOf(Statuses.POSITION_KEY) @@ -169,12 +170,15 @@ class ContentNotificationManager( val cr = context.contentResolver val accountKey = pref.accountKey val account = AccountUtils.getAccountDetails(am, accountKey, false) ?: return - val where = Expression.and( + val selection = Expression.and( Expression.equalsArgs(Activities.ACCOUNT_KEY), Expression.greaterThan(Activities.POSITION_KEY, position), Expression.notEquals(Activities.IS_GAP, 1) - ).sql - val whereArgs = arrayOf(accountKey.toString()) + ) + val selectionArgs = arrayOf(accountKey.toString()) + + val filteredSelection = DataStoreUtils.buildActivityFilterWhereClause(Activities.AboutMe.TABLE_NAME, + selection, FilterScope.INTERACTIONS) val builder = NotificationChannelSpec.contentInteractions.accountNotificationBuilder(context, accountKey) val pebbleNotificationStringBuilder = StringBuilder() @@ -197,7 +201,8 @@ class ContentNotificationManager( val filteredUserKeys = DataStoreUtils.getFilteredUserKeys(context) - val (remaining, consumed) = cr.queryReference(Activities.AboutMe.CONTENT_URI, Activities.COLUMNS, where, whereArgs, + val (remaining, consumed) = cr.queryReference(Activities.AboutMe.CONTENT_URI, Activities.COLUMNS, + filteredSelection.sql, selectionArgs, OrderBy(Activities.TIMESTAMP, false).sql).use { (cur) -> if (cur == null || cur.isEmpty) return@use Pair(-1, -1) val ci = ObjectCursor.indicesFrom(cur, ParcelableActivity::class.java)