mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2024-12-22 16:50:04 +02:00
Filter timelines locally (hide boosts/replies and filter with regex)
This commit is contained in:
parent
87a5b69ba0
commit
8fcbe19d4d
12 changed files with 398 additions and 42 deletions
|
@ -31,15 +31,21 @@ import android.graphics.drawable.Drawable;
|
|||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -69,6 +75,7 @@ import java.io.File;
|
|||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import app.fedilab.android.activities.ActionActivity;
|
||||
import app.fedilab.android.activities.BaseActivity;
|
||||
|
@ -100,6 +107,8 @@ import app.fedilab.android.helper.Helper;
|
|||
import app.fedilab.android.helper.PinnedTimelineHelper;
|
||||
import app.fedilab.android.helper.PushHelper;
|
||||
import app.fedilab.android.helper.ThemeHelper;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonConversation;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonNotification;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||
import app.fedilab.android.viewmodel.mastodon.InstancesVM;
|
||||
|
@ -123,7 +132,8 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
private AppBarConfiguration mAppBarConfiguration;
|
||||
private ActivityMainBinding binding;
|
||||
private Pinned pinned;
|
||||
|
||||
public static boolean show_boosts, show_replies, show_art_nsfw;
|
||||
public static String regex_home, regex_local, regex_public;
|
||||
|
||||
private final BroadcastReceiver broadcast_data = new BroadcastReceiver() {
|
||||
@Override
|
||||
|
@ -221,18 +231,53 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
binding.bottomNavView.inflateMenu(R.menu.bottom_nav_menu);
|
||||
binding.bottomNavView.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
|
||||
binding.navView.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
|
||||
|
||||
|
||||
//ManageClick on bottom menu items
|
||||
binding.bottomNavView.findViewById(R.id.nav_home).setOnLongClickListener(view -> {
|
||||
manageFilters(0);
|
||||
return false;
|
||||
});
|
||||
binding.bottomNavView.findViewById(R.id.nav_local).setOnLongClickListener(view -> {
|
||||
manageFilters(1);
|
||||
return false;
|
||||
});
|
||||
binding.bottomNavView.findViewById(R.id.nav_public).setOnLongClickListener(view -> {
|
||||
manageFilters(2);
|
||||
return false;
|
||||
});
|
||||
binding.bottomNavView.setOnItemSelectedListener(item -> {
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.nav_home) {
|
||||
binding.viewPager.setCurrentItem(0);
|
||||
if (binding.viewPager.getCurrentItem() == 0) {
|
||||
scrollToTop();
|
||||
} else {
|
||||
binding.viewPager.setCurrentItem(0);
|
||||
}
|
||||
} else if (itemId == R.id.nav_local) {
|
||||
binding.viewPager.setCurrentItem(1);
|
||||
if (binding.viewPager.getCurrentItem() == 1) {
|
||||
scrollToTop();
|
||||
} else {
|
||||
binding.viewPager.setCurrentItem(1);
|
||||
}
|
||||
} else if (itemId == R.id.nav_public) {
|
||||
binding.viewPager.setCurrentItem(2);
|
||||
if (binding.viewPager.getCurrentItem() == 2) {
|
||||
scrollToTop();
|
||||
} else {
|
||||
binding.viewPager.setCurrentItem(2);
|
||||
}
|
||||
} else if (itemId == R.id.nav_notifications) {
|
||||
binding.viewPager.setCurrentItem(3);
|
||||
if (binding.viewPager.getCurrentItem() == 3) {
|
||||
scrollToTop();
|
||||
} else {
|
||||
binding.viewPager.setCurrentItem(3);
|
||||
}
|
||||
} else if (itemId == R.id.nav_privates) {
|
||||
binding.viewPager.setCurrentItem(4);
|
||||
if (binding.viewPager.getCurrentItem() == 4) {
|
||||
scrollToTop();
|
||||
} else {
|
||||
binding.viewPager.setCurrentItem(4);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
@ -501,6 +546,12 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
}
|
||||
currentInstance = account.instance;
|
||||
currentUserID = account.user_id;
|
||||
show_boosts = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_BOOSTS) + currentUserID + currentInstance, true);
|
||||
show_replies = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_REPLIES) + currentUserID + currentInstance, true);
|
||||
regex_home = sharedpreferences.getString(getString(R.string.SET_FILTER_REGEX_HOME) + currentUserID + currentInstance, null);
|
||||
regex_local = sharedpreferences.getString(getString(R.string.SET_FILTER_REGEX_LOCAL) + currentUserID + currentInstance, null);
|
||||
regex_public = sharedpreferences.getString(getString(R.string.SET_FILTER_REGEX_PUBLIC) + currentUserID + currentInstance, null);
|
||||
show_art_nsfw = sharedpreferences.getBoolean(getString(R.string.SET_ART_WITH_NSFW) + currentUserID + currentInstance, false);
|
||||
accountWeakReference = new WeakReference<>(account);
|
||||
binding.profilePicture.setOnClickListener(v -> binding.drawerLayout.openDrawer(GravityCompat.START));
|
||||
Helper.loadPP(binding.profilePicture, account);
|
||||
|
@ -579,6 +630,134 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
LocalBroadcastManager.getInstance(BaseMainActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA));
|
||||
}
|
||||
|
||||
|
||||
private void manageFilters(int position) {
|
||||
View view = binding.bottomNavView.findViewById(R.id.nav_home);
|
||||
if (position == 1) {
|
||||
view = binding.bottomNavView.findViewById(R.id.nav_local);
|
||||
} else if (position == 2) {
|
||||
view = binding.bottomNavView.findViewById(R.id.nav_public);
|
||||
}
|
||||
PopupMenu popup = new PopupMenu(new ContextThemeWrapper(BaseMainActivity.this, Helper.popupStyle()), view, Gravity.TOP);
|
||||
popup.getMenuInflater()
|
||||
.inflate(R.menu.option_filter_toots, popup.getMenu());
|
||||
Menu menu = popup.getMenu();
|
||||
final MenuItem itemShowBoosts = menu.findItem(R.id.action_show_boosts);
|
||||
final MenuItem itemShowReplies = menu.findItem(R.id.action_show_replies);
|
||||
final MenuItem itemFilter = menu.findItem(R.id.action_filter);
|
||||
if (position > 0) {
|
||||
itemShowBoosts.setVisible(false);
|
||||
itemShowReplies.setVisible(false);
|
||||
} else {
|
||||
itemShowBoosts.setVisible(true);
|
||||
itemShowReplies.setVisible(true);
|
||||
}
|
||||
|
||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this);
|
||||
String show_filtered = null;
|
||||
if (position == 0) {
|
||||
show_filtered = sharedpreferences.getString(getString(R.string.SET_FILTER_REGEX_HOME) + currentUserID + currentInstance, null);
|
||||
} else if (position == 1) {
|
||||
show_filtered = sharedpreferences.getString(getString(R.string.SET_FILTER_REGEX_LOCAL) + currentUserID + currentInstance, null);
|
||||
} else if (position == 2) {
|
||||
show_filtered = sharedpreferences.getString(getString(R.string.SET_FILTER_REGEX_PUBLIC) + currentUserID + currentInstance, null);
|
||||
}
|
||||
itemShowBoosts.setChecked(show_boosts);
|
||||
itemShowReplies.setChecked(show_replies);
|
||||
if (show_filtered != null && show_filtered.length() > 0) {
|
||||
itemFilter.setTitle(show_filtered);
|
||||
}
|
||||
popup.setOnDismissListener(menu1 -> {
|
||||
if (binding.viewPager.getAdapter() != null) {
|
||||
Fragment fragment = (Fragment) binding.viewPager.getAdapter().instantiateItem(binding.viewPager, binding.tabLayout.getSelectedTabPosition());
|
||||
if (fragment instanceof FragmentMastodonTimeline && fragment.isVisible()) {
|
||||
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
|
||||
fragmentMastodonTimeline.refreshAllAdapters();
|
||||
}
|
||||
}
|
||||
});
|
||||
String finalShow_filtered = show_filtered;
|
||||
popup.setOnMenuItemClickListener(item -> {
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
|
||||
item.setActionView(new View(BaseMainActivity.this));
|
||||
item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
final SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.action_show_boosts) {
|
||||
show_boosts = !show_boosts;
|
||||
editor.putBoolean(getString(R.string.SET_SHOW_BOOSTS) + currentUserID + currentInstance, show_boosts);
|
||||
itemShowBoosts.setChecked(show_boosts);
|
||||
editor.apply();
|
||||
} else if (itemId == R.id.action_show_replies) {
|
||||
show_replies = !show_replies;
|
||||
editor.putBoolean(getString(R.string.SET_SHOW_REPLIES) + currentUserID + currentInstance, show_replies);
|
||||
itemShowReplies.setChecked(show_replies);
|
||||
editor.apply();
|
||||
} else if (itemId == R.id.action_filter) {
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(BaseMainActivity.this, Helper.dialogStyle());
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
View dialogView = inflater.inflate(R.layout.popup_filter_regex, new LinearLayout(BaseMainActivity.this), false);
|
||||
dialogBuilder.setView(dialogView);
|
||||
final EditText editText = dialogView.findViewById(R.id.filter_regex);
|
||||
Toast alertRegex = Toasty.warning(BaseMainActivity.this, getString(R.string.alert_regex), Toast.LENGTH_LONG);
|
||||
editText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
try {
|
||||
Pattern.compile("(" + s.toString() + ")", Pattern.CASE_INSENSITIVE);
|
||||
} catch (Exception e) {
|
||||
if (!alertRegex.getView().isShown()) {
|
||||
alertRegex.show();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
if (finalShow_filtered != null) {
|
||||
editText.setText(finalShow_filtered);
|
||||
editText.setSelection(editText.getText().toString().length());
|
||||
}
|
||||
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
|
||||
itemFilter.setTitle(editText.getText().toString().trim());
|
||||
if (position == 0) {
|
||||
editor.putString(getString(R.string.SET_FILTER_REGEX_HOME) + currentUserID + currentInstance, editText.getText().toString().trim());
|
||||
regex_home = editText.getText().toString().trim();
|
||||
} else if (position == 1) {
|
||||
editor.putString(getString(R.string.SET_FILTER_REGEX_LOCAL) + currentUserID + currentInstance, editText.getText().toString().trim());
|
||||
regex_local = editText.getText().toString().trim();
|
||||
} else if (position == 2) {
|
||||
editor.putString(getString(R.string.SET_FILTER_REGEX_PUBLIC) + currentUserID + currentInstance, editText.getText().toString().trim());
|
||||
regex_public = editText.getText().toString().trim();
|
||||
}
|
||||
editor.apply();
|
||||
});
|
||||
AlertDialog alertDialog = dialogBuilder.create();
|
||||
alertDialog.show();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
popup.show();
|
||||
}
|
||||
|
||||
public void refreshFragment() {
|
||||
if (binding.viewPager.getAdapter() != null) {
|
||||
binding.viewPager.getAdapter().notifyDataSetChanged();
|
||||
|
@ -615,6 +794,26 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow to scroll to top for bottom navigation items
|
||||
*/
|
||||
private void scrollToTop() {
|
||||
if (binding.viewPager.getAdapter() != null) {
|
||||
Fragment fragment = (Fragment) binding.viewPager.getAdapter().instantiateItem(binding.viewPager, binding.tabLayout.getSelectedTabPosition());
|
||||
if (fragment instanceof FragmentMastodonTimeline) {
|
||||
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
|
||||
fragmentMastodonTimeline.scrollToTop();
|
||||
} else if (fragment instanceof FragmentMastodonNotification) {
|
||||
FragmentMastodonNotification fragmentMastodonNotification = ((FragmentMastodonNotification) fragment);
|
||||
fragmentMastodonNotification.scrollToTop();
|
||||
} else if (fragment instanceof FragmentMastodonConversation) {
|
||||
FragmentMastodonConversation fragmentMastodonConversation = ((FragmentMastodonConversation) fragment);
|
||||
fragmentMastodonConversation.scrollToTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
|
|
@ -385,9 +385,9 @@ public class Timeline {
|
|||
@SerializedName("SCHEDULED_TOOT_CLIENT")
|
||||
SCHEDULED_TOOT_CLIENT("SCHEDULED_TOOT_CLIENT"),
|
||||
@SerializedName("SCHEDULED_BOOST")
|
||||
SCHEDULED_BOOST("SCHEDULED_BOOST");
|
||||
|
||||
|
||||
SCHEDULED_BOOST("SCHEDULED_BOOST"),
|
||||
@SerializedName("UNKNOWN")
|
||||
UNKNOWN("UNKNOWN");
|
||||
private final String value;
|
||||
|
||||
TimeLineEnum(String value) {
|
||||
|
|
|
@ -24,19 +24,42 @@ import androidx.lifecycle.ViewModelStoreOwner;
|
|||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import app.fedilab.android.BaseMainActivity;
|
||||
import app.fedilab.android.activities.MainActivity;
|
||||
import app.fedilab.android.client.mastodon.MastodonAccountsService;
|
||||
import app.fedilab.android.client.mastodon.entities.Filter;
|
||||
import app.fedilab.android.client.mastodon.entities.Notification;
|
||||
import app.fedilab.android.client.mastodon.entities.Status;
|
||||
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||
import okhttp3.OkHttpClient;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
public class TimelineHelper {
|
||||
|
||||
private static MastodonAccountsService init(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://" + MainActivity.currentInstance + "/api/v1/")
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(okHttpClient)
|
||||
.build();
|
||||
return retrofit.create(MastodonAccountsService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to filter statuses, should be called in API calls (background)
|
||||
*
|
||||
|
@ -49,17 +72,23 @@ public class TimelineHelper {
|
|||
//A security to make sure filters have been fetched before displaying messages
|
||||
List<Status> statusesToRemove = 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 -> {
|
||||
BaseMainActivity.filterFetched = true;
|
||||
BaseMainActivity.mainFilters = filters;
|
||||
});
|
||||
} catch (ClassCastException e) {
|
||||
e.printStackTrace();
|
||||
return statuses;
|
||||
MastodonAccountsService mastodonAccountsService = init(context);
|
||||
List<Filter> filterList;
|
||||
Call<List<Filter>> getFiltersCall = mastodonAccountsService.getFilters(MainActivity.currentToken);
|
||||
if (getFiltersCall != null) {
|
||||
try {
|
||||
Response<List<Filter>> getFiltersResponse = getFiltersCall.execute();
|
||||
if (getFiltersResponse.isSuccessful()) {
|
||||
BaseMainActivity.filterFetched = true;
|
||||
filterList = getFiltersResponse.body();
|
||||
BaseMainActivity.mainFilters = filterList;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If there are filters:
|
||||
if (BaseMainActivity.mainFilters != null && BaseMainActivity.mainFilters.size() > 0) {
|
||||
for (Filter filter : BaseMainActivity.mainFilters) {
|
||||
|
|
|
@ -28,6 +28,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import app.fedilab.android.client.entities.Timeline;
|
||||
import app.fedilab.android.client.mastodon.entities.Status;
|
||||
import app.fedilab.android.databinding.DrawerStatusBinding;
|
||||
import app.fedilab.android.viewmodel.mastodon.SearchVM;
|
||||
|
@ -71,7 +72,7 @@ public class ContextAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||
StatusesVM statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get(StatusesVM.class);
|
||||
SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get(SearchVM.class);
|
||||
StatusAdapter.StatusViewHolder holder = (StatusAdapter.StatusViewHolder) viewHolder;
|
||||
statusManagement(context, statusesVM, searchVM, holder, this, statusList, null, status, false, false);
|
||||
statusManagement(context, statusesVM, searchVM, holder, this, statusList, null, status, Timeline.TimeLineEnum.UNKNOWN, false);
|
||||
//Hide/Show specific view
|
||||
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import java.util.Locale;
|
|||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.ProfileActivity;
|
||||
import app.fedilab.android.client.entities.Timeline;
|
||||
import app.fedilab.android.client.mastodon.entities.Notification;
|
||||
import app.fedilab.android.databinding.DrawerFollowBinding;
|
||||
import app.fedilab.android.databinding.DrawerStatusNotificationBinding;
|
||||
|
@ -134,7 +135,7 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
|||
StatusAdapter.StatusViewHolder holderStatus = (StatusAdapter.StatusViewHolder) viewHolder;
|
||||
StatusesVM statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get(StatusesVM.class);
|
||||
SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get(SearchVM.class);
|
||||
statusManagement(context, statusesVM, searchVM, holderStatus, this, null, notificationList, notification.status, false, false);
|
||||
statusManagement(context, statusesVM, searchVM, holderStatus, this, null, notificationList, notification.status, Timeline.TimeLineEnum.NOTIFICATION, false);
|
||||
holderStatus.bindingNotification.containerTransparent.setAlpha(.3f);
|
||||
if (getItemViewType(position) == TYPE_MENTION || getItemViewType(position) == TYPE_STATUS) {
|
||||
holderStatus.bindingNotification.status.actionButtons.setVisibility(View.VISIBLE);
|
||||
|
|
|
@ -15,6 +15,11 @@ package app.fedilab.android.ui.drawer;
|
|||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
|
||||
import static app.fedilab.android.BaseMainActivity.regex_home;
|
||||
import static app.fedilab.android.BaseMainActivity.regex_local;
|
||||
import static app.fedilab.android.BaseMainActivity.regex_public;
|
||||
import static app.fedilab.android.BaseMainActivity.show_boosts;
|
||||
import static app.fedilab.android.BaseMainActivity.show_replies;
|
||||
import static app.fedilab.android.activities.ContextActivity.expand;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
@ -96,11 +101,13 @@ import app.fedilab.android.activities.ProfileActivity;
|
|||
import app.fedilab.android.activities.ReportActivity;
|
||||
import app.fedilab.android.activities.StatusInfoActivity;
|
||||
import app.fedilab.android.client.entities.StatusDraft;
|
||||
import app.fedilab.android.client.entities.Timeline;
|
||||
import app.fedilab.android.client.mastodon.entities.Attachment;
|
||||
import app.fedilab.android.client.mastodon.entities.Notification;
|
||||
import app.fedilab.android.client.mastodon.entities.Poll;
|
||||
import app.fedilab.android.client.mastodon.entities.Status;
|
||||
import app.fedilab.android.databinding.DrawerStatusBinding;
|
||||
import app.fedilab.android.databinding.DrawerStatusHiddenBinding;
|
||||
import app.fedilab.android.databinding.DrawerStatusNotificationBinding;
|
||||
import app.fedilab.android.databinding.DrawerStatusReportBinding;
|
||||
import app.fedilab.android.databinding.LayoutMediaBinding;
|
||||
|
@ -120,35 +127,75 @@ import jp.wasabeef.glide.transformations.BlurTransformation;
|
|||
|
||||
public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
private final List<Status> statusList;
|
||||
private final boolean remote;
|
||||
private final boolean minified;
|
||||
private Context context;
|
||||
private final Timeline.TimeLineEnum timelineType;
|
||||
|
||||
public StatusAdapter(List<Status> statuses, boolean remote, boolean minified) {
|
||||
public StatusAdapter(List<Status> statuses, Timeline.TimeLineEnum timelineType, boolean minified) {
|
||||
this.statusList = statuses;
|
||||
this.remote = remote;
|
||||
this.timelineType = timelineType;
|
||||
this.minified = minified;
|
||||
}
|
||||
|
||||
|
||||
public StatusAdapter(List<Status> statuses, boolean remote) {
|
||||
this.statusList = statuses;
|
||||
this.remote = remote;
|
||||
this.minified = false;
|
||||
private static boolean isVisble(Timeline.TimeLineEnum timelineType, Status status) {
|
||||
if (timelineType == Timeline.TimeLineEnum.HOME && !show_boosts && status.reblog != null) {
|
||||
return false;
|
||||
}
|
||||
if (timelineType == Timeline.TimeLineEnum.HOME && !show_replies && status.in_reply_to_id != null) {
|
||||
return false;
|
||||
}
|
||||
if (timelineType == Timeline.TimeLineEnum.HOME && regex_home != null && !regex_home.trim().equals("")) {
|
||||
try {
|
||||
Pattern filterPattern = Pattern.compile("(" + regex_home + ")", Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = filterPattern.matcher(status.content);
|
||||
if (matcher.find())
|
||||
return false;
|
||||
matcher = filterPattern.matcher(status.spoiler_text);
|
||||
if (matcher.find())
|
||||
return false;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (timelineType == Timeline.TimeLineEnum.LOCAL && regex_local != null && !regex_local.trim().equals("")) {
|
||||
try {
|
||||
Pattern filterPattern = Pattern.compile("(" + regex_local + ")", Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = filterPattern.matcher(status.content);
|
||||
if (matcher.find())
|
||||
return false;
|
||||
matcher = filterPattern.matcher(status.spoiler_text);
|
||||
if (matcher.find())
|
||||
return false;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (timelineType == Timeline.TimeLineEnum.PUBLIC && regex_public != null && !regex_public.trim().equals("")) {
|
||||
try {
|
||||
Pattern filterPattern = Pattern.compile("(" + regex_public + ")", Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = filterPattern.matcher(status.content);
|
||||
if (matcher.find())
|
||||
return false;
|
||||
matcher = filterPattern.matcher(status.spoiler_text);
|
||||
if (matcher.find())
|
||||
return false;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Manage status, this method is also reused in notifications timelines
|
||||
*
|
||||
* @param context Context
|
||||
* @param context Timeline.TimeLineEnum timelineType
|
||||
* @param statusesVM StatusesVM - For handling actions in background to the correct activity
|
||||
* @param searchVM SearchVM - For handling remote actions
|
||||
* @param holder StatusViewHolder
|
||||
* @param adapter RecyclerView.Adapter<RecyclerView.ViewHolder> - General adapter that can be for {@link StatusAdapter} or {@link NotificationAdapter}
|
||||
* @param statusList List<Status>
|
||||
* @param notificationList List<Notification>
|
||||
* @param remote boolean Indicate if the status is a remote one (ie not yet federated)
|
||||
* @param timelineType Timeline.TimeLineEnum
|
||||
* @param status {@link Status}
|
||||
*/
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
|
@ -160,12 +207,17 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
List<Status> statusList,
|
||||
List<Notification> notificationList,
|
||||
Status status,
|
||||
boolean remote,
|
||||
Timeline.TimeLineEnum timelineType,
|
||||
boolean minified) {
|
||||
if (status == null) {
|
||||
return;
|
||||
}
|
||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
boolean remote = timelineType == Timeline.TimeLineEnum.REMOTE;
|
||||
|
||||
Status statusToDeal = status.reblog != null ? status.reblog : status;
|
||||
|
||||
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
boolean expand_cw = sharedpreferences.getBoolean(context.getString(R.string.SET_EXPAND_CW), false);
|
||||
boolean expand_media = sharedpreferences.getBoolean(context.getString(R.string.SET_EXPAND_MEDIA), false);
|
||||
|
@ -1491,16 +1543,26 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return isVisble(timelineType, statusList.get(position)) ? 1 : 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
context = parent.getContext();
|
||||
if (!minified) {
|
||||
DrawerStatusBinding itemBinding = DrawerStatusBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
if (viewType == 0) {
|
||||
DrawerStatusHiddenBinding itemBinding = DrawerStatusHiddenBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new StatusViewHolder(itemBinding);
|
||||
} else {
|
||||
DrawerStatusReportBinding itemBinding = DrawerStatusReportBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new StatusViewHolder(itemBinding);
|
||||
if (!minified) {
|
||||
DrawerStatusBinding itemBinding = DrawerStatusBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new StatusViewHolder(itemBinding);
|
||||
} else {
|
||||
DrawerStatusReportBinding itemBinding = DrawerStatusReportBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new StatusViewHolder(itemBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1519,11 +1581,14 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||
if (viewHolder.getItemViewType() == 0) {
|
||||
return;
|
||||
}
|
||||
Status status = statusList.get(position);
|
||||
StatusViewHolder holder = (StatusViewHolder) viewHolder;
|
||||
StatusesVM statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get(StatusesVM.class);
|
||||
SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get(SearchVM.class);
|
||||
statusManagement(context, statusesVM, searchVM, holder, this, statusList, null, status, remote, minified);
|
||||
statusManagement(context, statusesVM, searchVM, holder, this, statusList, null, status, timelineType, minified);
|
||||
if (holder.timer != null) {
|
||||
holder.timer.cancel();
|
||||
holder.timer = null;
|
||||
|
@ -1557,6 +1622,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
|
||||
public static class StatusViewHolder extends RecyclerView.ViewHolder {
|
||||
DrawerStatusBinding binding;
|
||||
DrawerStatusHiddenBinding bindingHidden;
|
||||
DrawerStatusReportBinding bindingReport;
|
||||
DrawerStatusNotificationBinding bindingNotification;
|
||||
Timer timer;
|
||||
|
@ -1578,6 +1644,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
binding = itemView.status;
|
||||
}
|
||||
|
||||
StatusViewHolder(DrawerStatusHiddenBinding itemView) {
|
||||
super(itemView.getRoot());
|
||||
bindingHidden = itemView;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -93,8 +93,10 @@ public class FragmentMedia extends Fragment {
|
|||
|
||||
url = attachment.url;
|
||||
binding.mediaPicture.setOnMatrixChangeListener(rect -> {
|
||||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
canSwipe = (binding.mediaPicture.getScale() == 1);
|
||||
|
||||
if (!canSwipe) {
|
||||
if (!((MediaActivity) requireActivity()).getFullScreen()) {
|
||||
((MediaActivity) requireActivity()).setFullscreen(true);
|
||||
|
|
|
@ -39,6 +39,7 @@ import java.util.List;
|
|||
import app.fedilab.android.BaseMainActivity;
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.ContextActivity;
|
||||
import app.fedilab.android.client.entities.Timeline;
|
||||
import app.fedilab.android.client.mastodon.entities.Context;
|
||||
import app.fedilab.android.client.mastodon.entities.Status;
|
||||
import app.fedilab.android.databinding.FragmentPaginationBinding;
|
||||
|
@ -171,7 +172,7 @@ public class FragmentMastodonContext extends Fragment {
|
|||
this.statuses = new ArrayList<>();
|
||||
focusedStatus.isFocused = true;
|
||||
this.statuses.add(focusedStatus);
|
||||
statusAdapter = new StatusAdapter(this.statuses, false);
|
||||
statusAdapter = new StatusAdapter(this.statuses, Timeline.TimeLineEnum.UNKNOWN, false);
|
||||
binding.swipeContainer.setRefreshing(false);
|
||||
LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity());
|
||||
binding.recyclerView.setLayoutManager(mLayoutManager);
|
||||
|
|
|
@ -276,7 +276,7 @@ public class FragmentMastodonTimeline extends Fragment {
|
|||
min_id = statuses.pagination.min_id;
|
||||
}
|
||||
|
||||
statusAdapter = new StatusAdapter(this.statuses, timelineType == Timeline.TimeLineEnum.REMOTE, minified);
|
||||
statusAdapter = new StatusAdapter(this.statuses, timelineType, minified);
|
||||
|
||||
if (statusReport != null) {
|
||||
scrollToTop();
|
||||
|
@ -426,11 +426,12 @@ public class FragmentMastodonTimeline extends Fragment {
|
|||
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
|
||||
*/
|
||||
private void route(DIRECTION direction) {
|
||||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
QuickLoad quickLoad = new QuickLoad(requireActivity()).getSavedValue(timelineType, ident);
|
||||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
if (!binding.swipeContainer.isRefreshing() && direction == null && quickLoad != null && quickLoad.statuses != null && quickLoad.statuses.size() > 0) {
|
||||
Statuses statuses = new Statuses();
|
||||
statuses.statuses = quickLoad.statuses;
|
||||
|
@ -654,6 +655,16 @@ public class FragmentMastodonTimeline extends Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Refresh status in list
|
||||
*/
|
||||
public void refreshAllAdapters() {
|
||||
if (statusAdapter != null && statuses != null) {
|
||||
statusAdapter.notifyItemRangeChanged(0, statuses.size());
|
||||
}
|
||||
}
|
||||
|
||||
public enum DIRECTION {
|
||||
TOP,
|
||||
BOTTOM
|
||||
|
|
5
app/src/main/res/layout/drawer_status_hidden.xml
Normal file
5
app/src/main/res/layout/drawer_status_hidden.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:visibility="gone" />
|
14
app/src/main/res/layout/popup_filter_regex.xml
Normal file
14
app/src/main/res/layout/popup_filter_regex.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?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:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/filter_regex"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/filter_regex"
|
||||
android:inputType="text"
|
||||
android:singleLine="true" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
22
app/src/main/res/menu/option_filter_toots.xml
Normal file
22
app/src/main/res/menu/option_filter_toots.xml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<item
|
||||
android:id="@+id/action_show_boosts"
|
||||
android:checkable="true"
|
||||
android:title="@string/show_boosts"
|
||||
app:actionViewClass="android.widget.CheckBox"
|
||||
app:showAsAction="always"
|
||||
tools:ignore="AlwaysShowAction" />
|
||||
<item
|
||||
android:id="@+id/action_show_replies"
|
||||
android:checkable="true"
|
||||
android:title="@string/show_replies"
|
||||
app:actionViewClass="android.widget.CheckBox"
|
||||
app:showAsAction="always" />
|
||||
<item
|
||||
android:id="@+id/action_filter"
|
||||
android:title="@string/filter_regex"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
Loading…
Reference in a new issue