diff --git a/build.gradle b/build.gradle index 46b7100bf..ed237ae24 100644 --- a/build.gradle +++ b/build.gradle @@ -79,6 +79,7 @@ subprojects { ACRA : '4.9.2', AbstractTask : '0.9.5', Dagger : '2.11', + ExpandableLayout : '1.6.0', ] } 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 index da00cdee4..c637b5582 100644 --- 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 @@ -25,16 +25,21 @@ 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, FilterScope.FLAG_MATCH_NAME}, flag = true) + 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) @Retention(RetentionPolicy.SOURCE) public @interface FilterScope { int HOME = 0x1; int INTERACTIONS = 0x2; int MESSAGES = 0x4; - int SEARCH_RESULT = 0x8; + int SEARCH_RESULTS = 0x8; int LIST_GROUP_TIMELINE = 0x10; int FAVORITES = 0x20; + int USER_TIMELINE = 0x40; + int PUBLIC_TIMELINE = 0x80; + + int UGC_TIMELINE = LIST_GROUP_TIMELINE | FAVORITES | USER_TIMELINE | PUBLIC_TIMELINE; int FLAG_MATCH_NAME = 0x80000000; int FLAG_MATCH_TEXT = 0x40000000; @@ -42,6 +47,11 @@ public @interface FilterScope { int MASK_FLAG = 0xFF000000; int MASK_SCOPE = 0x00FFFFFF; + int VALID_MASKS_USERS = MASK_SCOPE; + int VALID_MASKS_KEYWORDS = MASK_SCOPE | MASK_FLAG; + int VALID_MASKS_SOURCES = MASK_SCOPE & ~MESSAGES; + int VALID_MASKS_LINKS = MASK_SCOPE; + // Contains all flags int ALL = 0xFFFFFFFF; } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java index 67c94dcbe..1e48e289a 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java @@ -105,6 +105,8 @@ public interface IntentConstants { String EXTRA_TEXT = "text"; String EXTRA_TITLE = "title"; String EXTRA_TYPE = "type"; + String EXTRA_VALUE = "value"; + String EXTRA_SCOPE = "scope"; // public static final String EXTRA_SUCCEED = "succeed"; String EXTRA_IDS = "ids"; String EXTRA_REFRESH_IDS = "refresh_ids"; diff --git a/twidere/build.gradle b/twidere/build.gradle index b04022312..fbd968f1b 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -224,6 +224,7 @@ 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']}" diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/util/ExtraFeaturesServiceExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/util/ExtraFeaturesServiceExtensions.kt new file mode 100644 index 000000000..55b3035ca --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/util/ExtraFeaturesServiceExtensions.kt @@ -0,0 +1,29 @@ +/* + * 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.extension.util + +import org.mariotaku.twidere.util.premium.ExtraFeaturesService +import org.mariotaku.twidere.util.premium.ExtraFeaturesService.Companion.FEATURE_ADVANCED_FILTERS + +/** + * Created by mariotaku on 2017/9/14. + */ +val ExtraFeaturesService.isAdvancedFiltersEnabled: Boolean + get() = isEnabled(FEATURE_ADVANCED_FILTERS) || isEnabled("filters_subscriptions") \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/AddEditItemFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/AddEditItemFragment.kt new file mode 100644 index 000000000..889757fd3 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/AddEditItemFragment.kt @@ -0,0 +1,247 @@ +/* + * 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.fragment.filter + +import android.accounts.AccountManager +import android.app.Dialog +import android.content.DialogInterface +import android.net.Uri +import android.os.Bundle +import android.os.Parcel +import android.os.Parcelable +import android.support.v7.app.AlertDialog +import android.view.View +import android.widget.CheckBox +import android.widget.Toast +import kotlinx.android.synthetic.main.dialog_filter_rule_editor.* +import org.mariotaku.ktextension.ContentValues +import org.mariotaku.ktextension.contains +import org.mariotaku.ktextension.set +import org.mariotaku.ktextension.string +import org.mariotaku.sqliteqb.library.Expression +import org.mariotaku.twidere.R +import org.mariotaku.twidere.adapter.ComposeAutoCompleteAdapter +import org.mariotaku.twidere.adapter.SourceAutoCompleteAdapter +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.fragment.BaseDialogFragment +import org.mariotaku.twidere.model.util.AccountUtils +import org.mariotaku.twidere.provider.TwidereDataStore.Filters + +class AddEditItemFragment : BaseDialogFragment(), DialogInterface.OnClickListener { + + private val contentUri: Uri + get() = arguments.getParcelable(EXTRA_URI) + + private val rowId: Long + get() = arguments.getLong(EXTRA_ID, -1) + + private val defaultValue: String? + get() = arguments.getString(EXTRA_VALUE) + + private val defaultScope: FilterScopes + get() = FilterScopes(filterMasks, arguments.getInt(EXTRA_SCOPE, filterMasks)) + + private val filterMasks: Int + get() = when (contentUri) { + Filters.Users.CONTENT_URI -> FilterScope.VALID_MASKS_USERS + Filters.Keywords.CONTENT_URI -> FilterScope.VALID_MASKS_KEYWORDS + Filters.Sources.CONTENT_URI -> FilterScope.VALID_MASKS_SOURCES + Filters.Links.CONTENT_URI -> FilterScope.VALID_MASKS_LINKS + else -> 0 + } + + private var Dialog.value: String? + get() = editText.string?.takeIf(String::isNotEmpty) + set(value) { + editText.string = value + } + + private var Dialog.scope: FilterScopes? + get() = defaultScope.also { applyScopes(it) } + set(value) { + loadScopes(value ?: defaultScope) + } + + override fun onClick(dialog: DialogInterface, which: Int) { + dialog as AlertDialog + when (which) { + DialogInterface.BUTTON_POSITIVE -> { + val value = dialog.value ?: return + val scope = dialog.scope ?: return + + val values = ContentValues { + this[Filters.VALUE] = value + this[Filters.SCOPE] = scope.value + } + val uri = contentUri + val id = rowId + val resolver = context.contentResolver + 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 { + Toast.makeText(context, R.string.message_toast_duplicate_filter_rule, + Toast.LENGTH_SHORT).show() + } + } else { + resolver.insert(uri, values) + } + } + } + + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val builder = AlertDialog.Builder(context) + builder.setView(R.layout.dialog_filter_rule_editor) + + if (arguments.getLong(EXTRA_ID, -1) >= 0) { + builder.setTitle(R.string.action_edit_filter_rule) + } else { + builder.setTitle(R.string.action_add_filter_rule) + } + builder.setPositiveButton(android.R.string.ok, this) + builder.setNegativeButton(android.R.string.cancel, this) + val dialog = builder.create() + dialog.applyOnShow { + applyTheme() + editText.setAdapter(when (contentUri) { + Filters.Sources.CONTENT_URI -> SourceAutoCompleteAdapter(activity) + Filters.Users.CONTENT_URI -> ComposeAutoCompleteAdapter(activity, requestManager).apply { + val am = AccountManager.get(activity) + account = AccountUtils.getDefaultAccountDetails(activity, am, false) + } + else -> null + }) + editText.threshold = 1 + + if (savedInstanceState == null) { + value = defaultValue + scope = defaultScope + } else { + value = savedInstanceState.getString(EXTRA_VALUE) + scope = savedInstanceState.getParcelable(EXTRA_SCOPE) + } + } + return dialog + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString(EXTRA_VALUE, dialog.value) + 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.loadScopes(scopes: FilterScopes) { + targetText.loadScope(scopes, FilterScope.FLAG_MATCH_TEXT) + targetName.loadScope(scopes, FilterScope.FLAG_MATCH_NAME) + scopeHome.loadScope(scopes, FilterScope.HOME) + scopeInteractions.loadScope(scopes, FilterScope.INTERACTIONS) + scopeMessages.loadScope(scopes, FilterScope.MESSAGES) + scopeSearchResults.loadScope(scopes, FilterScope.SEARCH_RESULTS) + scopeOther.loadScope(scopes, FilterScope.UGC_TIMELINE) + } + + private fun CheckBox.applyScope(scopes: FilterScopes, scope: Int) { + if (!isEnabled || visibility != View.VISIBLE) return + scopes[scope] = isChecked + } + + private fun CheckBox.loadScope(scopes: FilterScopes, scope: Int) { + if (scope in scopes) { + isEnabled = true + visibility = View.VISIBLE + } else { + isEnabled = false + visibility = View.GONE + return + } + isChecked = scopes[scope] + } + + class FilterScopes(val masks: Int, value: Int = 0) : Parcelable { + + var value: Int = 0 + get() = field and masks + private set(v) { + field = v and masks + } + + constructor(parcel: Parcel) : this(parcel.readInt(), parcel.readInt()) + + init { + this.value = value + } + + operator fun set(scope: Int, enabled: Boolean) { + value = if (enabled) { + value or scope + } else { + value and scope.inv() + } + } + + operator fun get(scope: Int): Boolean { + return scope in value + } + + operator fun contains(scope: Int): Boolean { + return scope in masks + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(masks) + parcel.writeInt(value) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): FilterScopes { + return FilterScopes(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + + } + +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/BaseFiltersFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/BaseFiltersFragment.kt index dabb2798d..99290604d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/BaseFiltersFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/BaseFiltersFragment.kt @@ -19,12 +19,7 @@ package org.mariotaku.twidere.fragment.filter -import android.accounts.AccountManager -import android.app.Dialog -import android.content.ContentValues import android.content.Context -import android.content.DialogInterface -import android.content.DialogInterface.OnClickListener import android.database.Cursor import android.graphics.PorterDuff import android.net.Uri @@ -35,36 +30,31 @@ import android.support.v4.content.CursorLoader import android.support.v4.content.Loader import android.support.v4.view.ViewCompat import android.support.v4.widget.SimpleCursorAdapter -import android.support.v7.app.AlertDialog import android.text.SpannableStringBuilder import android.text.Spanned -import android.text.TextUtils import android.view.* import android.widget.AbsListView import android.widget.AbsListView.MultiChoiceModeListener import android.widget.ListView import android.widget.TextView -import android.widget.Toast -import kotlinx.android.synthetic.main.dialog_auto_complete_textview.* import kotlinx.android.synthetic.main.fragment_content_listview.* +import org.mariotaku.ktextension.Bundle +import org.mariotaku.ktextension.set import org.mariotaku.ktextension.setGroupAvailability import org.mariotaku.library.objectcursor.ObjectCursor import org.mariotaku.sqliteqb.library.Columns import org.mariotaku.sqliteqb.library.Expression import org.mariotaku.twidere.R -import org.mariotaku.twidere.TwidereConstants.EXTRA_ID -import org.mariotaku.twidere.TwidereConstants.EXTRA_URI +import org.mariotaku.twidere.TwidereConstants.* import org.mariotaku.twidere.activity.iface.IControlBarActivity -import org.mariotaku.twidere.adapter.ComposeAutoCompleteAdapter -import org.mariotaku.twidere.adapter.SourceAutoCompleteAdapter -import org.mariotaku.twidere.extension.* +import org.mariotaku.twidere.extension.invertSelection +import org.mariotaku.twidere.extension.selectAll +import org.mariotaku.twidere.extension.selectNone +import org.mariotaku.twidere.extension.updateSelectionItems import org.mariotaku.twidere.fragment.AbsContentListViewFragment -import org.mariotaku.twidere.fragment.BaseDialogFragment import org.mariotaku.twidere.model.FiltersData -import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.provider.TwidereDataStore.Filters import org.mariotaku.twidere.text.style.EmojiSpan -import org.mariotaku.twidere.util.ParseUtils import org.mariotaku.twidere.util.ThemeUtils @@ -82,7 +72,6 @@ abstract class BaseFiltersFragment : AbsContentListViewFragment protected open val sortOrder: String? = "${Filters.SOURCE} >= 0" - protected open val autoCompleteType: Int = 0 protected open val supportsEdit: Boolean = true private val isQuickReturnEnabled: Boolean @@ -97,7 +86,7 @@ abstract class BaseFiltersFragment : AbsContentListViewFragment= 0) return@setOnItemClickListener - addOrEditItem(item.id, item.value) + addOrEditItem(item.id, item.value, item.scope) } listView.setMultiChoiceModeListener(this) loaderManager.initLoader(0, null, this) @@ -233,14 +222,14 @@ abstract class BaseFiltersFragment : AbsContentListViewFragment { - val text = text - if (TextUtils.isEmpty(text)) return - val values = ContentValues() - values.put(Filters.VALUE, text) - val uri: Uri = arguments.getParcelable(EXTRA_URI) - val id = arguments.getLong(EXTRA_ID, -1) - val resolver = context.contentResolver - if (id >= 0) { - val valueWhere = Expression.equalsArgs(Filters.VALUE).sql - val valueWhereArgs = arrayOf(text) - if (resolver.queryCount(uri, valueWhere, valueWhereArgs) == 0) { - val idWhere = Expression.equals(Filters._ID, id).sql - resolver.update(uri, values, idWhere, null) - } else { - Toast.makeText(context, R.string.message_toast_duplicate_filter_rule, - Toast.LENGTH_SHORT).show() - } - } else { - resolver.insert(uri, values) - } - } - } - - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(context) - builder.setView(R.layout.dialog_auto_complete_textview) - - if (arguments.getLong(EXTRA_ID, -1) >= 0) { - builder.setTitle(R.string.action_edit_filter_rule) - } else { - builder.setTitle(R.string.action_add_filter_rule) - } - builder.setPositiveButton(android.R.string.ok, this) - builder.setNegativeButton(android.R.string.cancel, this) - val dialog = builder.create() - dialog.onShow { - it.applyTheme() - val editText = it.editText - if (savedInstanceState == null) { - editText.setText(arguments.getString(EXTRA_VALUE)) - } - val autoCompleteType = arguments.getInt(EXTRA_AUTO_COMPLETE_TYPE, 0) - if (autoCompleteType != 0) { - val userAutoCompleteAdapter: SimpleCursorAdapter - if (autoCompleteType == AUTO_COMPLETE_TYPE_SOURCES) { - userAutoCompleteAdapter = SourceAutoCompleteAdapter(activity) - } else { - val adapter = ComposeAutoCompleteAdapter(activity, requestManager) - val am = AccountManager.get(activity) - adapter.account = AccountUtils.getDefaultAccountDetails(activity, am, false) - userAutoCompleteAdapter = adapter - } - editText.setAdapter(userAutoCompleteAdapter) - editText.threshold = 1 - } - } - return dialog - } - - private val text: String - get() { - val alertDialog = dialog as AlertDialog - return ParseUtils.parseString(alertDialog.editText.text) - } - - } - interface IFilterAdapter { fun isReadOnly(position: Int): Boolean } @@ -382,9 +297,6 @@ abstract class BaseFiltersFragment : AbsContentListViewFragment - if (!extraFeaturesService.isEnabled(ExtraFeaturesService.FEATURE_FILTERS_IMPORT)) { + if (!extraFeaturesService.isAdvancedFiltersEnabled) { ExtraFeaturesIntroductionDialogFragment.show(fragmentManager, - feature = ExtraFeaturesService.FEATURE_FILTERS_IMPORT, + feature = ExtraFeaturesService.FEATURE_ADVANCED_FILTERS, requestCode = REQUEST_PURCHASE_EXTRA_FEATURES) return@listener false } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredSourcesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredSourcesFragment.kt index 1b7504dba..2ff39f47d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredSourcesFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredSourcesFragment.kt @@ -9,6 +9,4 @@ class FilteredSourcesFragment : BaseFiltersFragment() { override val contentUri: Uri = Filters.Sources.CONTENT_URI - override val autoCompleteType: Int = AUTO_COMPLETE_TYPE_SOURCES - } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredUsersFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredUsersFragment.kt index 501563120..f8615f43d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredUsersFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredUsersFragment.kt @@ -161,7 +161,7 @@ class FilteredUsersFragment : BaseFiltersFragment() { return FilterUsersListAdapter(context) } - override fun addOrEditItem(id: Long, value: String?) { + override fun addOrEditItem(id: Long, value: String?, scope: Int) { // No-op } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FiltersSubscriptionsFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FiltersSubscriptionsFragment.kt index f8f61320a..24b69d276 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FiltersSubscriptionsFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FiltersSubscriptionsFragment.kt @@ -32,6 +32,7 @@ import org.mariotaku.twidere.extension.* import org.mariotaku.twidere.extension.model.getComponentLabel import org.mariotaku.twidere.extension.model.instantiateComponent import org.mariotaku.twidere.extension.model.setupUrl +import org.mariotaku.twidere.extension.util.isAdvancedFiltersEnabled import org.mariotaku.twidere.fragment.BaseDialogFragment import org.mariotaku.twidere.fragment.BaseFragment import org.mariotaku.twidere.fragment.ExtraFeaturesIntroductionDialogFragment @@ -78,9 +79,9 @@ class FiltersSubscriptionsFragment : BaseFragment(), LoaderManager.LoaderCallbac if (savedInstanceState == null) { when (arguments?.getString(EXTRA_ACTION)) { ACTION_ADD_URL_SUBSCRIPTION -> { - if (!extraFeaturesService.isEnabled(ExtraFeaturesService.FEATURE_FILTERS_SUBSCRIPTION)) { + if (!extraFeaturesService.isAdvancedFiltersEnabled) { val df = ExtraFeaturesIntroductionDialogFragment.create( - ExtraFeaturesService.FEATURE_FILTERS_SUBSCRIPTION) + ExtraFeaturesService.FEATURE_ADVANCED_FILTERS) df.setTargetFragment(this, REQUEST_ADD_URL_SUBSCRIPTION_PURCHASE) df.show(fragmentManager, ExtraFeaturesIntroductionDialogFragment.FRAGMENT_TAG) } else { @@ -88,9 +89,9 @@ class FiltersSubscriptionsFragment : BaseFragment(), LoaderManager.LoaderCallbac } } else -> { - if (!extraFeaturesService.isEnabled(ExtraFeaturesService.FEATURE_FILTERS_SUBSCRIPTION)) { + if (!extraFeaturesService.isAdvancedFiltersEnabled) { val df = ExtraFeaturesIntroductionDialogFragment.create( - ExtraFeaturesService.FEATURE_FILTERS_SUBSCRIPTION) + ExtraFeaturesService.FEATURE_ADVANCED_FILTERS) df.setTargetFragment(this, REQUEST_PURCHASE_EXTRA_FEATURES) df.show(fragmentManager, ExtraFeaturesIntroductionDialogFragment.FRAGMENT_TAG) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/premium/ExtraFeaturesService.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/premium/ExtraFeaturesService.kt index 9bf842693..47cf76a61 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/premium/ExtraFeaturesService.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/premium/ExtraFeaturesService.kt @@ -50,8 +50,7 @@ abstract class ExtraFeaturesService { companion object { const val FEATURE_FEATURES_PACK = "features_pack" - const val FEATURE_FILTERS_IMPORT = "import_filters" - const val FEATURE_FILTERS_SUBSCRIPTION = "filters_subscriptions" + const val FEATURE_ADVANCED_FILTERS = "import_filters" const val FEATURE_SYNC_DATA = "sync_data" const val FEATURE_SCHEDULE_STATUS = "schedule_status" const val FEATURE_SHARE_GIF = "share_gif" @@ -67,12 +66,10 @@ abstract class ExtraFeaturesService { fun getIntroduction(context: Context, feature: String): Introduction { return when (feature) { FEATURE_FEATURES_PACK -> Introduction(R.drawable.ic_action_infinity, "") - FEATURE_FILTERS_IMPORT -> Introduction(R.drawable.ic_action_speaker_muted, - context.getString(R.string.extra_feature_description_filters_import)) + FEATURE_ADVANCED_FILTERS -> Introduction(R.drawable.ic_action_speaker_muted, + context.getString(R.string.extra_feature_description_advanced_filters)) FEATURE_SYNC_DATA -> Introduction(R.drawable.ic_action_refresh, context.getString(R.string.extra_feature_description_sync_data)) - FEATURE_FILTERS_SUBSCRIPTION -> Introduction(R.drawable.ic_action_speaker_muted, - context.getString(R.string.extra_feature_description_filters_subscription)) FEATURE_SCHEDULE_STATUS -> Introduction(R.drawable.ic_action_time, context.getString(R.string.extra_feature_description_schedule_status)) FEATURE_SHARE_GIF -> Introduction(R.drawable.ic_action_gif, diff --git a/twidere/src/main/res-localized/values-ast/strings.xml b/twidere/src/main/res-localized/values-ast/strings.xml index 85c41a248..e5b30778f 100644 --- a/twidere/src/main/res-localized/values-ast/strings.xml +++ b/twidere/src/main/res-localized/values-ast/strings.xml @@ -296,12 +296,12 @@ Grupu esternu en %s Usuariu esternu en %s Configuraciones estra - Importa\'l llistáu de peñeres de bloqueos/silenciaos + Importa\'l llistáu de peñeres de bloqueos/silenciaos Soscríbite a peñeres de silenciu y sincronízales automáticamentes Planificar tuit (unviar más sero) Compartir GIF en tuit Sincroniza datos con Dropbox, Google Drive… etc - Importación de peñeres + Importación de peñeres Soscripción de peñeres Sofita Twidere pa consiguir carauterístiques ameyoraes diff --git a/twidere/src/main/res-localized/values-de/strings.xml b/twidere/src/main/res-localized/values-de/strings.xml index 2c096df9a..4587f58e9 100644 --- a/twidere/src/main/res-localized/values-de/strings.xml +++ b/twidere/src/main/res-localized/values-de/strings.xml @@ -301,12 +301,12 @@ Externe Gruppe bei %s Externer Benutzer bei %s Erweiterte Einstellungen - Importiere Filterliste von blockierten/stummgeschalteten + Importiere Filterliste von blockierten/stummgeschalteten Abonniere Stummschaltfilter und Synchronisation automatisch Tweet planen (später senden) GIF in Tweet teilen Synchronisieren von Daten mit Dropbox, Google Drive etc. - Filterimport + Filterimport Filterabonnement Unterstütze Twidere und erhalte erweiterte Funktionen diff --git a/twidere/src/main/res-localized/values-es/strings.xml b/twidere/src/main/res-localized/values-es/strings.xml index 17f47da34..e7bfbd322 100644 --- a/twidere/src/main/res-localized/values-es/strings.xml +++ b/twidere/src/main/res-localized/values-es/strings.xml @@ -301,12 +301,12 @@ Grupo externo en %s Usuario externo en %s Configuraciones Extra - Importar lista de filtros de bloqueados/silenciados + Importar lista de filtros de bloqueados/silenciados Suscripción a filtros y sincronización automática Planifica tu tweet (envíalo más tarde) Compartir GIF en tweet Sincronizar datos con Dropbox, Google Drive etc - Importar filtros + Importar filtros Suscripción a filtros Apoyar Twidere y obtener características mejoradas diff --git a/twidere/src/main/res-localized/values-fa/strings.xml b/twidere/src/main/res-localized/values-fa/strings.xml index 2cc364230..3b4fc179c 100644 --- a/twidere/src/main/res-localized/values-fa/strings.xml +++ b/twidere/src/main/res-localized/values-fa/strings.xml @@ -297,12 +297,12 @@ گروه خارجی در %s کاربر خارجی در %s پیکربندی‌های اضافی - درون‌ریزی فهرست‌های پالایه از انسداد/خموشی‌ها + درون‌ریزی فهرست‌های پالایه از انسداد/خموشی‌ها اشتراک در پالایه‌های خموشی و هم‌گام سازی خودکار زمان‌بندی توییت (ارسال بعداً) هم رسانی جیف در توییت هم‌گام سازی داده با دراپ‌باکس، گوگل‌درایو و… - درون‌ریزی پالایه‌ها + درون‌ریزی پالایه‌ها اشتراک پالایه‌ها حمایت از توییدر و گرفتن ویژگی‌های بهبودیافته diff --git a/twidere/src/main/res-localized/values-fr/strings.xml b/twidere/src/main/res-localized/values-fr/strings.xml index 78b5ccb4f..ee8320ab4 100644 --- a/twidere/src/main/res-localized/values-fr/strings.xml +++ b/twidere/src/main/res-localized/values-fr/strings.xml @@ -301,12 +301,12 @@ Groupe externe venant de %s Utilisateur externe venant de %s Configurations supplémentaires - Importer la liste de filtres depuis les blocages et \"mutes\" + Importer la liste de filtres depuis les blocages et \"mutes\" S\'abonner aux filtres de mise en sourdine et les synchroniser automatiquement Tweet programmé (envoyé plus tard) Partager un GIF dans le tweet Synchronisation des données avec Dropbox, Google Drive, etc - Importer des filtres + Importer des filtres Abonnement à des filtres Soutenez Twidere et accédez à des fonctionnalités avancées diff --git a/twidere/src/main/res-localized/values-gl/strings.xml b/twidere/src/main/res-localized/values-gl/strings.xml index ddff85602..8ca444c59 100644 --- a/twidere/src/main/res-localized/values-gl/strings.xml +++ b/twidere/src/main/res-localized/values-gl/strings.xml @@ -308,12 +308,12 @@ Grupo externo en %s Usuario externo en %s Axustes extra - Importar filtro de lista de bloqueados/silenciados + Importar filtro de lista de bloqueados/silenciados Subscribir a filtros silenciados e sincronizar automaticamente Programar chío (enviar máis tarde) Compartir GIF nun chío Sincroniza datos con Dropbox, Google Drive, etc - Importación de filtros + Importación de filtros Subscrición de filtros Apoia Twidere e consegue características avanzadas diff --git a/twidere/src/main/res-localized/values-hu/strings.xml b/twidere/src/main/res-localized/values-hu/strings.xml index 8925ed68e..4da590922 100644 --- a/twidere/src/main/res-localized/values-hu/strings.xml +++ b/twidere/src/main/res-localized/values-hu/strings.xml @@ -305,12 +305,12 @@ Külső csoport innen: %s Külső felhasználó innen: %s Egyéb beállítások - Blokkolt/elnémított szűrők importálása + Blokkolt/elnémított szűrők importálása Feliratkozás elnémító szűrőkre és automatikus szinkronizációra Tweet ütemezése (küldés később) GIF megosztása tweetben Szinkronizálás a Dropbox, Google Drive stb. szolgáltatásokkal - Szűrők importálása + Szűrők importálása Feliratkozás szűrőkre Támogassa Twidere alkalmazást, és jusson hozzá a bővített funkciókhoz diff --git a/twidere/src/main/res-localized/values-in/strings.xml b/twidere/src/main/res-localized/values-in/strings.xml index 6145bf5bd..7f6ef0918 100644 --- a/twidere/src/main/res-localized/values-in/strings.xml +++ b/twidere/src/main/res-localized/values-in/strings.xml @@ -269,7 +269,7 @@ Grup eksternal di %s Pengguna eksternal di %s Pengaturan Ekstra - Mengimpor daftar penyaringan dari blok/didiamkan + Mengimpor daftar penyaringan dari blok/didiamkan Jadwalkan tweer (kirim nanti) Sinkronisasi data dengan Dropbox, Google Drive, dll diff --git a/twidere/src/main/res-localized/values-ja/strings.xml b/twidere/src/main/res-localized/values-ja/strings.xml index e071b07a6..bb859af47 100644 --- a/twidere/src/main/res-localized/values-ja/strings.xml +++ b/twidere/src/main/res-localized/values-ja/strings.xml @@ -305,12 +305,12 @@ %s の外部グループ %sの外部ユーザー 追加設定 - ブロック/ミュート 一覧からフィルターリストをインポート + ブロック/ミュート 一覧からフィルターリストをインポート ミュートフィルターを購読、かつ自動同期 予約ツイート (あとで送る) ツイートでGIF動画を共有 データを Dropbox や Google ドライブなどで同期 - フィルターをインポート + フィルターをインポート フィルターを購読 Twidere を支援して追加機能を取得 diff --git a/twidere/src/main/res-localized/values-ko/strings.xml b/twidere/src/main/res-localized/values-ko/strings.xml index 70ee423e4..e8a3ab012 100644 --- a/twidere/src/main/res-localized/values-ko/strings.xml +++ b/twidere/src/main/res-localized/values-ko/strings.xml @@ -300,12 +300,12 @@ %s의 외부 그룹 %s의 외부 사용자 추가 설정 - 블락/뮤트에서 필터 리스트 가져오기 + 블락/뮤트에서 필터 리스트 가져오기 뮤트 필터를 구독하고 자동으로 동기화 트윗 예약 (나중에 보내기) 트윗에 GIF 공유 데이터를 Dropbox, Google Drive 등에 동기화하기 - 필터 가져오기 + 필터 가져오기 필터 구독 Twidere를 지원하시고 확장 기능을 받으세요. diff --git a/twidere/src/main/res-localized/values-pt/strings.xml b/twidere/src/main/res-localized/values-pt/strings.xml index 6e9bf4d1f..591ef7314 100644 --- a/twidere/src/main/res-localized/values-pt/strings.xml +++ b/twidere/src/main/res-localized/values-pt/strings.xml @@ -270,7 +270,7 @@ Inscreva-se para silenciar os filtros e sincronizar automaticamente Agendar Tweet(enviar depois) Dados de sincronização com o Dropbox, Google Drive etc - Importar Filtros + Importar Filtros Assinatura de filtros Suporte Twidere e obter recursos avançados diff --git a/twidere/src/main/res-localized/values-ru/strings.xml b/twidere/src/main/res-localized/values-ru/strings.xml index cb7b6d31b..8515bdd3e 100644 --- a/twidere/src/main/res-localized/values-ru/strings.xml +++ b/twidere/src/main/res-localized/values-ru/strings.xml @@ -274,7 +274,7 @@ Внешняя группа на %s Внешний пользователь на %s Дополнительные настройки - Импортировать список блокированных/заглушенных + Импортировать список блокированных/заглушенных Расписание твитов Синхронизация с Dropbox, Google Drive и др. diff --git a/twidere/src/main/res-localized/values-sv/strings.xml b/twidere/src/main/res-localized/values-sv/strings.xml index 7370cb2f7..526b6ec00 100644 --- a/twidere/src/main/res-localized/values-sv/strings.xml +++ b/twidere/src/main/res-localized/values-sv/strings.xml @@ -292,12 +292,12 @@ Extern grupp på %s Extern användare på %s Extra inställningar - Importera filterlista från blockeringar/tystningar + Importera filterlista från blockeringar/tystningar Prenumerera på mute-filter och synkronisera automatiskt Schemalägg tweet (Skicka senare) Dela GIF i tweet Synkronisera data med exempelvis Dropbox eller Google Drive - Import-filter + Import-filter Prenumerations-filter Stöd Twidere och få extrafunktioner diff --git a/twidere/src/main/res-localized/values-th/strings.xml b/twidere/src/main/res-localized/values-th/strings.xml index 1d08285b0..23d6f0978 100644 --- a/twidere/src/main/res-localized/values-th/strings.xml +++ b/twidere/src/main/res-localized/values-th/strings.xml @@ -301,12 +301,12 @@ กลุ่มภายนอกที่ %s ผู้ใช้ภายนอกที่ %s การตั้งค่าเพิ่มเติม - นำเข้ารายการตัวกรองจากบล็อก/ปิดเสียง + นำเข้ารายการตัวกรองจากบล็อก/ปิดเสียง สมัครสมาชิกเพื่อปิดการกรอง และซิงค์โดยอัตโนมัติ ตั้งเวลาทวีต (ส่งในภายหลัง) แชร์ GIF ในทวีต ซิงค์ข้อมูลกับ Dropbox, Google ไดรฟ์ ฯลฯ - นำเข้าตัวกรอง + นำเข้าตัวกรอง การใช้งานตัวกรอง สนับสนุน Twidere และรับคุณลักษณะขั้นสูง diff --git a/twidere/src/main/res-localized/values-zh-rCN/strings.xml b/twidere/src/main/res-localized/values-zh-rCN/strings.xml index 1f17940b7..9906d1839 100644 --- a/twidere/src/main/res-localized/values-zh-rCN/strings.xml +++ b/twidere/src/main/res-localized/values-zh-rCN/strings.xml @@ -312,12 +312,12 @@ %s 的外部群组 %s 的外部用户 额外设置 - 从被隐藏/阻止的用户导入过滤列表 + 从被隐藏/阻止的用户导入过滤列表 订阅到过滤器,并且自动同步 推文排期(稍后发送) 在推文中分享 GIF 与 Dropbox、Google 云端硬盘等同步数据 - 过滤器导入 + 过滤器导入 过滤器订阅 支持 Twidere 并获得增强功能 diff --git a/twidere/src/main/res-localized/values-zh-rHK/strings.xml b/twidere/src/main/res-localized/values-zh-rHK/strings.xml index 4f9e1add0..fbe8c6389 100644 --- a/twidere/src/main/res-localized/values-zh-rHK/strings.xml +++ b/twidere/src/main/res-localized/values-zh-rHK/strings.xml @@ -313,12 +313,12 @@ %s 的外部群組 %s 的外部使用者 額外設定 - 從被封鎖/靜音的使用者導入過濾列表 + 從被封鎖/靜音的使用者導入過濾列表 訂閱到過濾器列表,並且自動同步 定期發推 (稍候傳送) 在推文中分享 GIF 透過 Dropbox 或 Google Drive 等同步資料 - 過濾器匯入 + 過濾器匯入 過濾器訂閱 支持 Twidere 並且得到強化功能 diff --git a/twidere/src/main/res-localized/values-zh/strings.xml b/twidere/src/main/res-localized/values-zh/strings.xml index 4f03bdf66..f08facbf1 100644 --- a/twidere/src/main/res-localized/values-zh/strings.xml +++ b/twidere/src/main/res-localized/values-zh/strings.xml @@ -313,12 +313,12 @@ %s 的外部群組 %s 的外部使用者 額外設定 - 從被封鎖/靜音的使用者導入過濾列表 + 從被封鎖/靜音的使用者導入過濾列表 訂閱到過濾器列表,並且自動同步 定期發推 (稍候傳送) 在推文中分享 GIF 透過 Dropbox 或 Google Drive 等同步資料 - 過濾器匯入 + 過濾器匯入 過濾器訂閱 支持 Twidere 並且得到強化功能 diff --git a/twidere/src/main/res/layout/dialog_filter_rule_editor.xml b/twidere/src/main/res/layout/dialog_filter_rule_editor.xml new file mode 100644 index 000000000..b09e84f7d --- /dev/null +++ b/twidere/src/main/res/layout/dialog_filter_rule_editor.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/values/strings.xml b/twidere/src/main/res/values/strings.xml index 81a0b1c73..a917cc18d 100644 --- a/twidere/src/main/res/values/strings.xml +++ b/twidere/src/main/res/values/strings.xml @@ -376,13 +376,11 @@ External user at %s Extra configurations - Import filter list from blocks/mutes - Subscribe to mute filters and sync automatically + Filter subscription, advanced rules etc Schedule tweet (send later) Share GIF in tweet Sync data with Dropbox, Google Drive etc - Filters import - Filters subscription + Advanced filters Support Twidere and get enhanced features Or buy features pack to get all features (including features in future releases) @@ -518,6 +516,15 @@ Twidere database provider Content Title + Apply to… + For… + Home + Interactions + Messages + Other + Search results + Name + Text Subscription Follow request sent %1$s via %2$s