parent
8bf21db632
commit
74599d8a7f
|
@ -147,6 +147,7 @@ import app.fedilab.android.ui.fragment.timeline.FragmentMastodonConversation;
|
|||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentNotificationContainer;
|
||||
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||
import app.fedilab.android.viewmodel.mastodon.FiltersVM;
|
||||
import app.fedilab.android.viewmodel.mastodon.InstancesVM;
|
||||
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
|
||||
import app.fedilab.android.viewmodel.mastodon.TopBarVM;
|
||||
|
@ -691,7 +692,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
editor.apply();
|
||||
});
|
||||
//Retrieve filters
|
||||
new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class).getFilters(currentInstance, currentToken)
|
||||
new ViewModelProvider(BaseMainActivity.this).get(FiltersVM.class).getFilters(currentInstance, currentToken)
|
||||
.observe(BaseMainActivity.this, filters -> mainFilters = filters);
|
||||
new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class).getConnectedAccount(currentInstance, currentToken)
|
||||
.observe(BaseMainActivity.this, mastodonAccount -> {
|
||||
|
|
|
@ -28,6 +28,8 @@ import android.widget.Button;
|
|||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.AppCompatCheckBox;
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
@ -35,6 +37,7 @@ import androidx.lifecycle.ViewModelStoreOwner;
|
|||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -42,11 +45,12 @@ import app.fedilab.android.BaseMainActivity;
|
|||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.client.entities.api.Filter;
|
||||
import app.fedilab.android.databinding.ActivityFiltersBinding;
|
||||
import app.fedilab.android.databinding.KeywordsLayoutBinding;
|
||||
import app.fedilab.android.databinding.PopupAddFilterBinding;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.helper.ThemeHelper;
|
||||
import app.fedilab.android.ui.drawer.FilterAdapter;
|
||||
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||
import app.fedilab.android.viewmodel.mastodon.FiltersVM;
|
||||
|
||||
public class FilterActivity extends BaseActivity implements FilterAdapter.Delete {
|
||||
|
||||
|
@ -64,7 +68,7 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
|
|||
public static void addEditFilter(Context context, Filter filter, FilterAdapter.FilterAction listener) {
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context, Helper.dialogStyle());
|
||||
PopupAddFilterBinding popupAddFilterBinding = PopupAddFilterBinding.inflate(LayoutInflater.from(context));
|
||||
AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AccountsVM.class);
|
||||
FiltersVM filtersVM = new ViewModelProvider((ViewModelStoreOwner) context).get(FiltersVM.class);
|
||||
dialogBuilder.setView(popupAddFilterBinding.getRoot());
|
||||
ArrayAdapter<CharSequence> adapterResize = ArrayAdapter.createFromResource(Objects.requireNonNull(context),
|
||||
R.array.filter_expire, android.R.layout.simple_spinner_dropdown_item);
|
||||
|
@ -103,8 +107,14 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
|
|||
}
|
||||
});
|
||||
|
||||
popupAddFilterBinding.addKeyword.setOnClickListener(v -> {
|
||||
KeywordsLayoutBinding keywordsLayoutBinding = KeywordsLayoutBinding.inflate(LayoutInflater.from(context));
|
||||
keywordsLayoutBinding.deleteKeyword.setOnClickListener(v2 -> popupAddFilterBinding.keywordsContainer.removeView(keywordsLayoutBinding.deleteKeyword));
|
||||
popupAddFilterBinding.keywordsContainer.addView(keywordsLayoutBinding.getRoot());
|
||||
});
|
||||
|
||||
if (filter != null) {
|
||||
popupAddFilterBinding.addPhrase.setText(filter.phrase);
|
||||
popupAddFilterBinding.addTitle.setText(filter.title);
|
||||
if (filter.context != null)
|
||||
for (String val : filter.context) {
|
||||
switch (val) {
|
||||
|
@ -125,14 +135,20 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
|
|||
break;
|
||||
}
|
||||
}
|
||||
popupAddFilterBinding.contextWholeWord.setChecked(filter.whole_word);
|
||||
if (filter.irreversible) {
|
||||
popupAddFilterBinding.actionRemove.setChecked(true);
|
||||
popupAddFilterBinding.actionHide.setChecked(false);
|
||||
} else {
|
||||
popupAddFilterBinding.actionRemove.setChecked(false);
|
||||
popupAddFilterBinding.actionHide.setChecked(true);
|
||||
if (filter.keywords != null && filter.keywords.size() > 0) {
|
||||
for (Filter.FilterKeyword filterKeyword : filter.keywords) {
|
||||
KeywordsLayoutBinding keywordsLayoutBinding = KeywordsLayoutBinding.inflate(LayoutInflater.from(context));
|
||||
keywordsLayoutBinding.keywordPhrase.setText(filterKeyword.keyword);
|
||||
keywordsLayoutBinding.wholeWord.setChecked(filterKeyword.whole_word);
|
||||
keywordsLayoutBinding.deleteKeyword.setOnClickListener(v -> popupAddFilterBinding.keywordsContainer.removeView(keywordsLayoutBinding.deleteKeyword));
|
||||
popupAddFilterBinding.keywordsContainer.addView(keywordsLayoutBinding.getRoot());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Add at least a view
|
||||
KeywordsLayoutBinding keywordsLayoutBinding = KeywordsLayoutBinding.inflate(LayoutInflater.from(context));
|
||||
keywordsLayoutBinding.deleteKeyword.setOnClickListener(v -> popupAddFilterBinding.keywordsContainer.removeView(keywordsLayoutBinding.deleteKeyword));
|
||||
popupAddFilterBinding.keywordsContainer.addView(keywordsLayoutBinding.getRoot());
|
||||
}
|
||||
popupAddFilterBinding.actionRemove.setOnClickListener(v -> {
|
||||
popupAddFilterBinding.actionHide.setChecked(false);
|
||||
|
@ -149,15 +165,33 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
|
|||
|
||||
Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
button.setOnClickListener(view -> {
|
||||
if (popupAddFilterBinding.addPhrase.getText() == null || popupAddFilterBinding.addPhrase.getText().toString().trim().length() == 0) {
|
||||
popupAddFilterBinding.addPhrase.setError(context.getString(R.string.cannot_be_empty));
|
||||
return;
|
||||
|
||||
int keywordsItem = popupAddFilterBinding.keywordsContainer.getChildCount();
|
||||
List<Filter.KeywordsAttributes> keywordsAttributes = null;
|
||||
boolean canBeSent = true;
|
||||
|
||||
for (int i = 0; i < keywordsItem; i++) {
|
||||
View itemView = popupAddFilterBinding.keywordsContainer.getChildAt(i);
|
||||
AppCompatEditText keyword = itemView.findViewById(R.id.keyword_phrase);
|
||||
AppCompatCheckBox whole_word = itemView.findViewById(R.id.whole_word);
|
||||
keywordsAttributes = new ArrayList<>();
|
||||
if (keyword != null && whole_word != null) {
|
||||
Filter.KeywordsAttributes keywordsAttr = new Filter.KeywordsAttributes();
|
||||
keywordsAttr.keyword = keyword.getText().toString();
|
||||
keywordsAttr.whole_word = whole_word.isChecked();
|
||||
if (keywordsAttr.keyword.trim().isEmpty()) {
|
||||
keyword.setError(context.getString(R.string.cannot_be_empty));
|
||||
canBeSent = false;
|
||||
}
|
||||
keywordsAttributes.add(keywordsAttr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!popupAddFilterBinding.contextConversation.isChecked() && !popupAddFilterBinding.contextHome.isChecked() && !popupAddFilterBinding.contextPublic.isChecked() && !popupAddFilterBinding.contextNotification.isChecked() && !popupAddFilterBinding.contextProfiles.isChecked()) {
|
||||
popupAddFilterBinding.contextDescription.setError(context.getString(R.string.cannot_be_empty));
|
||||
return;
|
||||
canBeSent = false;
|
||||
}
|
||||
if (popupAddFilterBinding.addPhrase.getText() != null && popupAddFilterBinding.addPhrase.getText().toString().trim().length() > 0) {
|
||||
if (canBeSent) {
|
||||
Filter filterSent = new Filter();
|
||||
ArrayList<String> contextFilter = new ArrayList<>();
|
||||
if (popupAddFilterBinding.contextHome.isChecked())
|
||||
|
@ -171,15 +205,19 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
|
|||
if (popupAddFilterBinding.contextProfiles.isChecked())
|
||||
contextFilter.add("account");
|
||||
filterSent.context = contextFilter;
|
||||
filterSent.expires_at_sent = expire[0];
|
||||
filterSent.phrase = popupAddFilterBinding.addPhrase.getText().toString();
|
||||
filterSent.whole_word = popupAddFilterBinding.contextWholeWord.isChecked();
|
||||
filterSent.irreversible = popupAddFilterBinding.actionRemove.isChecked();
|
||||
if (expire[0] != -1) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.SECOND, expire[0]);
|
||||
filterSent.expires_at = calendar.getTime();
|
||||
} else {
|
||||
filterSent.expires_at = null;
|
||||
}
|
||||
filterSent.filter_action = popupAddFilterBinding.actionHide.isChecked() ? "hide" : "warn";
|
||||
if (filter != null) {
|
||||
accountsVM.editFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filter.id, filterSent.phrase, filterSent.context, filterSent.irreversible, filterSent.whole_word, filterSent.expires_at_sent)
|
||||
filtersVM.editFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filter.id, filterSent.title, filterSent.expires_at, filterSent.context, filterSent.filter_action, keywordsAttributes)
|
||||
.observe((LifecycleOwner) context, listener::callback);
|
||||
} else {
|
||||
accountsVM.addFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterSent.phrase, filterSent.context, filterSent.irreversible, filterSent.whole_word, filterSent.expires_at_sent)
|
||||
filtersVM.addFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterSent.title, filterSent.expires_at, filterSent.context, filterSent.filter_action, keywordsAttributes)
|
||||
.observe((LifecycleOwner) context, listener::callback);
|
||||
}
|
||||
alertDialog.dismiss();
|
||||
|
@ -192,7 +230,7 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
|
|||
alertDialog.setOnDismissListener(dialogInterface -> {
|
||||
//Hide keyboard
|
||||
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(popupAddFilterBinding.addPhrase.getWindowToken(), 0);
|
||||
imm.hideSoftInputFromWindow(popupAddFilterBinding.addTitle.getWindowToken(), 0);
|
||||
});
|
||||
if (alertDialog.getWindow() != null) {
|
||||
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
||||
|
@ -213,8 +251,8 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
|
|||
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
|
||||
}
|
||||
|
||||
AccountsVM accountsVM = new ViewModelProvider(FilterActivity.this).get(AccountsVM.class);
|
||||
accountsVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
|
||||
FiltersVM filtersVM = new ViewModelProvider(FilterActivity.this).get(FiltersVM.class);
|
||||
filtersVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
|
||||
.observe(FilterActivity.this, filters -> {
|
||||
BaseMainActivity.mainFilters = filters;
|
||||
if (filters != null && filters.size() > 0) {
|
||||
|
|
|
@ -15,6 +15,7 @@ package app.fedilab.android.client.endpoints;
|
|||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import app.fedilab.android.client.entities.api.Filter;
|
||||
|
@ -49,7 +50,7 @@ public interface MastodonFiltersService {
|
|||
Call<Filter> addFilter(
|
||||
@Header("Authorization") String token,
|
||||
@Field("title") String title,
|
||||
@Field("expires_in") Integer expires_in,
|
||||
@Field("expires_at") Date expires_at,
|
||||
@Field("filter_action") String filter_action,
|
||||
@Field("context[]") List<String> context,
|
||||
@Field("keywords_attributes") List<Filter.KeywordsAttributes> keywordsAttributes
|
||||
|
@ -62,7 +63,7 @@ public interface MastodonFiltersService {
|
|||
@Header("Authorization") String token,
|
||||
@Path("id") String id,
|
||||
@Field("title") String title,
|
||||
@Field("expires_in") Integer expires_in,
|
||||
@Field("expires_at") Date expires_at,
|
||||
@Field("filter_action") String filter_action,
|
||||
@Field("context[]") List<String> context,
|
||||
@Field("keywords_attributes") List<Filter.KeywordsAttributes> keywordsAttributes
|
||||
|
|
|
@ -24,12 +24,10 @@ import java.util.List;
|
|||
public class Filter implements Serializable {
|
||||
@SerializedName("id")
|
||||
public String id;
|
||||
@SerializedName("phrase")
|
||||
public String phrase;
|
||||
@SerializedName("title")
|
||||
public String title;
|
||||
@SerializedName("context")
|
||||
public List<String> context;
|
||||
@SerializedName("whole_word")
|
||||
public boolean whole_word;
|
||||
@SerializedName("expires_at")
|
||||
public Date expires_at;
|
||||
@SerializedName("filter_action")
|
||||
|
|
|
@ -33,12 +33,12 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import app.fedilab.android.BaseMainActivity;
|
||||
import app.fedilab.android.client.endpoints.MastodonAccountsService;
|
||||
import app.fedilab.android.client.endpoints.MastodonFiltersService;
|
||||
import app.fedilab.android.client.entities.api.Filter;
|
||||
import app.fedilab.android.client.entities.api.Notification;
|
||||
import app.fedilab.android.client.entities.api.Status;
|
||||
import app.fedilab.android.client.entities.app.Timeline;
|
||||
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||
import app.fedilab.android.viewmodel.mastodon.FiltersVM;
|
||||
import okhttp3.OkHttpClient;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
|
@ -47,18 +47,18 @@ import retrofit2.converter.gson.GsonConverterFactory;
|
|||
|
||||
public class TimelineHelper {
|
||||
|
||||
private static MastodonAccountsService init(Context context) {
|
||||
private static MastodonFiltersService initv2(Context context) {
|
||||
OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
||||
.readTimeout(60, TimeUnit.SECONDS)
|
||||
.connectTimeout(60, TimeUnit.SECONDS)
|
||||
.proxy(Helper.getProxy(context))
|
||||
.build();
|
||||
Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl("https://" + BaseMainActivity.currentInstance + "/api/v1/")
|
||||
.baseUrl("https://" + BaseMainActivity.currentInstance + "/api/v2/")
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(okHttpClient)
|
||||
.build();
|
||||
return retrofit.create(MastodonAccountsService.class);
|
||||
return retrofit.create(MastodonFiltersService.class);
|
||||
}
|
||||
|
||||
|
||||
|
@ -74,9 +74,9 @@ public class TimelineHelper {
|
|||
//A security to make sure filters have been fetched before displaying messages
|
||||
List<Status> statusesToRemove = new ArrayList<>();
|
||||
if (!BaseMainActivity.filterFetched) {
|
||||
MastodonAccountsService mastodonAccountsService = init(context);
|
||||
MastodonFiltersService mastodonFiltersService = initv2(context);
|
||||
List<Filter> filterList;
|
||||
Call<List<Filter>> getFiltersCall = mastodonAccountsService.getFilters(BaseMainActivity.currentToken);
|
||||
Call<List<Filter>> getFiltersCall = mastodonFiltersService.getFilters(BaseMainActivity.currentToken);
|
||||
if (getFiltersCall != null) {
|
||||
try {
|
||||
Response<List<Filter>> getFiltersResponse = getFiltersCall.execute();
|
||||
|
@ -111,52 +111,55 @@ public class TimelineHelper {
|
|||
} else {
|
||||
if (!filter.context.contains("public")) continue;
|
||||
}
|
||||
|
||||
if (filter.whole_word) {
|
||||
Pattern p = Pattern.compile("(^|\\W)(" + Pattern.quote(filter.phrase) + ")($|\\W)", Pattern.CASE_INSENSITIVE);
|
||||
for (Status status : statuses) {
|
||||
String content;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content).toString();
|
||||
Matcher m = p.matcher(content);
|
||||
if (m.find()) {
|
||||
statusesToRemove.add(status);
|
||||
continue;
|
||||
}
|
||||
if (status.spoiler_text != null) {
|
||||
String spoilerText;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text).toString();
|
||||
Matcher ms = p.matcher(spoilerText);
|
||||
if (ms.find()) {
|
||||
statusesToRemove.add(status);
|
||||
if (filter.keywords != null && filter.keywords.size() > 0) {
|
||||
for (Filter.FilterKeyword filterKeyword : filter.keywords) {
|
||||
if (filterKeyword.whole_word) {
|
||||
Pattern p = Pattern.compile("(^|\\W)(" + Pattern.quote(filterKeyword.keyword) + ")($|\\W)", Pattern.CASE_INSENSITIVE);
|
||||
for (Status status : statuses) {
|
||||
String content;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content).toString();
|
||||
Matcher m = p.matcher(content);
|
||||
if (m.find()) {
|
||||
statusesToRemove.add(status);
|
||||
continue;
|
||||
}
|
||||
if (status.spoiler_text != null) {
|
||||
String spoilerText;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text).toString();
|
||||
Matcher ms = p.matcher(spoilerText);
|
||||
if (ms.find()) {
|
||||
statusesToRemove.add(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Status status : statuses) {
|
||||
String content;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content).toString();
|
||||
if (content.contains(filter.phrase)) {
|
||||
statusesToRemove.add(status);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
for (Status status : statuses) {
|
||||
String content;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content).toString();
|
||||
if (content.contains(filterKeyword.keyword)) {
|
||||
statusesToRemove.add(status);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status.spoiler_text != null) {
|
||||
String spoilerText;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text).toString();
|
||||
if (spoilerText.contains(filter.phrase)) {
|
||||
statusesToRemove.add(status);
|
||||
if (status.spoiler_text != null) {
|
||||
String spoilerText;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text).toString();
|
||||
if (spoilerText.contains(filterKeyword.keyword)) {
|
||||
statusesToRemove.add(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,8 +185,8 @@ public class TimelineHelper {
|
|||
List<Notification> notificationToRemove = new ArrayList<>();
|
||||
if (!BaseMainActivity.filterFetched) {
|
||||
try {
|
||||
AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AccountsVM.class);
|
||||
accountsVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken).observe((LifecycleOwner) context, filters -> {
|
||||
FiltersVM filtersVM = new ViewModelProvider((ViewModelStoreOwner) context).get(FiltersVM.class);
|
||||
filtersVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken).observe((LifecycleOwner) context, filters -> {
|
||||
BaseMainActivity.filterFetched = true;
|
||||
BaseMainActivity.mainFilters = filters;
|
||||
});
|
||||
|
@ -192,51 +195,81 @@ public class TimelineHelper {
|
|||
}
|
||||
}
|
||||
//If there are filters:
|
||||
if (BaseMainActivity.mainFilters != null && BaseMainActivity.mainFilters.size() > 0) {
|
||||
if (BaseMainActivity.mainFilters != null && BaseMainActivity.mainFilters.size() > 0 && notifications != null && notifications.size() > 0) {
|
||||
|
||||
//Loop through filters
|
||||
for (Filter filter : BaseMainActivity.mainFilters) {
|
||||
if (filter.irreversible) { //Dealt by the server
|
||||
if (filter.expires_at != null && filter.expires_at.before(new Date())) {
|
||||
//Expired filter
|
||||
continue;
|
||||
}
|
||||
for (String filterContext : filter.context) {
|
||||
if (Timeline.TimeLineEnum.NOTIFICATION.getValue().equalsIgnoreCase(filterContext)) {
|
||||
if (filter.whole_word) {
|
||||
Pattern p = Pattern.compile("(^" + Pattern.quote(filter.phrase) + "\\b|\\b" + Pattern.quote(filter.phrase) + "$)", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
if (!filter.context.contains("notification")) continue;
|
||||
if (filter.keywords != null && filter.keywords.size() > 0) {
|
||||
for (Filter.FilterKeyword filterKeyword : filter.keywords) {
|
||||
if (filterKeyword.whole_word) {
|
||||
Pattern p = Pattern.compile("(^|\\W)(" + Pattern.quote(filterKeyword.keyword) + ")($|\\W)", Pattern.CASE_INSENSITIVE);
|
||||
for (Notification notification : notifications) {
|
||||
notification.cached = cached;
|
||||
if (notification.status != null) {
|
||||
String content;
|
||||
if (notification.status == null) {
|
||||
continue;
|
||||
}
|
||||
String content;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
content = Html.fromHtml(notification.status.reblog != null ? notification.status.reblog.content : notification.status.content, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
content = Html.fromHtml(notification.status.reblog != null ? notification.status.reblog.content : notification.status.content).toString();
|
||||
Matcher m = p.matcher(content);
|
||||
if (m.find()) {
|
||||
notificationToRemove.add(notification);
|
||||
continue;
|
||||
}
|
||||
if (notification.status.spoiler_text != null) {
|
||||
String spoilerText;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
content = Html.fromHtml(notification.status.content, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
spoilerText = Html.fromHtml(notification.status.reblog != null ? notification.status.reblog.spoiler_text : notification.status.spoiler_text, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
content = Html.fromHtml(notification.status.content).toString();
|
||||
Matcher m = p.matcher(content);
|
||||
if (m.find()) {
|
||||
spoilerText = Html.fromHtml(notification.status.reblog != null ? notification.status.reblog.spoiler_text : notification.status.spoiler_text).toString();
|
||||
Matcher ms = p.matcher(spoilerText);
|
||||
if (ms.find()) {
|
||||
notificationToRemove.add(notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Notification notification : notifications) {
|
||||
if (notification.status == null) {
|
||||
continue;
|
||||
}
|
||||
String content;
|
||||
notification.cached = cached;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
content = Html.fromHtml(notification.status.content, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
content = Html.fromHtml(notification.status.reblog != null ? notification.status.reblog.content : notification.status.content, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
content = Html.fromHtml(notification.status.content).toString();
|
||||
if (content.contains(filter.phrase)) {
|
||||
content = Html.fromHtml(notification.status.reblog != null ? notification.status.reblog.content : notification.status.content).toString();
|
||||
if (content.contains(filterKeyword.keyword)) {
|
||||
notificationToRemove.add(notification);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (notification.status.spoiler_text != null) {
|
||||
String spoilerText;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
spoilerText = Html.fromHtml(notification.status.reblog != null ? notification.status.reblog.spoiler_text : notification.status.spoiler_text, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
else
|
||||
spoilerText = Html.fromHtml(notification.status.reblog != null ? notification.status.reblog.spoiler_text : notification.status.spoiler_text).toString();
|
||||
if (spoilerText.contains(filterKeyword.keyword)) {
|
||||
notificationToRemove.add(notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Notification notification : notifications) {
|
||||
notification.cached = cached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
notifications.removeAll(notificationToRemove);
|
||||
if (notifications != null) {
|
||||
notifications.removeAll(notificationToRemove);
|
||||
}
|
||||
return notifications;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import app.fedilab.android.activities.FilterActivity;
|
|||
import app.fedilab.android.client.entities.api.Filter;
|
||||
import app.fedilab.android.databinding.DrawerFilterBinding;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||
import app.fedilab.android.viewmodel.mastodon.FiltersVM;
|
||||
|
||||
|
||||
public class FilterAdapter extends RecyclerView.Adapter<FilterAdapter.FilterViewHolder> {
|
||||
|
@ -69,8 +69,8 @@ public class FilterAdapter extends RecyclerView.Adapter<FilterAdapter.FilterView
|
|||
@Override
|
||||
public void onBindViewHolder(@NonNull FilterViewHolder holder, int position) {
|
||||
Filter filter = filters.get(position);
|
||||
if (filter.phrase != null) {
|
||||
holder.binding.filterWord.setText(filter.phrase);
|
||||
if (filter.title != null) {
|
||||
holder.binding.filterWord.setText(filter.title);
|
||||
}
|
||||
StringBuilder contextString = new StringBuilder();
|
||||
if (filter.context != null)
|
||||
|
@ -79,10 +79,7 @@ public class FilterAdapter extends RecyclerView.Adapter<FilterAdapter.FilterView
|
|||
holder.binding.filterContext.setText(contextString.toString());
|
||||
holder.binding.editFilter.setOnClickListener(v -> FilterActivity.addEditFilter(context, filter, filter1 -> {
|
||||
if (filter1 != null) {
|
||||
BaseMainActivity.mainFilters.get(position).phrase = filter1.phrase;
|
||||
BaseMainActivity.mainFilters.get(position).context = filter1.context;
|
||||
BaseMainActivity.mainFilters.get(position).whole_word = filter1.whole_word;
|
||||
BaseMainActivity.mainFilters.get(position).irreversible = filter1.irreversible;
|
||||
BaseMainActivity.mainFilters.get(position).expires_at = filter1.expires_at;
|
||||
}
|
||||
filterAdapter.notifyItemChanged(position);
|
||||
|
@ -93,8 +90,8 @@ public class FilterAdapter extends RecyclerView.Adapter<FilterAdapter.FilterView
|
|||
builder.setMessage(R.string.action_lists_confirm_delete);
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AccountsVM.class);
|
||||
accountsVM.removeFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filter.id);
|
||||
FiltersVM filtersVM = new ViewModelProvider((ViewModelStoreOwner) context).get(FiltersVM.class);
|
||||
filtersVM.removeFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filter.id);
|
||||
filters.remove(filter);
|
||||
if (filters.size() == 0) {
|
||||
delete.allFiltersDeleted();
|
||||
|
|
|
@ -25,6 +25,7 @@ import androidx.lifecycle.AndroidViewModel;
|
|||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -129,24 +130,15 @@ public class FiltersVM extends AndroidViewModel {
|
|||
/**
|
||||
* Create a filter
|
||||
*
|
||||
* @param phrase Text to be filtered
|
||||
* @param filterContext Array of enumerable strings "home", "notifications", "public", "thread". At least one context must be specified.
|
||||
* @param irreversible Should the server irreversibly drop matching entities from home and notifications?
|
||||
* @param wholeWord Consider word boundaries?
|
||||
* @param expiresIn Number of seconds from now the filter should expire. Otherwise, null for a filter that doesn't expire.
|
||||
* @return {@link LiveData} containing a {@link Filter}
|
||||
*/
|
||||
public LiveData<Filter> addFilter(@NonNull String instance, String token,
|
||||
@NonNull String phrase,
|
||||
@NonNull List<String> filterContext,
|
||||
boolean irreversible,
|
||||
boolean wholeWord,
|
||||
int expiresIn) {
|
||||
@NonNull String title, Date expires_at, @NonNull List<String> filterContext, String filter_action, List<Filter.KeywordsAttributes> keywordsAttributes) {
|
||||
filterMutableLiveData = new MutableLiveData<>();
|
||||
MastodonFiltersService mastodonFiltersService = initV2(instance);
|
||||
new Thread(() -> {
|
||||
Filter filter = null;
|
||||
Call<Filter> addFilterCall = mastodonFiltersService.addFilter(token, phrase, filterContext, irreversible, wholeWord, expiresIn == -1 ? null : expiresIn);
|
||||
Call<Filter> addFilterCall = mastodonFiltersService.addFilter(token, title, expires_at, filter_action, filterContext, keywordsAttributes);
|
||||
if (addFilterCall != null) {
|
||||
try {
|
||||
Response<Filter> addFiltersResponse = addFilterCall.execute();
|
||||
|
@ -168,20 +160,14 @@ public class FiltersVM extends AndroidViewModel {
|
|||
/**
|
||||
* Update a filter
|
||||
*
|
||||
* @param id ID of the filter
|
||||
* @param phrase Text to be filtered
|
||||
* @param filterContext Array of enumerable strings "home", "notifications", "public", "thread". At least one context must be specified.
|
||||
* @param irreversible Should the server irreversibly drop matching entities from home and notifications?
|
||||
* @param wholeWord Consider word boundaries?
|
||||
* @param expiresIn Number of seconds from now the filter should expire. Otherwise, null for a filter that doesn't expire.
|
||||
* @return {@link LiveData} containing a {@link Filter}
|
||||
*/
|
||||
public LiveData<Filter> editFilter(@NonNull String instance, String token, @NonNull String id, @NonNull String phrase, @NonNull List<String> filterContext, boolean irreversible, boolean wholeWord, int expiresIn) {
|
||||
public LiveData<Filter> editFilter(@NonNull String instance, String token, @NonNull String id, @NonNull String title, Date expires_at, @NonNull List<String> filterContext, String filter_action, List<Filter.KeywordsAttributes> keywordsAttributes) {
|
||||
filterMutableLiveData = new MutableLiveData<>();
|
||||
MastodonFiltersService mastodonFiltersService = initV2(instance);
|
||||
new Thread(() -> {
|
||||
Filter filter = null;
|
||||
Call<Filter> editFilterCall = mastodonFiltersService.editFilter(token, id, phrase, filterContext, irreversible, wholeWord, expiresIn == -1 ? null : expiresIn);
|
||||
Call<Filter> editFilterCall = mastodonFiltersService.editFilter(token, id, title, expires_at, filter_action, filterContext, keywordsAttributes);
|
||||
if (editFilterCall != null) {
|
||||
Log.v(Helper.TAG, "request: " + editFilterCall.request());
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/keyword_phrase"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/keyword_or_phrase" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/whole_word"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/delete_keyword"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:contentDescription="@string/delete_keyword"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_baseline_delete_24" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
@ -35,6 +35,24 @@
|
|||
android:inputType="text"
|
||||
android:singleLine="true" />
|
||||
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/filter_expire" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
android:id="@+id/filter_expire"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="30dp" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -167,6 +185,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="@string/hide_completely"
|
||||
android:textSize="16sp"
|
||||
android:checked="true"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
|
@ -178,41 +197,47 @@
|
|||
|
||||
</RadioGroup>
|
||||
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/context_whole_word"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/context_whole_word"
|
||||
app:buttonTint="@color/cyanea_accent_dark_reference" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:text="@string/context_whole_word_explanations"
|
||||
android:textColor="@color/cyanea_accent_dark_reference"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/keyword_or_phrase" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="116dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/context_whole_word" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/keywords_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/add_keyword"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/filter_expire" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
android:id="@+id/filter_expire"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="30dp" />
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/add_keyword"
|
||||
android:textColor="@color/cyanea_accent_dark_reference"
|
||||
app:icon="@drawable/ic_baseline_add_24"
|
||||
app:iconTint="@color/cyanea_primary_dark_reference"
|
||||
app:strokeColor="@color/cyanea_accent_dark_reference" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
</ScrollView>
|
|
@ -1997,4 +1997,7 @@
|
|||
<string name="hide_completely_description">Completely hide the filtered content, behaving as if it did not exist</string>
|
||||
<string name="context_home_list">Home and lists</string>
|
||||
<string name="title">Title</string>
|
||||
<string name="keyword_or_phrase">Keyword or phrase</string>
|
||||
<string name="delete_keyword">Delete keyword</string>
|
||||
<string name="add_keyword">Add keyword</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue