supported filters for description

This commit is contained in:
Mariotaku Lee 2017-09-14 23:38:31 +08:00
parent c06c500092
commit 5066f36506
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
22 changed files with 277 additions and 121 deletions

View File

@ -79,7 +79,6 @@ subprojects {
ACRA : '4.9.2',
AbstractTask : '0.9.5',
Dagger : '2.11',
ExpandableLayout : '1.6.0',
]
}

View File

@ -26,8 +26,9 @@ import java.lang.annotation.RetentionPolicy;
@IntDef(value = {FilterScope.HOME, FilterScope.INTERACTIONS, FilterScope.MESSAGES,
FilterScope.SEARCH_RESULTS, FilterScope.LIST_GROUP_TIMELINE, FilterScope.FAVORITES,
FilterScope.USER_TIMELINE, FilterScope.PUBLIC_TIMELINE, FilterScope.ALL,
FilterScope.FLAG_MATCH_NAME, FilterScope.FLAG_MATCH_TEXT, FilterScope.UGC_TIMELINE}, flag = true)
FilterScope.USER_TIMELINE, FilterScope.PUBLIC_TIMELINE, FilterScope.UGC_TIMELINE,
FilterScope.FLAG_MATCH_NAME, FilterScope.FLAG_MATCH_TEXT, FilterScope.FLAG_MATCH_DESCRIPTION,
FilterScope.ALL, FilterScope.DEFAULT}, flag = true)
@Retention(RetentionPolicy.SOURCE)
public @interface FilterScope {
int HOME = 0x1;
@ -43,6 +44,7 @@ public @interface FilterScope {
int FLAG_MATCH_NAME = 0x80000000;
int FLAG_MATCH_TEXT = 0x40000000;
int FLAG_MATCH_DESCRIPTION = 0x20000000;
int MASK_FLAG = 0xFF000000;
int MASK_SCOPE = 0x00FFFFFF;
@ -54,4 +56,6 @@ public @interface FilterScope {
// Contains all flags
int ALL = 0xFFFFFFFF;
@SuppressWarnings("PointlessBitwiseExpression")
int DEFAULT = ALL & ~FLAG_MATCH_NAME;
}

View File

@ -345,6 +345,9 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
@CursorField(value = Statuses.FILTER_TEXTS)
public String filter_texts;
@CursorField(value = Statuses.FILTER_DESCRIPTIONS)
public String filter_descriptions;
public transient boolean is_pinned_status;
public transient boolean is_filtered;

View File

@ -736,6 +736,7 @@ public interface TwidereDataStore {
String FILTER_SOURCES = "filter_sources";
String FILTER_NAMES = "filter_names";
String FILTER_TEXTS = "filter_texts";
String FILTER_DESCRIPTIONS = "filter_descriptions";
String DEFAULT_SORT_ORDER = TIMESTAMP + " DESC, " + SORT_ID + " DESC, " + ID
+ " DESC";

View File

@ -224,7 +224,6 @@ dependencies {
implementation "com.github.bumptech.glide:okhttp3-integration:${libVersions['GlideOkHttp3']}@aar"
implementation "jp.wasabeef:glide-transformations:${libVersions['GlideTransformations']}"
implementation "com.theartofdev.edmodo:android-image-cropper:${libVersions['AndroidImageCropper']}"
implementation "com.github.aakira:expandable-layout:${libVersions['ExpandableLayout']}@aar"
implementation "com.github.mariotaku.MediaViewerLibrary:base:${libVersions['MediaViewerLibrary']}"
implementation "com.github.mariotaku.MediaViewerLibrary:subsample-image-view:${libVersions['MediaViewerLibrary']}"

View File

@ -1 +1 @@
2d1412bd3a0e5eb7aa9c556ad373f836ac9f67ba
3dc89e56116e8ce97933fcc36164cd73f7901ad8

View File

@ -30,7 +30,7 @@ import org.mariotaku.twidere.model.UserKey;
public interface Constants extends TwidereConstants {
String DATABASES_NAME = "twidere.sqlite";
int DATABASES_VERSION = 187;
int DATABASES_VERSION = 188;
int EXTRA_FEATURES_NOTICE_VERSION = 2;

View File

@ -26,14 +26,28 @@ import android.net.Uri
import org.mariotaku.ktextension.map
import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.sqliteqb.library.SQLFunctions
import org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_LIMIT
import org.mariotaku.twidere.extension.model.component1
import org.mariotaku.twidere.model.CursorReference
import org.mariotaku.twidere.util.TwidereQueryBuilder
import org.mariotaku.twidere.util.content.ContentResolverUtils
fun ContentResolver.query(uri: Uri, projection: Array<String>? = null,
selection: String? = null, selectionArgs: Array<String>? = null, sortOrder: String? = null,
limit: String? = null): Cursor {
return if (limit != null) {
query(uri.buildUpon().appendQueryParameter(QUERY_PARAM_LIMIT, limit.toString()).build(),
projection, selection, selectionArgs, sortOrder)
} else {
query(uri, projection, selection, selectionArgs, sortOrder)
}
}
fun ContentResolver.queryReference(uri: Uri, projection: Array<String>? = null,
selection: String? = null, selectionArgs: Array<String>? = null, sortOrder: String? = null) =
CursorReference(query(uri, projection, selection, selectionArgs, sortOrder))
selection: String? = null, selectionArgs: Array<String>? = null, sortOrder: String? = null,
limit: String? = null): CursorReference<Cursor> {
return CursorReference(query(uri, projection, selection, selectionArgs, sortOrder, limit))
}
@SuppressLint("Recycle")
fun ContentResolver.rawQuery(sql: String, selectionArgs: Array<String>?, notifyUri: Uri? = null): Cursor? {
@ -41,9 +55,10 @@ fun ContentResolver.rawQuery(sql: String, selectionArgs: Array<String>?, notifyU
return query(rawUri, null, null, selectionArgs, null)
}
fun <T> ContentResolver.queryOne(uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String? = null, cls: Class<T>): T? {
return queryReference(uri, projection, selection, selectionArgs, sortOrder).use { (cur) ->
return queryReference(uri, projection, selection, selectionArgs, sortOrder, "1").use { (cur) ->
if (!cur.moveToFirst()) return@use null
val indices = ObjectCursor.indicesFrom(cur, cls)
return@use indices.newObject(cur)
@ -67,6 +82,16 @@ fun ContentResolver.queryCount(uri: Uri, selection: String?, selectionArgs: Arra
}
}
fun ContentResolver.queryLong(uri: Uri, field: String, selection: String?, selectionArgs: Array<String>?, def: Long = -1): Long {
val projection = arrayOf(field)
return queryReference(uri, projection, selection, selectionArgs, null, "1").use { (cur) ->
if (cur.moveToFirst()) {
return@use cur.getLong(0)
}
return@use def
}
}
fun <T : Any> ContentResolver.insert(uri: Uri, obj: T, cls: Class<T> = obj.javaClass): Uri? {
return this.insert(uri, ObjectCursor.valuesCreatorFrom(cls).create(obj))
}

View File

@ -30,10 +30,11 @@ val ParcelableActivity.activityStatus: ParcelableActivity?
else -> null
}
val ParcelableActivity.reachedCountLimit: Boolean get() {
return sources.reachedCountLimit() || targets.reachedCountLimit() ||
target_objects.reachedCountLimit()
}
val ParcelableActivity.reachedCountLimit: Boolean
get() {
return sources.reachedCountLimit() || targets.reachedCountLimit() ||
target_objects.reachedCountLimit()
}
fun ParcelableActivity.isSameSources(another: ParcelableActivity): Boolean {
return Arrays.equals(sources, another.sources)
@ -66,13 +67,20 @@ fun ParcelableActivity.prependTargetObjects(from: ParcelableActivity) {
.prepend(from.target_objects)
}
inline val ParcelableActivity.RelatedObject.size get() = when {
statuses != null -> statuses.size
users != null -> users.size
user_lists != null -> user_lists.size
else -> 0
fun ParcelableActivity.updateActivityFilterInfo() {
updateFilterInfo(sources?.flatMap {
listOf(it.description_unescaped, it.location)
}?.toTypedArray())
}
inline val ParcelableActivity.RelatedObject.size
get() = when {
statuses != null -> statuses.size
users != null -> users.size
user_lists != null -> user_lists.size
else -> 0
}
fun ParcelableActivity.RelatedObject?.isNullOrEmpty(): Boolean {
if (this == null) return true
return size == 0

View File

@ -127,7 +127,7 @@ fun ParcelableStatus.addFilterFlag(@ParcelableStatus.FilterFlags flags: Long) {
filter_flags = filter_flags or flags
}
fun ParcelableStatus.updateFilterInfo() {
fun ParcelableStatus.updateFilterInfo(descriptions: Array<String?>?) {
val links = mutableSetOf<String>()
spans?.mapNotNullTo(links) { span ->
if (span.type != SpanItem.SpanType.LINK) return@mapNotNullTo null
@ -166,6 +166,8 @@ fun ParcelableStatus.updateFilterInfo() {
item.alt_text?.appendNewLineTo(texts)
}
filter_texts = texts.toString()
filter_descriptions = descriptions?.filterNotNull()?.joinToString("\n")
}
fun ParcelableStatus.updateExtraInformation(details: AccountDetails) {

View File

@ -42,6 +42,7 @@ import org.mariotaku.twidere.text.AcctMentionSpan
import org.mariotaku.twidere.text.HashtagSpan
import org.mariotaku.twidere.util.HtmlBuilder
import org.mariotaku.twidere.util.HtmlSpanBuilder
import org.mariotaku.twidere.util.InternalTwitterContentUtils
import org.mariotaku.twidere.util.InternalTwitterContentUtils.getMediaUrl
import org.mariotaku.twidere.util.InternalTwitterContentUtils.getStartEndForEntity
@ -200,7 +201,9 @@ fun Status.applyTo(accountKey: UserKey, accountType: String, profileImageSize: S
result.addFilterFlag(ParcelableStatus.FilterFlags.HAS_MEDIA)
}
result.updateFilterInfo()
result.updateFilterInfo(arrayOf(userDescriptionUnescaped, retweetedStatus?.userDescriptionUnescaped,
quotedStatus?.userDescriptionUnescaped, this.user.location, retweetedStatus?.user?.location,
quotedStatus?.user?.location))
}
@ -268,6 +271,9 @@ private fun String.twitterUnescaped(): String {
return twitterRawTextTranslator.translate(this)
}
private val Status.userDescriptionUnescaped: String?
get() = user?.let { InternalTwitterContentUtils.formatUserDescription(it)?.first }
/**
* @param spans Ordered spans
* *
@ -279,35 +285,39 @@ internal fun findByOrigRange(spans: Array<SpanItem>, start: Int, end: Int): List
return spans.filter { it.orig_start >= start && it.orig_end <= end }
}
internal inline val CharSequence.spanItems get() = (this as? Spanned)?.let { text ->
text.getSpans(0, length, URLSpan::class.java).mapToArray {
val item = it.toSpanItem(text)
when (it) {
is AcctMentionSpan -> item.type = SpanItem.SpanType.ACCT_MENTION
is HashtagSpan -> item.type = SpanItem.SpanType.HASHTAG
internal inline val CharSequence.spanItems
get() = (this as? Spanned)?.let { text ->
text.getSpans(0, length, URLSpan::class.java).mapToArray {
val item = it.toSpanItem(text)
when (it) {
is AcctMentionSpan -> item.type = SpanItem.SpanType.ACCT_MENTION
is HashtagSpan -> item.type = SpanItem.SpanType.HASHTAG
}
return@mapToArray item
}
return@mapToArray item
}
}
internal inline val String.isHtml get() = contains('<') && contains('>')
private inline val Status.inReplyToName get() = userMentionEntities?.firstOrNull {
inReplyToUserId == it.id
}?.name ?: attentions?.firstOrNull {
inReplyToUserId == it.id
}?.fullName ?: inReplyToScreenName
private inline val Status.inReplyToName
get() = userMentionEntities?.firstOrNull {
inReplyToUserId == it.id
}?.name ?: attentions?.firstOrNull {
inReplyToUserId == it.id
}?.fullName ?: inReplyToScreenName
private inline val Status.placeFullName get() = place?.fullName ?: location?.takeIf {
ParcelableLocation.valueOf(location) == null
}
private inline val Status.inferredExternalUrl get() = externalUrl ?: uri?.let { uri ->
noticeUriRegex.matchEntire(uri)?.let { result: MatchResult ->
"https://${result.groups[1]?.value}/notice/${result.groups[3]?.value}"
private inline val Status.placeFullName
get() = place?.fullName ?: location?.takeIf {
ParcelableLocation.valueOf(location) == null
}
private inline val Status.inferredExternalUrl
get() = externalUrl ?: uri?.let { uri ->
noticeUriRegex.matchEntire(uri)?.let { result: MatchResult ->
"https://${result.groups[1]?.value}/notice/${result.groups[3]?.value}"
}
}
}
private val Status.parcelableLocation: ParcelableLocation?
get() {

View File

@ -25,6 +25,7 @@ import org.mariotaku.microblog.library.mastodon.model.Relationship
import org.mariotaku.microblog.library.twitter.model.Activity
import org.mariotaku.twidere.extension.model.toLite
import org.mariotaku.twidere.extension.model.toSummaryLine
import org.mariotaku.twidere.extension.model.updateActivityFilterInfo
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableActivity
import org.mariotaku.twidere.model.ParcelableUser
@ -92,6 +93,8 @@ fun Notification.toParcelable(accountKey: UserKey, relationships: Map<String, Re
result.sources_lite = result.sources?.mapToArray { it.toLite() }
result.source_keys = result.sources_lite?.mapToArray { it.key }
result.updateActivityFilterInfo()
return result
}

View File

@ -26,7 +26,9 @@ import org.mariotaku.ktextension.isNotNullOrEmpty
import org.mariotaku.ktextension.mapToArray
import org.mariotaku.microblog.library.mastodon.model.Status
import org.mariotaku.twidere.extension.model.addFilterFlag
import org.mariotaku.twidere.extension.model.api.isHtml
import org.mariotaku.twidere.extension.model.api.spanItems
import org.mariotaku.twidere.extension.model.updateFilterInfo
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.text.AcctMentionSpan
import org.mariotaku.twidere.text.HashtagSpan
@ -39,6 +41,7 @@ fun Status.toParcelable(details: AccountDetails): ParcelableStatus {
account_color = details.color
}
}
fun Status.toParcelable(accountKey: UserKey): ParcelableStatus {
val result = ParcelableStatus()
applyTo(accountKey, result)
@ -121,6 +124,8 @@ fun Status.applyTo(accountKey: UserKey, result: ParcelableStatus) {
}
result.extras = extras
result.updateFilterInfo(arrayOf(accountDescriptionUnescaped, reblog?.accountDescriptionUnescaped))
}
private fun calculateDisplayTextRange(text: String, spans: Array<SpanItem>?, media: Array<ParcelableMedia>?): IntArray? {
@ -130,6 +135,16 @@ private fun calculateDisplayTextRange(text: String, spans: Array<SpanItem>?, med
return intArrayOf(0, lastMatch.start)
}
private val Status.accountDescriptionUnescaped: String?
get() {
val note = account?.note ?: return null
return if (note.isHtml) {
HtmlSpanBuilder.fromHtml(note, note, MastodonSpanProcessor).toString()
} else {
HtmlEscapeHelper.unescape(note)
}
}
object MastodonSpanProcessor : HtmlSpanBuilder.SpanProcessor {
override fun appendText(text: Editable, buffer: CharArray, start: Int, len: Int,

View File

@ -27,7 +27,7 @@ import org.mariotaku.twidere.extension.model.api.applyTo
import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.extension.model.toLite
import org.mariotaku.twidere.extension.model.toSummaryLine
import org.mariotaku.twidere.extension.model.updateFilterInfo
import org.mariotaku.twidere.extension.model.updateActivityFilterInfo
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableActivity
import org.mariotaku.twidere.model.ParcelableUserList
@ -152,7 +152,7 @@ fun Activity.toParcelable(accountKey: UserKey, accountType: String, isGap: Boole
} ?: false
result.is_gap = isGap
result.updateFilterInfo()
result.updateActivityFilterInfo()
return result
}

View File

@ -44,6 +44,7 @@ import org.mariotaku.twidere.loader.ExtendedObjectCursorLoader
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.event.*
import org.mariotaku.twidere.model.pagination.SinceMaxPagination
import org.mariotaku.twidere.provider.TwidereDataStore
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
import org.mariotaku.twidere.provider.TwidereDataStore.Filters
import org.mariotaku.twidere.task.twitter.GetStatusesTask
@ -354,7 +355,8 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
val activityColumnsLite = Activities.COLUMNS - arrayOf(Activities.SOURCES, Activities.TARGETS,
Activities.TARGET_OBJECTS, Activities.MENTIONS_JSON, Activities.CARD,
Activities.FILTER_FLAGS, Activities.FILTER_USERS, Activities.FILTER_LINKS,
Activities.FILTER_SOURCES, Activities.FILTER_NAMES, Activities.FILTER_TEXTS)
Activities.FILTER_SOURCES, Activities.FILTER_NAMES, Activities.FILTER_TEXTS,
Activities.FILTER_DESCRIPTIONS)
}
}

View File

@ -319,6 +319,7 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() {
companion object {
private val statusColumnsLite = Statuses.COLUMNS - arrayOf(Statuses.MENTIONS_JSON,
Statuses.CARD, Statuses.FILTER_FLAGS, Statuses.FILTER_USERS, Statuses.FILTER_LINKS,
Statuses.FILTER_SOURCES, Statuses.FILTER_NAMES, Statuses.FILTER_TEXTS)
Statuses.FILTER_SOURCES, Statuses.FILTER_NAMES, Statuses.FILTER_TEXTS,
Statuses.FILTER_DESCRIPTIONS)
}
}

View File

@ -28,6 +28,7 @@ import android.os.Parcel
import android.os.Parcelable
import android.support.v7.app.AlertDialog
import android.view.View
import android.view.WindowManager
import android.widget.CheckBox
import android.widget.Toast
import kotlinx.android.synthetic.main.dialog_filter_rule_editor.*
@ -43,7 +44,8 @@ import org.mariotaku.twidere.annotation.FilterScope
import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.extension.applyOnShow
import org.mariotaku.twidere.extension.applyTheme
import org.mariotaku.twidere.extension.queryCount
import org.mariotaku.twidere.extension.queryLong
import org.mariotaku.twidere.extension.setVisible
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Filters
@ -60,7 +62,7 @@ class AddEditItemFragment : BaseDialogFragment(), DialogInterface.OnClickListene
get() = arguments.getString(EXTRA_VALUE)
private val defaultScope: FilterScopes
get() = FilterScopes(filterMasks, arguments.getInt(EXTRA_SCOPE, filterMasks))
get() = FilterScopes(filterMasks, arguments.getInt(EXTRA_SCOPE, FilterScope.DEFAULT))
private val filterMasks: Int
get() = when (contentUri) {
@ -78,11 +80,18 @@ class AddEditItemFragment : BaseDialogFragment(), DialogInterface.OnClickListene
}
private var Dialog.scope: FilterScopes?
get() = defaultScope.also { applyScopes(it) }
get() = defaultScope.also { saveScopes(it) }
set(value) {
loadScopes(value ?: defaultScope)
}
private var Dialog.advancedExpanded: Boolean
get() = advancedContainer.visibility == View.VISIBLE
set(value) {
advancedContainer.setVisible(value)
advancedCollapseIndicator.rotation = if (value) 90f else 0f
}
override fun onClick(dialog: DialogInterface, which: Int) {
dialog as AlertDialog
when (which) {
@ -100,12 +109,14 @@ class AddEditItemFragment : BaseDialogFragment(), DialogInterface.OnClickListene
if (id >= 0) {
val valueWhere = Expression.equalsArgs(Filters.VALUE).sql
val valueWhereArgs = arrayOf(value)
if (resolver.queryCount(uri, valueWhere, valueWhereArgs) == 0) {
val idWhere = Expression.equals(Filters._ID, id).sql
resolver.update(uri, values, idWhere, null)
} else {
val matchedId = resolver.queryLong(uri, Filters._ID, valueWhere, valueWhereArgs,
-1)
if (matchedId != -1L && matchedId != id) {
Toast.makeText(context, R.string.message_toast_duplicate_filter_rule,
Toast.LENGTH_SHORT).show()
} else {
val idWhere = Expression.equals(Filters._ID, id).sql
resolver.update(uri, values, idWhere, null)
}
} else {
resolver.insert(uri, values)
@ -129,6 +140,7 @@ class AddEditItemFragment : BaseDialogFragment(), DialogInterface.OnClickListene
val dialog = builder.create()
dialog.applyOnShow {
applyTheme()
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
editText.setAdapter(when (contentUri) {
Filters.Sources.CONTENT_URI -> SourceAutoCompleteAdapter(activity)
Filters.Users.CONTENT_URI -> ComposeAutoCompleteAdapter(activity, requestManager).apply {
@ -138,6 +150,10 @@ class AddEditItemFragment : BaseDialogFragment(), DialogInterface.OnClickListene
else -> null
})
editText.threshold = 1
advancedToggle.setOnClickListener {
advancedExpanded = !advancedExpanded
}
advancedExpanded = false
if (savedInstanceState == null) {
value = defaultValue
@ -156,19 +172,21 @@ class AddEditItemFragment : BaseDialogFragment(), DialogInterface.OnClickListene
outState.putParcelable(EXTRA_SCOPE, dialog.scope)
}
private fun Dialog.applyScopes(scopes: FilterScopes) {
targetText.applyScope(scopes, FilterScope.FLAG_MATCH_TEXT)
targetName.applyScope(scopes, FilterScope.FLAG_MATCH_NAME)
scopeHome.applyScope(scopes, FilterScope.HOME)
scopeInteractions.applyScope(scopes, FilterScope.INTERACTIONS)
scopeMessages.applyScope(scopes, FilterScope.MESSAGES)
scopeSearchResults.applyScope(scopes, FilterScope.SEARCH_RESULTS)
scopeOther.applyScope(scopes, FilterScope.UGC_TIMELINE)
private fun Dialog.saveScopes(scopes: FilterScopes) {
targetText.saveScope(scopes, FilterScope.FLAG_MATCH_TEXT)
targetName.saveScope(scopes, FilterScope.FLAG_MATCH_NAME)
targetDescription.saveScope(scopes, FilterScope.FLAG_MATCH_DESCRIPTION)
scopeHome.saveScope(scopes, FilterScope.HOME)
scopeInteractions.saveScope(scopes, FilterScope.INTERACTIONS)
scopeMessages.saveScope(scopes, FilterScope.MESSAGES)
scopeSearchResults.saveScope(scopes, FilterScope.SEARCH_RESULTS)
scopeOther.saveScope(scopes, FilterScope.UGC_TIMELINE)
}
private fun Dialog.loadScopes(scopes: FilterScopes) {
targetText.loadScope(scopes, FilterScope.FLAG_MATCH_TEXT)
targetName.loadScope(scopes, FilterScope.FLAG_MATCH_NAME)
targetDescription.loadScope(scopes, FilterScope.FLAG_MATCH_DESCRIPTION)
scopeHome.loadScope(scopes, FilterScope.HOME)
scopeInteractions.loadScope(scopes, FilterScope.INTERACTIONS)
scopeMessages.loadScope(scopes, FilterScope.MESSAGES)
@ -176,7 +194,7 @@ class AddEditItemFragment : BaseDialogFragment(), DialogInterface.OnClickListene
scopeOther.loadScope(scopes, FilterScope.UGC_TIMELINE)
}
private fun CheckBox.applyScope(scopes: FilterScopes, scope: Int) {
private fun CheckBox.saveScope(scopes: FilterScopes, scope: Int) {
if (!isEnabled || visibility != View.VISIBLE) return
scopes[scope] = isChecked
}

View File

@ -47,6 +47,7 @@ import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.iface.IControlBarActivity
import org.mariotaku.twidere.annotation.FilterScope
import org.mariotaku.twidere.extension.invertSelection
import org.mariotaku.twidere.extension.selectAll
import org.mariotaku.twidere.extension.selectNone
@ -222,7 +223,7 @@ abstract class BaseFiltersFragment : AbsContentListViewFragment<SimpleCursorAdap
context.contentResolver.delete(contentUri, where.sql, Array(ids.size) { ids[it].toString() })
}
protected open fun addOrEditItem(id: Long = -1, value: String? = null, scope: Int = 0) {
protected open fun addOrEditItem(id: Long = -1, value: String? = null, scope: Int = FilterScope.DEFAULT) {
val dialog = AddEditItemFragment()
dialog.arguments = Bundle {
this[EXTRA_URI] = contentUri

View File

@ -230,8 +230,9 @@ class TwidereDataProvider : ContentProvider(), LazyLoadCallback {
}
}
if (table == null) return null
val limit = uri.getQueryParameter(QUERY_PARAM_LIMIT)
val c = databaseWrapper.query(table, projection, selection, selectionArgs,
null, null, sortOrder)
null, null, sortOrder, limit)
c?.setNotificationUri(context.contentResolver, uri)
return c
} catch (e: SQLException) {

View File

@ -496,6 +496,11 @@ object DataStoreUtils {
Expression.notEquals("${Filters.Keywords.TABLE_NAME}.${Filters.Keywords.SCOPE} & ${FilterScope.FLAG_MATCH_NAME}", 0),
ScopeMatchesExpression(Filters.Keywords.TABLE_NAME, Filters.Keywords.SCOPE),
ContainsExpression(Statuses.FILTER_NAMES, Filters.Keywords.TABLE_NAME, Filters.Keywords.VALUE)
),
Expression.and(
Expression.notEquals("${Filters.Keywords.TABLE_NAME}.${Filters.Keywords.SCOPE} & ${FilterScope.FLAG_MATCH_DESCRIPTION}", 0),
ScopeMatchesExpression(Filters.Keywords.TABLE_NAME, Filters.Keywords.SCOPE),
ContainsExpression(Statuses.FILTER_DESCRIPTIONS, Filters.Keywords.TABLE_NAME, Filters.Keywords.VALUE)
)
)
val filteredLinksWhere = Expression.and(

View File

@ -17,76 +17,133 @@
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatAutoCompleteTextView
android:id="@+id/editText"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
android:maxLines="1"
app:backgroundTint="?colorAccent"/>
android:orientation="vertical"
android:padding="8dp">
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_filter_for"
android:textColor="?colorAccent"/>
<android.support.v7.widget.AppCompatAutoCompleteTextView
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
android:maxLines="1"
app:backgroundTint="?colorAccent"/>
<CheckBox
android:id="@+id/targetText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_filter_target_text"/>
<LinearLayout
android:id="@+id/advancedToggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/element_spacing_normal"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal">
<CheckBox
android:id="@+id/targetName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_filter_target_name"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/label_advanced"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"/>
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_filter_apply_to"
android:textColor="?colorAccent"/>
<android.support.v7.widget.AppCompatImageView
android:id="@+id/advancedCollapseIndicator"
android:layout_width="@dimen/element_size_small"
android:layout_height="@dimen/element_size_small"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_indicator_arrow_next"
app:tint="?android:textColorSecondary"/>
</LinearLayout>
<CheckBox
android:id="@+id/scopeHome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_filter_scope_home"/>
<LinearLayout
android:id="@+id/advancedContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_normal"
android:orientation="vertical">
<CheckBox
android:id="@+id/scopeInteractions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_filter_scope_interactions"/>
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_small"
android:text="@string/label_filter_for"
android:textAllCaps="true"
android:textColor="?colorAccent"/>
<CheckBox
android:id="@+id/scopeMessages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_filter_scope_messages"/>
<CheckBox
android:id="@+id/targetText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_small"
android:text="@string/label_filter_target_text"/>
<CheckBox
android:id="@+id/scopeSearchResults"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_filter_scope_search_results"/>
<CheckBox
android:id="@+id/targetName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_small"
android:text="@string/label_filter_target_name"/>
<CheckBox
android:id="@+id/scopeOther"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_filter_scope_search_other"/>
</LinearLayout>
<CheckBox
android:id="@+id/targetDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_small"
android:text="@string/label_filter_target_description"/>
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_small"
android:text="@string/label_filter_apply_to"
android:textAllCaps="true"
android:textColor="?colorAccent"/>
<CheckBox
android:id="@+id/scopeHome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_small"
android:text="@string/label_filter_scope_home"/>
<CheckBox
android:id="@+id/scopeInteractions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_small"
android:text="@string/label_filter_scope_interactions"/>
<CheckBox
android:id="@+id/scopeMessages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_small"
android:text="@string/label_filter_scope_messages"/>
<CheckBox
android:id="@+id/scopeSearchResults"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_small"
android:text="@string/label_filter_scope_search_results"/>
<CheckBox
android:id="@+id/scopeOther"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_small"
android:text="@string/label_filter_scope_search_other"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -500,6 +500,7 @@
<string name="label_account">Account</string>
<string name="label_account_type">Account type</string>
<string name="label_advanced">Advanced</string>
<string name="label_auth_type">Auth type</string>
<string name="label_background_operation_service">Background operation service</string>
<string name="label_buffer_accounts">Buffer accounts</string>
@ -523,6 +524,7 @@
<string name="label_filter_scope_messages">Messages</string>
<string name="label_filter_scope_search_other">Other</string>
<string name="label_filter_scope_search_results">Search results</string>
<string name="label_filter_target_description">Description</string>
<string name="label_filter_target_name">Name</string>
<string name="label_filter_target_text">Text</string>
<string name="label_filters_subscription">Subscription</string>