Comment #467 #464 - Support API endpoints

This commit is contained in:
Thomas 2022-11-17 18:43:48 +01:00
parent 8bf21db632
commit 74599d8a7f
10 changed files with 274 additions and 162 deletions

View File

@ -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 -> {

View File

@ -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) {

View File

@ -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

View File

@ -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")

View File

@ -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;
}

View File

@ -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();

View File

@ -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 {

View File

@ -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>

View File

@ -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>

View File

@ -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>