Notifications M3 redesign (+ read marker support)
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Fragment;
|
||||
import android.app.NotificationManager;
|
||||
import android.graphics.Outline;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -14,14 +16,23 @@ import android.view.WindowInsets;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.otto.Subscribe;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.PushNotificationReceiver;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.markers.GetMarkers;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.NotificationsMarkerUpdatedEvent;
|
||||
import org.joinmastodon.android.fragments.discover.DiscoverFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.OnboardingFollowSuggestionsFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.model.PaginatedResponse;
|
||||
import org.joinmastodon.android.model.TimelineMarkers;
|
||||
import org.joinmastodon.android.ui.AccountSwitcherSheet;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
@@ -29,11 +40,14 @@ import org.joinmastodon.android.ui.views.TabBar;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import me.grishka.appkit.FragmentStackActivity;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.AppKitFragment;
|
||||
import me.grishka.appkit.fragments.LoaderFragment;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
@@ -45,7 +59,7 @@ import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||
public class HomeFragment extends AppKitFragment implements OnBackPressedListener{
|
||||
private FragmentRootLinearLayout content;
|
||||
private HomeTimelineFragment homeTimelineFragment;
|
||||
private NotificationsFragment notificationsFragment;
|
||||
private NotificationsListFragment notificationsFragment;
|
||||
private DiscoverFragment searchFragment;
|
||||
private ProfileFragment profileFragment;
|
||||
private TabBar tabBar;
|
||||
@@ -53,6 +67,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
private ImageView tabBarAvatar;
|
||||
@IdRes
|
||||
private int currentTab=R.id.tab_home;
|
||||
private TextView notificationsBadge;
|
||||
|
||||
private String accountID;
|
||||
|
||||
@@ -74,7 +89,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
args.putBoolean("noAutoLoad", true);
|
||||
searchFragment=new DiscoverFragment();
|
||||
searchFragment.setArguments(args);
|
||||
notificationsFragment=new NotificationsFragment();
|
||||
notificationsFragment=new NotificationsListFragment();
|
||||
notificationsFragment.setArguments(args);
|
||||
args=new Bundle(args);
|
||||
args.putParcelable("profileAccount", Parcels.wrap(AccountSessionManager.getInstance().getAccount(accountID).self));
|
||||
@@ -83,6 +98,13 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
profileFragment.setArguments(args);
|
||||
}
|
||||
|
||||
E.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy(){
|
||||
super.onDestroy();
|
||||
E.unregister(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -106,6 +128,9 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
||||
ViewImageLoader.loadWithoutAnimation(tabBarAvatar, null, new UrlImageLoaderRequest(self.avatar, V.dp(24), V.dp(24)));
|
||||
|
||||
notificationsBadge=tabBar.findViewById(R.id.notifications_badge);
|
||||
notificationsBadge.setVisibility(View.GONE);
|
||||
|
||||
if(savedInstanceState==null){
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.add(R.id.fragment_wrap, homeTimelineFragment)
|
||||
@@ -138,7 +163,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
return;
|
||||
homeTimelineFragment=(HomeTimelineFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTimelineFragment");
|
||||
searchFragment=(DiscoverFragment) getChildFragmentManager().getFragment(savedInstanceState, "searchFragment");
|
||||
notificationsFragment=(NotificationsFragment) getChildFragmentManager().getFragment(savedInstanceState, "notificationsFragment");
|
||||
notificationsFragment=(NotificationsListFragment) getChildFragmentManager().getFragment(savedInstanceState, "notificationsFragment");
|
||||
profileFragment=(ProfileFragment) getChildFragmentManager().getFragment(savedInstanceState, "profileFragment");
|
||||
currentTab=savedInstanceState.getInt("selectedTab");
|
||||
tabBar.selectTab(currentTab);
|
||||
@@ -224,9 +249,8 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
lf.loadData();
|
||||
}else if(newFragment instanceof DiscoverFragment){
|
||||
((DiscoverFragment) newFragment).loadData();
|
||||
}else if(newFragment instanceof NotificationsFragment){
|
||||
((NotificationsFragment) newFragment).loadData();
|
||||
// TODO make an interface?
|
||||
}
|
||||
if(newFragment instanceof NotificationsListFragment){
|
||||
NotificationManager nm=getActivity().getSystemService(NotificationManager.class);
|
||||
nm.cancel(accountID, PushNotificationReceiver.NOTIFICATION_ID);
|
||||
}
|
||||
@@ -267,4 +291,62 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
|
||||
getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShown(){
|
||||
super.onShown();
|
||||
reloadNotificationsForUnreadCount();
|
||||
}
|
||||
|
||||
private void reloadNotificationsForUnreadCount(){
|
||||
List<Notification>[] notifications=new List[]{null};
|
||||
String[] marker={null};
|
||||
|
||||
AccountSessionManager.get(accountID).reloadNotificationsMarker(m->{
|
||||
marker[0]=m;
|
||||
if(notifications[0]!=null){
|
||||
updateUnreadCount(notifications[0], marker[0]);
|
||||
}
|
||||
});
|
||||
|
||||
AccountSessionManager.get(accountID).getCacheController().getNotifications(null, 40, false, true, new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(PaginatedResponse<List<Notification>> result){
|
||||
notifications[0]=result.items;
|
||||
if(marker[0]!=null)
|
||||
updateUnreadCount(notifications[0], marker[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private void updateUnreadCount(List<Notification> notifications, String marker){
|
||||
if(notifications.isEmpty() || notifications.get(0).id.compareTo(marker)<=0){
|
||||
notificationsBadge.setVisibility(View.GONE);
|
||||
}else{
|
||||
notificationsBadge.setVisibility(View.VISIBLE);
|
||||
if(notifications.get(notifications.size()-1).id.compareTo(marker)<=0){
|
||||
notificationsBadge.setText(String.format("%d+", notifications.size()));
|
||||
}else{
|
||||
int count=0;
|
||||
for(Notification n:notifications){
|
||||
if(n.id.equals(marker))
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
notificationsBadge.setText(String.format("%d", count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onNotificationsMarkerUpdated(NotificationsMarkerUpdatedEvent ev){
|
||||
if(!ev.accountID.equals(accountID))
|
||||
return;
|
||||
if(ev.clearUnread)
|
||||
notificationsBadge.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.TimelineMarkers;
|
||||
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
@@ -154,7 +155,7 @@ public class HomeTimelineFragment extends StatusListFragment{
|
||||
new SaveMarkers(topPostID, null)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(SaveMarkers.Response result){
|
||||
public void onSuccess(TimelineMarkers result){
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.fragments.ToolbarFragment;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class NotificationsFragment extends MastodonToolbarFragment implements ScrollableToTop{
|
||||
|
||||
private TabLayout tabLayout;
|
||||
private ViewPager2 pager;
|
||||
private FrameLayout[] tabViews;
|
||||
private TabLayoutMediator tabLayoutMediator;
|
||||
|
||||
private NotificationsListFragment allNotificationsFragment, mentionsFragment;
|
||||
|
||||
private String accountID;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
||||
setRetainInstance(true);
|
||||
|
||||
accountID=getArguments().getString("account");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity){
|
||||
super.onAttach(activity);
|
||||
setTitle(R.string.notifications);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||
LinearLayout view=(LinearLayout) inflater.inflate(R.layout.fragment_notifications, container, false);
|
||||
|
||||
tabLayout=view.findViewById(R.id.tabbar);
|
||||
pager=view.findViewById(R.id.pager);
|
||||
|
||||
tabViews=new FrameLayout[2];
|
||||
for(int i=0;i<tabViews.length;i++){
|
||||
FrameLayout tabView=new FrameLayout(getActivity());
|
||||
tabView.setId(switch(i){
|
||||
case 0 -> R.id.notifications_all;
|
||||
case 1 -> R.id.notifications_mentions;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+i);
|
||||
});
|
||||
tabView.setVisibility(View.GONE);
|
||||
view.addView(tabView); // needed so the fragment manager will have somewhere to restore the tab fragment
|
||||
tabViews[i]=tabView;
|
||||
}
|
||||
|
||||
tabLayout.setTabTextSize(V.dp(16));
|
||||
tabLayout.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorTabInactive), UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
||||
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
scrollToTop();
|
||||
}
|
||||
});
|
||||
|
||||
pager.setOffscreenPageLimit(4);
|
||||
pager.setAdapter(new DiscoverPagerAdapter());
|
||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
||||
@Override
|
||||
public void onPageSelected(int position){
|
||||
if(position==0)
|
||||
return;
|
||||
Fragment _page=getFragmentForPage(position);
|
||||
if(_page instanceof BaseRecyclerFragment<?> page){
|
||||
if(!page.loaded && !page.isDataLoading())
|
||||
page.loadData();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(allNotificationsFragment==null){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putBoolean("__is_tab", true);
|
||||
|
||||
allNotificationsFragment=new NotificationsListFragment();
|
||||
allNotificationsFragment.setArguments(args);
|
||||
|
||||
args=new Bundle(args);
|
||||
args.putBoolean("onlyMentions", true);
|
||||
mentionsFragment=new NotificationsListFragment();
|
||||
mentionsFragment.setArguments(args);
|
||||
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.add(R.id.notifications_all, allNotificationsFragment)
|
||||
.add(R.id.notifications_mentions, mentionsFragment)
|
||||
.commit();
|
||||
}
|
||||
|
||||
tabLayoutMediator=new TabLayoutMediator(tabLayout, pager, new TabLayoutMediator.TabConfigurationStrategy(){
|
||||
@Override
|
||||
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
|
||||
tab.setText(switch(position){
|
||||
case 0 -> R.string.all_notifications;
|
||||
case 1 -> R.string.mentions;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+position);
|
||||
});
|
||||
tab.view.textView.setAllCaps(true);
|
||||
}
|
||||
});
|
||||
tabLayoutMediator.attach();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollToTop(){
|
||||
getFragmentForPage(pager.getCurrentItem()).scrollToTop();
|
||||
}
|
||||
|
||||
public void loadData(){
|
||||
if(allNotificationsFragment!=null && !allNotificationsFragment.loaded && !allNotificationsFragment.dataLoading)
|
||||
allNotificationsFragment.loadData();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateToolbar(){
|
||||
super.updateToolbar();
|
||||
getToolbar().setOutlineProvider(null);
|
||||
getToolbar().setOnClickListener(v->scrollToTop());
|
||||
}
|
||||
|
||||
private NotificationsListFragment getFragmentForPage(int page){
|
||||
return switch(page){
|
||||
case 0 -> allNotificationsFragment;
|
||||
case 1 -> mentionsFragment;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||
};
|
||||
}
|
||||
|
||||
private class DiscoverPagerAdapter extends RecyclerView.Adapter<SimpleViewHolder>{
|
||||
@NonNull
|
||||
@Override
|
||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
FrameLayout view=tabViews[viewType];
|
||||
((ViewGroup)view.getParent()).removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return new SimpleViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position){
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import com.squareup.otto.Subscribe;
|
||||
@@ -15,33 +23,44 @@ import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.model.PaginatedResponse;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.displayitems.NotificationHeaderStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.NestedRecyclerScrollView;
|
||||
import org.joinmastodon.android.utils.ObjectIdComparator;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
|
||||
private boolean onlyMentions;
|
||||
private boolean onlyMentions=true;
|
||||
private String maxID;
|
||||
private View tabBar;
|
||||
private View mentionsTab, allTab;
|
||||
private View endMark;
|
||||
private String unreadMarker, realUnreadMarker;
|
||||
private MenuItem markAllReadItem;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
setLayout(R.layout.fragment_notifications);
|
||||
E.register(this);
|
||||
if(savedInstanceState!=null){
|
||||
onlyMentions=savedInstanceState.getBoolean("onlyMentions", true);
|
||||
}
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,28 +72,29 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
@Override
|
||||
public void onAttach(Activity activity){
|
||||
super.onAttach(activity);
|
||||
onlyMentions=getArguments().getBoolean("onlyMentions", false);
|
||||
setTitle(R.string.notifications);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
|
||||
String extraText=switch(n.type){
|
||||
case FOLLOW -> getString(R.string.user_followed_you);
|
||||
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
|
||||
case MENTION, STATUS -> null;
|
||||
case REBLOG -> getString(R.string.notification_boosted);
|
||||
case FAVORITE -> getString(R.string.user_favorited);
|
||||
case POLL -> getString(R.string.poll_ended);
|
||||
};
|
||||
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, null, extraText) : null;
|
||||
NotificationHeaderStatusDisplayItem titleItem;
|
||||
if(n.type==Notification.Type.MENTION || n.type==Notification.Type.STATUS){
|
||||
titleItem=null;
|
||||
}else{
|
||||
titleItem=new NotificationHeaderStatusDisplayItem(n.id, this, n, accountID);
|
||||
if(n.status!=null){
|
||||
n.status.card=null;
|
||||
n.status.spoilerText=null;
|
||||
}
|
||||
}
|
||||
if(n.status!=null){
|
||||
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null);
|
||||
int flags=titleItem==null ? 0 : (StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET | StatusDisplayItem.FLAG_NO_HEADER);
|
||||
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, flags);
|
||||
if(titleItem!=null)
|
||||
items.add(0, titleItem);
|
||||
return items;
|
||||
}else if(titleItem!=null){
|
||||
AccountCardStatusDisplayItem card=new AccountCardStatusDisplayItem(n.id, this, n.account);
|
||||
return Arrays.asList(titleItem, card);
|
||||
return Collections.singletonList(titleItem);
|
||||
}else{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -90,6 +110,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
endMark.setVisibility(View.GONE);
|
||||
AccountSessionManager.getInstance()
|
||||
.getAccount(accountID).getCacheController()
|
||||
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, refreshing, new SimpleCallback<>(this){
|
||||
@@ -97,39 +118,23 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
public void onSuccess(PaginatedResponse<List<Notification>> result){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
if(refreshing)
|
||||
relationships.clear();
|
||||
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
|
||||
Set<String> needRelationships=result.items.stream()
|
||||
.filter(ntf->ntf.status==null && !relationships.containsKey(ntf.account.id))
|
||||
.map(ntf->ntf.account.id)
|
||||
.collect(Collectors.toSet());
|
||||
loadRelationships(needRelationships);
|
||||
maxID=result.maxID;
|
||||
|
||||
if(offset==0 && !result.items.isEmpty()){
|
||||
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
|
||||
}
|
||||
endMark.setVisibility(result.items.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRelationshipsLoaded(){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
for(int i=0;i<list.getChildCount();i++){
|
||||
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||
if(holder instanceof AccountCardStatusDisplayItem.Holder accountHolder)
|
||||
accountHolder.rebind();
|
||||
}
|
||||
protected void onShown(){
|
||||
super.onShown();
|
||||
unreadMarker=realUnreadMarker=AccountSessionManager.get(accountID).getLastKnownNotificationsMarker();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShown(){
|
||||
super.onShown();
|
||||
// if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
||||
// loadData();
|
||||
protected void onHidden(){
|
||||
super.onHidden();
|
||||
resetUnreadBackground();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -153,8 +158,62 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
tabBar=view.findViewById(R.id.tabbar);
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
||||
|
||||
View tabBarItself=view.findViewById(R.id.tabbar_inner);
|
||||
tabBarItself.setOutlineProvider(OutlineProviders.roundedRect(20));
|
||||
tabBarItself.setClipToOutline(true);
|
||||
|
||||
mentionsTab=view.findViewById(R.id.mentions_tab);
|
||||
allTab=view.findViewById(R.id.all_tab);
|
||||
mentionsTab.setOnClickListener(this::onTabClick);
|
||||
allTab.setOnClickListener(this::onTabClick);
|
||||
mentionsTab.setSelected(onlyMentions);
|
||||
allTab.setSelected(!onlyMentions);
|
||||
|
||||
NestedRecyclerScrollView scroller=view.findViewById(R.id.scroller);
|
||||
scroller.setScrollableChildSupplier(()->list);
|
||||
scroller.setTakePriorityOverChildViews(true);
|
||||
|
||||
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
||||
private Paint paint=new Paint();
|
||||
private Rect tmpRect=new Rect();
|
||||
|
||||
{
|
||||
paint.setColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3SurfaceVariant));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||
if(TextUtils.isEmpty(unreadMarker))
|
||||
return;
|
||||
for(int i=0;i<parent.getChildCount();i++){
|
||||
View child=parent.getChildAt(i);
|
||||
if(parent.getChildViewHolder(child) instanceof StatusDisplayItem.Holder<?> holder){
|
||||
String itemID=holder.getItemID();
|
||||
if(ObjectIdComparator.INSTANCE.compare(itemID, unreadMarker)>0){
|
||||
parent.getDecoratedBoundsWithMargins(child, tmpRect);
|
||||
c.drawRect(tmpRect, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<View> getViewsForElevationEffect(){
|
||||
ArrayList<View> views=new ArrayList<>(super.getViewsForElevationEffect());
|
||||
views.add(tabBar);
|
||||
return views;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState){
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean("onlyMentions", onlyMentions);
|
||||
}
|
||||
|
||||
private Notification getNotificationByID(String id){
|
||||
@@ -211,4 +270,85 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
displayItems.subList(index, lastIndex).clear();
|
||||
adapter.notifyItemRangeRemoved(index, lastIndex-index);
|
||||
}
|
||||
|
||||
private void onTabClick(View v){
|
||||
boolean newOnlyMentions=v.getId()==R.id.mentions_tab;
|
||||
if(newOnlyMentions==onlyMentions)
|
||||
return;
|
||||
onlyMentions=newOnlyMentions;
|
||||
mentionsTab.setSelected(onlyMentions);
|
||||
allTab.setSelected(!onlyMentions);
|
||||
maxID=null;
|
||||
showProgress();
|
||||
loadData(0, 20);
|
||||
refreshing=true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View onCreateFooterView(LayoutInflater inflater){
|
||||
View v=inflater.inflate(R.layout.load_more_with_end_mark, null);
|
||||
endMark=v.findViewById(R.id.end_mark);
|
||||
endMark.setVisibility(View.GONE);
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needDividerForExtraItem(View child, View bottomSibling, RecyclerView.ViewHolder holder, RecyclerView.ViewHolder siblingHolder){
|
||||
return super.needDividerForExtraItem(child, bottomSibling, holder, siblingHolder) || (siblingHolder!=null && siblingHolder.getAbsoluteAdapterPosition()>=adapter.getItemCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||
inflater.inflate(R.menu.notifications, menu);
|
||||
markAllReadItem=menu.findItem(R.id.mark_all_read);
|
||||
updateMarkAllReadButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item){
|
||||
if(item.getItemId()==R.id.mark_all_read){
|
||||
markAsRead();
|
||||
resetUnreadBackground();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void markAsRead(){
|
||||
String id=data.get(0).id;
|
||||
new SaveMarkers(null, id).exec(accountID);
|
||||
AccountSessionManager.get(accountID).setNotificationsMarker(id, true);
|
||||
realUnreadMarker=id;
|
||||
updateMarkAllReadButton();
|
||||
}
|
||||
|
||||
private void resetUnreadBackground(){
|
||||
unreadMarker=realUnreadMarker;
|
||||
list.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh(){
|
||||
super.onRefresh();
|
||||
resetUnreadBackground();
|
||||
AccountSessionManager.get(accountID).reloadNotificationsMarker(m->{
|
||||
unreadMarker=realUnreadMarker=m;
|
||||
});
|
||||
}
|
||||
|
||||
private void updateMarkAllReadButton(){
|
||||
markAllReadItem.setEnabled(!data.isEmpty() && !realUnreadMarker.equals(data.get(0).id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppendItems(List<Notification> items){
|
||||
super.onAppendItems(items);
|
||||
if(data.isEmpty() || data.get(0).id.equals(realUnreadMarker))
|
||||
return;
|
||||
for(Notification n:items){
|
||||
if(ObjectIdComparator.INSTANCE.compare(n.id, realUnreadMarker)<=0){
|
||||
markAsRead();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user