Commit e2be4871 authored by Thomas's avatar Thomas
Browse files

Add counter for new messages

parent 204cb84d
Loading
Loading
Loading
Loading
+58 −1
Original line number Diff line number Diff line
@@ -148,7 +148,7 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public abstract class BaseMainActivity extends BaseActivity implements NetworkStateReceiver.NetworkStateReceiverListener {
public abstract class BaseMainActivity extends BaseActivity implements NetworkStateReceiver.NetworkStateReceiverListener, FragmentMastodonTimeline.UpdateCounters {

    public static String currentInstance, currentToken, currentUserID, client_id, client_secret, software;
    public static HashMap<String, List<Emoji>> emojis = new HashMap<>();
@@ -1082,6 +1082,63 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
        }
    }

    @Override
    public void onUpdate(int count, Timeline.TimeLineEnum type, String slug) {
        SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this);
        boolean singleBar = sharedpreferences.getBoolean(getString(R.string.SET_USE_SINGLE_TOPBAR), false);
        if (!singleBar) {
            switch (type) {
                case HOME:
                    if (count > 0) {
                        binding.bottomNavView.getOrCreateBadge(R.id.nav_home).setNumber(count);
                        binding.bottomNavView.getBadge(R.id.nav_home).setBackgroundColor(ContextCompat.getColor(BaseMainActivity.this, R.color.cyanea_accent_reference));
                        binding.bottomNavView.getBadge(R.id.nav_home).setBadgeTextColor(ThemeHelper.getAttColor(BaseMainActivity.this, R.attr.mTextColor));
                    } else {
                        binding.bottomNavView.removeBadge(R.id.nav_home);
                    }
                    break;
                case LOCAL:
                    if (count > 0) {
                        binding.bottomNavView.getOrCreateBadge(R.id.nav_local).setNumber(count);
                        binding.bottomNavView.getBadge(R.id.nav_local).setBackgroundColor(ContextCompat.getColor(BaseMainActivity.this, R.color.cyanea_accent_reference));
                        binding.bottomNavView.getBadge(R.id.nav_local).setBadgeTextColor(ThemeHelper.getAttColor(BaseMainActivity.this, R.attr.mTextColor));
                    } else {
                        binding.bottomNavView.removeBadge(R.id.nav_local);
                    }
                    break;
                case PUBLIC:
                    if (count > 0) {
                        binding.bottomNavView.getOrCreateBadge(R.id.nav_public).setNumber(count);
                        binding.bottomNavView.getBadge(R.id.nav_public).setBackgroundColor(ContextCompat.getColor(BaseMainActivity.this, R.color.cyanea_accent_reference));
                        binding.bottomNavView.getBadge(R.id.nav_public).setBadgeTextColor(ThemeHelper.getAttColor(BaseMainActivity.this, R.attr.mTextColor));
                    } else {
                        binding.bottomNavView.removeBadge(R.id.nav_public);
                    }
                    break;
                case NOTIFICATION:
                    if (count > 0) {
                        binding.bottomNavView.getOrCreateBadge(R.id.nav_notifications).setNumber(count);
                        binding.bottomNavView.getBadge(R.id.nav_notifications).setBackgroundColor(ContextCompat.getColor(BaseMainActivity.this, R.color.cyanea_accent_reference));
                        binding.bottomNavView.getBadge(R.id.nav_notifications).setBadgeTextColor(ThemeHelper.getAttColor(BaseMainActivity.this, R.attr.mTextColor));
                    } else {
                        binding.bottomNavView.removeBadge(R.id.nav_notifications);
                    }
                    break;
                case DIRECT:
                    if (count > 0) {
                        binding.bottomNavView.getOrCreateBadge(R.id.nav_privates).setNumber(count);
                        binding.bottomNavView.getBadge(R.id.nav_privates).setBackgroundColor(ContextCompat.getColor(BaseMainActivity.this, R.color.cyanea_accent_reference));
                        binding.bottomNavView.getBadge(R.id.nav_privates).setBadgeTextColor(ThemeHelper.getAttColor(BaseMainActivity.this, R.attr.mTextColor));
                    } else {
                        binding.bottomNavView.removeBadge(R.id.nav_privates);
                    }
                    break;
            }
        }


    }

    @Override
    protected void onDestroy() {
        LocalBroadcastManager.getInstance(BaseMainActivity.this).unregisterReceiver(broadcast_data);
+160 −139
Original line number Diff line number Diff line
@@ -77,6 +77,14 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
    private StatusAdapter statusAdapter;
    private Timeline.TimeLineEnum timelineType;
    private List<Status> timelineStatuses;
    public UpdateCounters update;

    @Override
    public void onResume() {
        super.onResume();
        route(DIRECTION.FETCH_NEW, true);
    }

    //Handle actions that can be done in other fragments
    private final BroadcastReceiver receive_action = new BroadcastReceiver() {
        @Override
@@ -263,6 +271,81 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
        return binding.getRoot();
    }

    /**
     * Update view and pagination when scrolling down
     *
     * @param fetched_statuses Statuses
     */
    private synchronized void dealWithPagination(Statuses fetched_statuses, DIRECTION direction, boolean fetchingMissing, Status statusToUpdate) {
        if (binding == null || !isAdded() || getActivity() == null) {
            return;
        }
        binding.swipeContainer.setRefreshing(false);
        binding.loadingNextElements.setVisibility(View.GONE);
        flagLoading = false;
        if (timelineStatuses != null && fetched_statuses != null && fetched_statuses.statuses != null && fetched_statuses.statuses.size() > 0) {
            try {
                if (statusToUpdate != null) {
                    new Thread(() -> {
                        StatusCache statusCache = new StatusCache();
                        statusCache.instance = BaseMainActivity.currentInstance;
                        statusCache.user_id = BaseMainActivity.currentUserID;
                        statusToUpdate.isFetchMore = false;
                        statusCache.status = statusToUpdate;
                        statusCache.status_id = statusToUpdate.id;
                        try {
                            new StatusCache(requireActivity()).updateIfExists(statusCache);
                        } catch (DBException e) {
                            e.printStackTrace();
                        }
                    }).start();
                }
            } catch (Exception ignored) {
            }

            flagLoading = fetched_statuses.pagination.max_id == null;
            binding.noAction.setVisibility(View.GONE);
            if (timelineType == Timeline.TimeLineEnum.ART) {
                //We have to split media in different statuses
                List<Status> mediaStatuses = new ArrayList<>();
                for (Status status : fetched_statuses.statuses) {
                    if (status.media_attachments.size() > 1) {
                        for (Attachment attachment : status.media_attachments) {
                            status.media_attachments = new ArrayList<>();
                            status.media_attachments.add(0, attachment);
                            mediaStatuses.add(status);
                        }
                    }
                }
                fetched_statuses.statuses = mediaStatuses;
            }
            //Update the timeline with new statuses
            int insertedStatus = updateStatusListWith(fetched_statuses.statuses);

            //For these directions, the app will display counters for new messages
            if (insertedStatus >= 0 && (direction == DIRECTION.FETCH_NEW || direction == DIRECTION.SCROLL_TOP)) {
                update.onUpdate(insertedStatus, timelineType, slug);
            }
            if (direction == DIRECTION.TOP && fetchingMissing) {
                binding.recyclerView.scrollToPosition(getPosition(fetched_statuses.statuses.get(fetched_statuses.statuses.size() - 1)) + 1);
            }
            if (!fetchingMissing) {
                if (fetched_statuses.pagination.max_id == null) {
                    flagLoading = true;
                } else if (max_id == null || fetched_statuses.pagination.max_id.compareTo(max_id) < 0) {
                    max_id = fetched_statuses.pagination.max_id;
                }
                if (min_id == null || (fetched_statuses.pagination.min_id != null && fetched_statuses.pagination.min_id.compareTo(min_id) > 0)) {
                    min_id = fetched_statuses.pagination.min_id;
                }
            }
        } else if (direction == DIRECTION.BOTTOM) {
            flagLoading = true;
        }
        if (direction == DIRECTION.SCROLL_TOP) {
            binding.recyclerView.scrollToPosition(0);
        }
    }

    /**
     * Intialize the common view for statuses on different timelines
@@ -377,91 +460,13 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
        }
    }

    /**
     * Update view and pagination when scrolling down
     *
     * @param fetched_statuses Statuses
     */
    private synchronized void dealWithPagination(Statuses fetched_statuses, DIRECTION direction, boolean fetchingMissing, Status statusToUpdate) {
        if (binding == null || !isAdded() || getActivity() == null) {
            return;
        }
        binding.swipeContainer.setRefreshing(false);
        binding.loadingNextElements.setVisibility(View.GONE);
        flagLoading = false;
        if (timelineStatuses != null && fetched_statuses != null && fetched_statuses.statuses != null && fetched_statuses.statuses.size() > 0) {
            try {
                if (statusToUpdate != null) {
                    new Thread(() -> {
                        StatusCache statusCache = new StatusCache();
                        statusCache.instance = BaseMainActivity.currentInstance;
                        statusCache.user_id = BaseMainActivity.currentUserID;
                        statusToUpdate.isFetchMore = false;
                        statusCache.status = statusToUpdate;
                        statusCache.status_id = statusToUpdate.id;
                        try {
                            new StatusCache(requireActivity()).updateIfExists(statusCache);
                        } catch (DBException e) {
                            e.printStackTrace();
                        }
                    }).start();
                }
            } catch (Exception ignored) {
            }
            flagLoading = fetched_statuses.pagination.max_id == null;
            binding.noAction.setVisibility(View.GONE);
            if (timelineType == Timeline.TimeLineEnum.ART) {
                //We have to split media in different statuses
                List<Status> mediaStatuses = new ArrayList<>();
                for (Status status : fetched_statuses.statuses) {
                    if (status.media_attachments.size() > 1) {
                        for (Attachment attachment : status.media_attachments) {
                            status.media_attachments = new ArrayList<>();
                            status.media_attachments.add(0, attachment);
                            mediaStatuses.add(status);
                        }
                    }
                }
                fetched_statuses.statuses = mediaStatuses;
            }
            //Update the timeline with new statuses
            updateStatusListWith(fetched_statuses.statuses);
            if (direction == DIRECTION.TOP && fetchingMissing) {
                binding.recyclerView.scrollToPosition(getPosition(fetched_statuses.statuses.get(fetched_statuses.statuses.size() - 1)) + 1);
            }
            if (!fetchingMissing) {
                if (fetched_statuses.pagination.max_id == null) {
                    flagLoading = true;
                } else if (max_id == null || fetched_statuses.pagination.max_id.compareTo(max_id) < 0) {
                    max_id = fetched_statuses.pagination.max_id;
                }
                if (min_id == null || (fetched_statuses.pagination.min_id != null && fetched_statuses.pagination.min_id.compareTo(min_id) > 0)) {
                    min_id = fetched_statuses.pagination.min_id;
                }
            }
        } else if (direction == DIRECTION.BOTTOM) {
            flagLoading = true;
        }
        if (direction == DIRECTION.SCROLL_TOP) {
            binding.recyclerView.scrollToPosition(0);
        }
    }

    /**
     * Update view and pagination when scrolling down
     *
     * @param fetched_statuses Statuses
     */
    private synchronized void dealWithPagination(Statuses fetched_statuses, DIRECTION direction, boolean fetchingMissing) {
        dealWithPagination(fetched_statuses, direction, fetchingMissing, null);
    }

    /**
     * Update the timeline with received statuses
     *
     * @param statusListReceived - List<Status> Statuses received
     */
    private void updateStatusListWith(List<Status> statusListReceived) {
    private int updateStatusListWith(List<Status> statusListReceived) {
        int insertedStatus = 0;
        if (statusListReceived != null && statusListReceived.size() > 0) {
            for (Status statusReceived : statusListReceived) {
                int position = 0;
@@ -477,6 +482,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
                            if (!timelineStatuses.contains(statusReceived) && !statusReceived.pinned && timelineType != Timeline.TimeLineEnum.ACCOUNT_TIMELINE) {
                                timelineStatuses.add(position, statusReceived);
                                statusAdapter.notifyItemInserted(position);
                                insertedStatus++;
                            }
                            break;
                        }
@@ -491,56 +497,16 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
                }
            }
        }
        return insertedStatus;
    }

    @Override
    public void onPause() {
        storeMarker();
        super.onPause();
    }

    @Override
    public void onDestroyView() {
        //Update last read id for home timeline
        if (isAdded()) {
            storeMarker();
        }
        LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(receive_action);
        super.onDestroyView();
    }


    private void storeMarker() {
        if (timelineType == Timeline.TimeLineEnum.HOME && mLayoutManager != null) {
            int position = mLayoutManager.findFirstVisibleItemPosition();
            if (timelineStatuses != null && timelineStatuses.size() > position) {
                try {
                    Status status = timelineStatuses.get(position);
                    SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
                    SharedPreferences.Editor editor = sharedpreferences.edit();
                    editor.putString(getString(R.string.SET_INNER_MARKER) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance + slug, status.id);
                    editor.apply();
                    timelinesVM.addMarker(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, status.id, null);

                } catch (Exception ignored) {
                }
            }
        }
    }

    private void router(DIRECTION direction) {
        if (networkAvailable == BaseMainActivity.status.UNKNOWN) {
            new Thread(() -> {
                if (networkAvailable == BaseMainActivity.status.UNKNOWN) {
                    networkAvailable = Helper.isConnectedToInternet(requireActivity(), BaseMainActivity.currentInstance);
                }
                Handler mainHandler = new Handler(Looper.getMainLooper());
                Runnable myRunnable = () -> route(direction, false);
                mainHandler.post(myRunnable);
            }).start();
        } else {
            route(direction, false);
        }
    /**
     * Update view and pagination when scrolling down
     *
     * @param fetched_statuses Statuses
     */
    private synchronized void dealWithPagination(Statuses fetched_statuses, DIRECTION direction, boolean fetchingMissing) {
        dealWithPagination(fetched_statuses, direction, fetchingMissing, null);
    }

    /**
@@ -600,7 +566,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
        }
        SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
        boolean useCache = sharedpreferences.getBoolean(getString(R.string.SET_USE_CACHE), true);
        if (useCache) {
        if (useCache && direction != DIRECTION.SCROLL_TOP && direction != DIRECTION.FETCH_NEW) {
            getCachedStatus(direction, fetchingMissing, timelineParams);
        } else {
            getLiveStatus(direction, fetchingMissing, timelineParams, status);
@@ -608,6 +574,56 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.

    }

    @Override
    public void onPause() {
        storeMarker();
        super.onPause();
    }

    @Override
    public void onDestroyView() {
        //Update last read id for home timeline
        if (isAdded()) {
            storeMarker();
        }
        LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(receive_action);
        super.onDestroyView();
    }


    private void storeMarker() {
        if (timelineType == Timeline.TimeLineEnum.HOME && mLayoutManager != null) {
            int position = mLayoutManager.findFirstVisibleItemPosition();
            if (timelineStatuses != null && timelineStatuses.size() > position) {
                try {
                    Status status = timelineStatuses.get(position);
                    SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
                    SharedPreferences.Editor editor = sharedpreferences.edit();
                    editor.putString(getString(R.string.SET_INNER_MARKER) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance + slug, status.id);
                    editor.apply();
                    timelinesVM.addMarker(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, status.id, null);

                } catch (Exception ignored) {
                }
            }
        }
    }

    private void router(DIRECTION direction) {
        if (networkAvailable == BaseMainActivity.status.UNKNOWN) {
            new Thread(() -> {
                if (networkAvailable == BaseMainActivity.status.UNKNOWN) {
                    networkAvailable = Helper.isConnectedToInternet(requireActivity(), BaseMainActivity.currentInstance);
                }
                Handler mainHandler = new Handler(Looper.getMainLooper());
                Runnable myRunnable = () -> route(direction, false);
                mainHandler.post(myRunnable);
            }).start();
        } else {
            route(direction, false);
        }
    }

    private void getCachedStatus(DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams) {
        if (direction == null) {
            timelinesVM.getTimelineCache(timelineStatuses, timelineParams)
@@ -637,11 +653,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
                        }

                    });
        } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
            timelinesVM.getTimeline(timelineStatuses, timelineParams)
        } else if (direction == DIRECTION.REFRESH) {
            timelinesVM.getTimelineCache(timelineStatuses, timelineParams)
                    .observe(getViewLifecycleOwner(), statusesRefresh -> {
                        if (statusesRefresh == null || statusesRefresh.statuses == null || statusesRefresh.statuses.size() == 0) {
                            getCachedStatus(direction, fetchingMissing, timelineParams);
                            getLiveStatus(direction, fetchingMissing, timelineParams, null);
                        } else {
                            if (statusAdapter != null) {
                                dealWithPagination(statusesRefresh, direction, true);
@@ -653,7 +669,6 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
        }
    }


    private void getLiveStatus(DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams, Status status) {
        if (direction == null) {
            timelinesVM.getTimeline(timelineStatuses, timelineParams)
@@ -664,7 +679,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
        } else if (direction == DIRECTION.TOP) {
            timelinesVM.getTimeline(timelineStatuses, timelineParams)
                    .observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, fetchingMissing, status));
        } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
        } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP || direction == DIRECTION.FETCH_NEW) {
            timelinesVM.getTimeline(timelineStatuses, timelineParams)
                    .observe(getViewLifecycleOwner(), statusesRefresh -> {
                        if (statusAdapter != null) {
@@ -677,6 +692,15 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
    }


    public enum DIRECTION {
        TOP,
        BOTTOM,
        REFRESH,
        SCROLL_TOP,
        FETCH_NEW
    }


    /**
     * Router for timelines
     *
@@ -887,10 +911,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
        route(DIRECTION.BOTTOM, true, statusToUpdate);
    }

    public enum DIRECTION {
        TOP,
        BOTTOM,
        REFRESH,
        SCROLL_TOP
    public interface UpdateCounters {
        void onUpdate(int count, Timeline.TimeLineEnum type, String slug);
    }
}
 No newline at end of file
+3 −0
Original line number Diff line number Diff line
@@ -46,10 +46,12 @@ public class FedilabPageAdapter extends FragmentStatePagerAdapter {
    private final int toRemove;
    private final boolean singleBar;
    private Fragment mCurrentFragment;
    private final BaseMainActivity activity;

    public FedilabPageAdapter(BaseMainActivity activity, FragmentManager fm, Pinned pinned, BottomMenu bottomMenu) {
        super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        this.pinned = pinned;
        this.activity = activity;
        this.bottomMenu = bottomMenu;
        SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
        singleBar = sharedpreferences.getBoolean(activity.getString(R.string.SET_USE_SINGLE_TOPBAR), false);
@@ -86,6 +88,7 @@ public class FedilabPageAdapter extends FragmentStatePagerAdapter {
    @Override
    public Fragment getItem(int position) {
        FragmentMastodonTimeline fragment = new FragmentMastodonTimeline();
        fragment.update = activity;
        Bundle bundle = new Bundle();
        //Position 3 is for notifications
        if (position < (BOTTOM_TIMELINE_COUNT - toRemove) && !singleBar) {
+9 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/cyanea_accent_dark_reference" />
    <padding
        android:left="2dp"
        android:right="2dp" />
    <corners android:radius="3dp" />
</shape>
 No newline at end of file
+17 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/notifications.badge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|center_horizontal"
        android:layout_marginStart="10dp"
        android:background="@drawable/shape_counter"
        android:gravity="center"
        android:padding="3dp"
        android:textColor="@color/white"
        android:textSize="11sp"
        tools:text="9+" />
</merge>
 No newline at end of file