org.quantumbadger.redreader.fragments.CommentListingFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.quantumbadger.redreader.fragments.CommentListingFragment.java

Source

/*******************************************************************************
 * This file is part of RedReader.
 *
 * RedReader is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * RedReader is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with RedReader.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package org.quantumbadger.redreader.fragments;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.*;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.quantumbadger.redreader.R;
import org.quantumbadger.redreader.account.RedditAccount;
import org.quantumbadger.redreader.account.RedditAccountManager;
import org.quantumbadger.redreader.activities.BugReportActivity;
import org.quantumbadger.redreader.activities.CommentReplyActivity;
import org.quantumbadger.redreader.activities.OptionsMenuUtility;
import org.quantumbadger.redreader.adapters.FilteredCommentListingManager;
import org.quantumbadger.redreader.adapters.GroupedRecyclerViewAdapter;
import org.quantumbadger.redreader.cache.downloadstrategy.DownloadStrategy;
import org.quantumbadger.redreader.cache.downloadstrategy.DownloadStrategyAlways;
import org.quantumbadger.redreader.cache.downloadstrategy.DownloadStrategyIfNotCached;
import org.quantumbadger.redreader.common.*;
import org.quantumbadger.redreader.reddit.CommentListingRequest;
import org.quantumbadger.redreader.reddit.RedditCommentListItem;
import org.quantumbadger.redreader.reddit.api.RedditAPICommentAction;
import org.quantumbadger.redreader.reddit.prepared.RedditChangeDataManager;
import org.quantumbadger.redreader.reddit.prepared.RedditPreparedPost;
import org.quantumbadger.redreader.reddit.prepared.RedditRenderableComment;
import org.quantumbadger.redreader.reddit.url.RedditURLParser;
import org.quantumbadger.redreader.views.RedditCommentView;
import org.quantumbadger.redreader.views.RedditPostHeaderView;
import org.quantumbadger.redreader.views.RedditPostView;
import org.quantumbadger.redreader.views.ScrollbarRecyclerViewManager;
import org.quantumbadger.redreader.views.bezelmenu.BezelSwipeOverlay;
import org.quantumbadger.redreader.views.bezelmenu.SideToolbarOverlay;
import org.quantumbadger.redreader.views.liststatus.CommentSubThreadView;
import org.quantumbadger.redreader.views.liststatus.ErrorView;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.UUID;

public class CommentListingFragment extends RRFragment implements RedditPostView.PostSelectionListener,
        RedditCommentView.CommentListener, CommentListingRequest.Listener {

    private static final String SAVEDSTATE_FIRST_VISIBLE_POS = "firstVisiblePosition";

    private final RedditAccount mUser;
    private final ArrayList<RedditURLParser.RedditURL> mAllUrls;
    private final LinkedList<RedditURLParser.RedditURL> mUrlsToDownload;
    private final UUID mSession;
    private final DownloadStrategy mDownloadStrategy;

    private RedditPreparedPost mPost = null;
    private boolean isArchived;

    private final FilteredCommentListingManager mCommentListingManager;

    private final RecyclerView mRecyclerView;

    private final FrameLayout mOuterFrame;
    private final @Nullable LinearLayout mFloatingToolbar;

    private final float mCommentFontScale;
    private final boolean mShowLinkButtons;

    private Long mCachedTimestamp = null;

    private Integer mPreviousFirstVisibleItemPosition;

    public CommentListingFragment(final AppCompatActivity parent, final Bundle savedInstanceState,
            final ArrayList<RedditURLParser.RedditURL> urls, final UUID session, final String searchString,
            final boolean forceDownload) {

        super(parent, savedInstanceState);

        if (savedInstanceState != null) {
            mPreviousFirstVisibleItemPosition = savedInstanceState.getInt(SAVEDSTATE_FIRST_VISIBLE_POS);
        }

        mCommentListingManager = new FilteredCommentListingManager(parent, searchString);
        mAllUrls = urls;

        mUrlsToDownload = new LinkedList<>(mAllUrls);

        this.mSession = session;

        if (forceDownload) {
            mDownloadStrategy = DownloadStrategyAlways.INSTANCE;

        } else {
            mDownloadStrategy = DownloadStrategyIfNotCached.INSTANCE;
        }

        mUser = RedditAccountManager.getInstance(getActivity()).getDefaultAccount();

        parent.invalidateOptionsMenu();

        final Context context = getActivity();

        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        mCommentFontScale = PrefsUtility.appearance_fontscale_comments(context, prefs);
        mShowLinkButtons = PrefsUtility.pref_appearance_linkbuttons(context, prefs);

        mOuterFrame = new FrameLayout(context);

        final ScrollbarRecyclerViewManager recyclerViewManager = new ScrollbarRecyclerViewManager(context, null,
                false);

        if (parent instanceof OptionsMenuUtility.OptionsMenuCommentsListener
                && PrefsUtility.pref_behaviour_enable_swipe_refresh(context, prefs)) {

            recyclerViewManager.enablePullToRefresh(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    ((OptionsMenuUtility.OptionsMenuCommentsListener) parent).onRefreshComments();
                }
            });
        }

        mRecyclerView = recyclerViewManager.getRecyclerView();
        mCommentListingManager.setLayoutManager((LinearLayoutManager) mRecyclerView.getLayoutManager());

        mRecyclerView.setAdapter(mCommentListingManager.getAdapter());
        mOuterFrame.addView(recyclerViewManager.getOuterView());

        mRecyclerView.setItemAnimator(null);

        /* TODO
        {
           final RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator();
           itemAnimator.setRemoveDuration(80);
           itemAnimator.setChangeDuration(80);
           itemAnimator.setAddDuration(80);
           itemAnimator.setMoveDuration(80);
        }
        */

        if (!PrefsUtility.pref_appearance_comments_show_floating_toolbar(context, prefs)) {
            mFloatingToolbar = null;

        } else {
            mFloatingToolbar = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.floating_toolbar,
                    mOuterFrame, false);

            // We need a container so that setVisible() doesn't mess with the Z-order
            final FrameLayout floatingToolbarContainer = new FrameLayout(context);

            floatingToolbarContainer.addView(mFloatingToolbar);
            mOuterFrame.addView(floatingToolbarContainer);

            if (PrefsUtility.isNightMode(context)) {
                mFloatingToolbar.setBackgroundColor(Color.argb(0xCC, 0x33, 0x33, 0x33));
            }

            final int buttonVPadding = General.dpToPixels(context, 12);
            final int buttonHPadding = General.dpToPixels(context, 16);

            {
                final ImageButton previousButton = (ImageButton) LayoutInflater.from(context)
                        .inflate(R.layout.flat_image_button, mFloatingToolbar, false);

                previousButton.setPadding(buttonHPadding, buttonVPadding, buttonHPadding, buttonVPadding);
                previousButton.setImageResource(R.drawable.ic_ff_up_dark);
                previousButton.setContentDescription(getString(R.string.button_prev_comment_parent));
                mFloatingToolbar.addView(previousButton);

                previousButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(final View view) {

                        final LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView
                                .getLayoutManager();

                        for (int pos = layoutManager.findFirstVisibleItemPosition() - 1; pos > 0; pos--) {

                            final GroupedRecyclerViewAdapter.Item item = mCommentListingManager
                                    .getItemAtPosition(pos);

                            if (item instanceof RedditCommentListItem && ((RedditCommentListItem) item).isComment()
                                    && ((RedditCommentListItem) item).getIndent() == 0) {

                                layoutManager.scrollToPositionWithOffset(pos, 0);
                                return;
                            }
                        }

                        layoutManager.scrollToPositionWithOffset(0, 0);
                    }
                });

                previousButton.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(final View view) {
                        General.quickToast(context, R.string.button_prev_comment_parent);
                        return true;
                    }
                });
            }

            {
                final ImageButton nextButton = (ImageButton) LayoutInflater.from(context)
                        .inflate(R.layout.flat_image_button, mFloatingToolbar, false);

                nextButton.setPadding(buttonHPadding, buttonVPadding, buttonHPadding, buttonVPadding);
                nextButton.setImageResource(R.drawable.ic_ff_down_dark);
                nextButton.setContentDescription(getString(R.string.button_next_comment_parent));
                mFloatingToolbar.addView(nextButton);

                nextButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(final View view) {

                        final LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView
                                .getLayoutManager();

                        for (int pos = layoutManager.findFirstVisibleItemPosition() + 1; pos < layoutManager
                                .getItemCount(); pos++) {

                            final GroupedRecyclerViewAdapter.Item item = mCommentListingManager
                                    .getItemAtPosition(pos);

                            if (item instanceof RedditCommentListItem && ((RedditCommentListItem) item).isComment()
                                    && ((RedditCommentListItem) item).getIndent() == 0) {

                                layoutManager.scrollToPositionWithOffset(pos, 0);
                                break;
                            }
                        }
                    }
                });

                nextButton.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(final View view) {
                        General.quickToast(context, R.string.button_next_comment_parent);
                        return true;
                    }
                });
            }
        }

        final SideToolbarOverlay toolbarOverlay = new SideToolbarOverlay(context);

        final BezelSwipeOverlay bezelOverlay = new BezelSwipeOverlay(context,
                new BezelSwipeOverlay.BezelSwipeListener() {
                    @Override
                    public boolean onSwipe(@BezelSwipeOverlay.SwipeEdge int edge) {

                        if (mPost == null)
                            return false;

                        toolbarOverlay.setContents(mPost.generateToolbar(getActivity(), true, toolbarOverlay));
                        toolbarOverlay
                                .show(edge == BezelSwipeOverlay.LEFT ? SideToolbarOverlay.SideToolbarPosition.LEFT
                                        : SideToolbarOverlay.SideToolbarPosition.RIGHT);
                        return true;
                    }

                    public boolean onTap() {

                        if (toolbarOverlay.isShown()) {
                            toolbarOverlay.hide();
                            return true;
                        }

                        return false;
                    }
                });

        mOuterFrame.addView(bezelOverlay);
        mOuterFrame.addView(toolbarOverlay);

        bezelOverlay.getLayoutParams().width = android.widget.FrameLayout.LayoutParams.MATCH_PARENT;
        bezelOverlay.getLayoutParams().height = android.widget.FrameLayout.LayoutParams.MATCH_PARENT;

        toolbarOverlay.getLayoutParams().width = android.widget.FrameLayout.LayoutParams.MATCH_PARENT;
        toolbarOverlay.getLayoutParams().height = android.widget.FrameLayout.LayoutParams.MATCH_PARENT;

        makeNextRequest(context);
    }

    public void handleCommentVisibilityToggle(final RedditCommentView view) {

        final RedditChangeDataManager changeDataManager = RedditChangeDataManager.getInstance(mUser);
        final RedditCommentListItem item = view.getComment();

        if (item.isComment()) {

            final RedditRenderableComment comment = item.asComment();

            changeDataManager.markHidden(RRTime.utcCurrentTimeMillis(), comment,
                    !comment.isCollapsed(changeDataManager));

            mCommentListingManager.updateHiddenStatus();

            final LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
            final int position = layoutManager.getPosition(view);

            if (position == layoutManager.findFirstVisibleItemPosition()) {
                layoutManager.scrollToPositionWithOffset(position, 0);
            }
        }
    }

    @Override
    public View getView() {
        return mOuterFrame;
    }

    @Override
    public Bundle onSaveInstanceState() {

        final Bundle bundle = new Bundle();

        final LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
        bundle.putInt(SAVEDSTATE_FIRST_VISIBLE_POS, layoutManager.findFirstVisibleItemPosition());

        return bundle;
    }

    @SuppressLint("WrongConstant")
    private void makeNextRequest(final Context context) {

        if (!mUrlsToDownload.isEmpty()) {
            new CommentListingRequest(context, this, getActivity(), mUrlsToDownload.getFirst(),
                    mAllUrls.size() == 1, mUrlsToDownload.getFirst(), mUser, mSession, mDownloadStrategy, this);
        }
    }

    @Override
    public void onCommentClicked(final RedditCommentView view) {
        switch (PrefsUtility.pref_behaviour_actions_comment_tap(getActivity(),
                PreferenceManager.getDefaultSharedPreferences(getActivity()))) {

        case COLLAPSE:
            handleCommentVisibilityToggle(view);
            break;

        case ACTION_MENU: {
            final RedditCommentListItem item = view.getComment();
            if (item != null && item.isComment()) {
                RedditAPICommentAction.showActionMenu(getActivity(), this, item.asComment(), view,
                        RedditChangeDataManager.getInstance(mUser), isArchived);
            }
            break;
        }
        }
    }

    @Override
    public void onCommentLongClicked(final RedditCommentView view) {
        switch (PrefsUtility.pref_behaviour_actions_comment_longclick(getActivity(),
                PreferenceManager.getDefaultSharedPreferences(getActivity()))) {

        case ACTION_MENU: {
            final RedditCommentListItem item = view.getComment();
            if (item != null && item.isComment()) {
                RedditAPICommentAction.showActionMenu(getActivity(), this, item.asComment(), view,
                        RedditChangeDataManager.getInstance(mUser), isArchived);
            }
            break;
        }

        case COLLAPSE:
            handleCommentVisibilityToggle(view);
            break;

        case NOTHING:
            break;
        }
    }

    @Override
    public void onCommentListingRequestDownloadNecessary() {
        mCommentListingManager.setLoadingVisible(true);
    }

    @Override
    public void onCommentListingRequestDownloadStarted() {
    }

    @Override
    public void onCommentListingRequestException(final Throwable t) {
        BugReportActivity.handleGlobalError(getActivity(), t);
    }

    @Override
    public void onCommentListingRequestFailure(final RRError error) {
        mCommentListingManager.setLoadingVisible(false);
        mCommentListingManager.addFooterError(new ErrorView(getActivity(), error));
    }

    @Override
    public void onCommentListingRequestCachedCopy(final long timestamp) {
        mCachedTimestamp = timestamp;
    }

    @Override
    public void onCommentListingRequestParseStart() {
        mCommentListingManager.setLoadingVisible(true);
    }

    @Override
    public void onCommentListingRequestAuthorizing() {
        mCommentListingManager.setLoadingVisible(true);
    }

    @Override
    public void onCommentListingRequestPostDownloaded(final RedditPreparedPost post) {

        final Context context = getActivity();

        if (mPost == null) {

            final RRThemeAttributes attr = new RRThemeAttributes(context);

            mPost = post;
            isArchived = post.isArchived;

            final RedditPostHeaderView postHeader = new RedditPostHeaderView(getActivity(), this.mPost);

            mCommentListingManager.addPostHeader(postHeader);
            ((LinearLayoutManager) mRecyclerView.getLayoutManager()).scrollToPositionWithOffset(0, 0);

            if (post.src.getSelfText() != null) {
                final ViewGroup selfText = post.src.getSelfText().buildView(getActivity(), attr.rrMainTextCol,
                        14f * mCommentFontScale, mShowLinkButtons);
                selfText.setFocusable(false);
                selfText.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);

                final int paddingPx = General.dpToPixels(context, 10);
                final FrameLayout paddingLayout = new FrameLayout(context);
                final TextView collapsedView = new TextView(context);
                collapsedView.setText("[ + ]  " + getActivity().getString(R.string.collapsed_self_post));
                collapsedView.setVisibility(View.GONE);
                collapsedView.setPadding(paddingPx, paddingPx, paddingPx, paddingPx);
                paddingLayout.addView(selfText);
                paddingLayout.addView(collapsedView);
                paddingLayout.setPadding(paddingPx, paddingPx, paddingPx, paddingPx);

                paddingLayout.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (selfText.getVisibility() == View.GONE) {
                            selfText.setVisibility(View.VISIBLE);
                            collapsedView.setVisibility(View.GONE);
                        } else {
                            selfText.setVisibility(View.GONE);
                            collapsedView.setVisibility(View.VISIBLE);
                        }

                    }
                });
                // TODO mListHeaderNotifications.setBackgroundColor(Color.argb(35, 128, 128, 128));

                mCommentListingManager.addPostSelfText(paddingLayout);
            }

            if (!General.isTablet(context, PreferenceManager.getDefaultSharedPreferences(context))) {
                getActivity().setTitle(post.src.getTitle());
            }

            if (mCommentListingManager.isSearchListing()) {
                final CommentSubThreadView searchCommentThreadView = new CommentSubThreadView(getActivity(),
                        mAllUrls.get(0).asPostCommentListURL(), R.string.comment_header_search_thread_title);

                mCommentListingManager.addNotification(searchCommentThreadView);
            } else if (!mAllUrls.isEmpty() && mAllUrls.get(0).pathType() == RedditURLParser.POST_COMMENT_LISTING_URL
                    && mAllUrls.get(0).asPostCommentListURL().commentId != null) {

                final CommentSubThreadView specificCommentThreadView = new CommentSubThreadView(getActivity(),
                        mAllUrls.get(0).asPostCommentListURL(), R.string.comment_header_specific_thread_title);

                mCommentListingManager.addNotification(specificCommentThreadView);
            }

            // TODO pref (currently 10 mins)
            if (mCachedTimestamp != null && RRTime.since(mCachedTimestamp) > 10 * 60 * 1000) {

                final TextView cacheNotif = (TextView) LayoutInflater.from(getActivity())
                        .inflate(R.layout.cached_header, null, false);
                cacheNotif.setText(getActivity().getString(R.string.listing_cached,
                        RRTime.formatDateTime(mCachedTimestamp, getActivity())));
                mCommentListingManager.addNotification(cacheNotif);
            }
        }
    }

    @Override
    public void onCommentListingRequestAllItemsDownloaded(final ArrayList<RedditCommentListItem> items) {

        mCommentListingManager.addComments(items);

        if (mFloatingToolbar != null) {
            mFloatingToolbar.setVisibility(View.VISIBLE);
            final Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.slide_in_from_bottom);
            animation.setInterpolator(new OvershootInterpolator());
            mFloatingToolbar.startAnimation(animation);
        }

        mUrlsToDownload.removeFirst();

        final LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();

        if (mPreviousFirstVisibleItemPosition != null
                && layoutManager.getItemCount() > mPreviousFirstVisibleItemPosition) {

            layoutManager.scrollToPositionWithOffset(mPreviousFirstVisibleItemPosition, 0);

            mPreviousFirstVisibleItemPosition = null;
        }

        if (mUrlsToDownload.isEmpty()) {

            if (mCommentListingManager.getCommentCount() == 0) {

                final View emptyView = LayoutInflater.from(getContext()).inflate(R.layout.no_comments_yet,
                        mRecyclerView, false);

                if (mCommentListingManager.isSearchListing()) {
                    ((TextView) emptyView.findViewById(R.id.empty_view_text)).setText(R.string.no_search_results);
                }

                mCommentListingManager.addViewToItems(emptyView);

            } else {

                final View blankView = new View(getContext());
                blankView.setMinimumWidth(1);
                blankView.setMinimumHeight(General.dpToPixels(getContext(), 96));
                mCommentListingManager.addViewToItems(blankView);
            }

            mCommentListingManager.setLoadingVisible(false);

        } else {
            makeNextRequest(getActivity());
        }
    }

    @Override
    public void onCreateOptionsMenu(Menu menu) {
        if (mAllUrls != null && mAllUrls.size() > 0
                && mAllUrls.get(0).pathType() == RedditURLParser.POST_COMMENT_LISTING_URL) {
            menu.add(R.string.action_reply);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        if (item.getTitle() != null && item.getTitle().equals(getActivity().getString(R.string.action_reply))) {

            onParentReply();
            return true;
        }

        return false;
    }

    private void onParentReply() {

        if (mPost != null) {
            final Intent intent = new Intent(getActivity(), CommentReplyActivity.class);
            intent.putExtra(CommentReplyActivity.PARENT_ID_AND_TYPE_KEY, mPost.src.getIdAndType());
            intent.putExtra(CommentReplyActivity.PARENT_MARKDOWN_KEY, mPost.src.getUnescapedSelfText());
            startActivity(intent);

        } else {
            General.quickToast(getActivity(), R.string.error_toast_parent_post_not_downloaded);
        }
    }

    public void onPostSelected(final RedditPreparedPost post) {
        ((RedditPostView.PostSelectionListener) getActivity()).onPostSelected(post);
    }

    public void onPostCommentsSelected(final RedditPreparedPost post) {
        ((RedditPostView.PostSelectionListener) getActivity()).onPostCommentsSelected(post);
    }
}