private Duration estimateIntervalStep(String interval) {
   Duration step;
   switch (interval) {
     case "minute":
       step = Minutes.ONE.toStandardDuration();
       break;
     case "hour":
       step = Hours.ONE.toStandardDuration();
       break;
     case "day":
       step = Days.ONE.toStandardDuration();
       break;
     case "week":
       step = Weeks.ONE.toStandardDuration();
       break;
     case "month":
       step = Days.days(31).toStandardDuration();
       break;
     case "quarter":
       step = Days.days(31 * 3).toStandardDuration();
       break;
     case "year":
       step = Days.days(365).toStandardDuration();
       break;
     default:
       throw new IllegalArgumentException("Invalid duration specified: " + interval);
   }
   return step;
 }
예제 #2
0
public class CommentsAdapter extends RecyclerView.Adapter<CommentsAdapter.CommentView> {
  private final String selfName;
  private ImmutableList<CommentEntry> comments;
  private Optional<String> op;
  private CommentActionListener commentActionListener;
  private long selectedCommentId;
  private boolean prioritizeOpComments;

  private final Instant scoreVisibleThreshold = now().minus(Hours.ONE.toStandardDuration());
  private TLongSet favedComments = new TLongHashSet();
  private boolean showFavCommentButton;

  public CommentsAdapter(String selfName) {
    this.selfName = selfName;

    setHasStableIds(true);
    set(emptyList(), emptyMap(), null);
  }

  public void set(Collection<Comment> comments, Map<Long, Vote> votes, String op) {
    ImmutableMap<Long, Comment> byId = Maps.uniqueIndex(comments, Comment::getId);

    this.op = Optional.fromNullable(op);
    this.comments =
        FluentIterable.from(sort(comments, op))
            .transform(
                comment -> {
                  int depth = getCommentDepth(byId, comment);
                  Vote baseVote = firstNonNull(votes.get(comment.getId()), Vote.NEUTRAL);
                  return new CommentEntry(comment, baseVote, depth);
                })
            .toList();

    notifyDataSetChanged();
  }

  public void setShowFavCommentButton(boolean showFavCommentButton) {
    this.showFavCommentButton = showFavCommentButton;
  }

  public void setPrioritizeOpComments(boolean enabled) {
    if (prioritizeOpComments != enabled) {
      prioritizeOpComments = enabled;
      notifyDataSetChanged();
    }
  }

  public void setSelectedCommentId(long id) {
    if (selectedCommentId != id) {
      selectedCommentId = id;
      notifyDataSetChanged();
    }
  }

  public void setFavedComments(TLongSet favedComments) {
    boolean hasChanged = !this.favedComments.equals(favedComments);
    if (hasChanged) {
      this.favedComments = favedComments;
      notifyDataSetChanged();
    }
  }

  public List<Comment> getComments() {
    return FluentIterable.from(comments).transform(c -> c.comment).toList();
  }

  @Override
  public CommentView onCreateViewHolder(ViewGroup parent, int viewType) {
    return new CommentView(
        LayoutInflater.from(parent.getContext()).inflate(R.layout.comment_layout, parent, false));
  }

  @Override
  public void onViewRecycled(CommentView holder) {
    super.onViewRecycled(holder);
  }

  @Override
  public void onBindViewHolder(CommentView view, int position) {
    CommentEntry entry = comments.get(position);
    Comment comment = entry.comment;

    view.setCommentDepth(entry.depth);
    view.senderInfo.setSenderName(comment.getName(), comment.getMark());
    view.senderInfo.setOnSenderClickedListener(v -> doOnAuthorClicked(comment));

    AndroidUtility.linkify(view.comment, comment.getContent());

    // show the points
    if (equalsIgnoreCase(comment.getName(), selfName)
        || comment.getCreated().isBefore(scoreVisibleThreshold)) {

      view.senderInfo.setPoints(getCommentScore(entry));
    } else {
      view.senderInfo.setPointsUnknown();
    }

    // and the date of the post
    view.senderInfo.setDate(comment.getCreated());

    // enable or disable the badge
    boolean badge = op.transform(op -> op.equalsIgnoreCase(comment.getName())).or(false);
    view.senderInfo.setBadgeOpVisible(badge);

    // and register a vote handler
    view.vote.setVote(entry.vote, true);
    view.vote.setOnVoteListener(
        v -> {
          boolean changed = doVote(entry, v);
          notifyItemChanged(position);
          return changed;
        });

    // set alpha for the sub views. sadly, setting alpha on view.itemView is not working
    view.comment.setAlpha(entry.vote == Vote.DOWN ? 0.5f : 1f);
    view.senderInfo.setAlpha(entry.vote == Vote.DOWN ? 0.5f : 1f);

    view.senderInfo.setOnAnswerClickedListener(v -> doAnswer(comment));

    Context context = view.itemView.getContext();
    view.itemView.setBackgroundColor(
        ContextCompat.getColor(
            context,
            comment.getId() == selectedCommentId
                ? R.color.selected_comment_background
                : R.color.feed_background));

    if (view.kFav != null) {
      if (showFavCommentButton) {
        boolean isFavorite = favedComments.contains(comment.getId());

        view.kFav.setTextColor(
            isFavorite
                ? ContextCompat.getColor(context, R.color.primary)
                : ContextCompat.getColor(context, R.color.grey_700));

        view.kFav.setVisibility(View.VISIBLE);
        view.kFav.setOnClickListener(
            v -> {
              commentActionListener.onCommentMarkAsFavoriteClicked(comment, !isFavorite);
            });
      } else {
        view.kFav.setVisibility(View.GONE);
      }
    }
  }

  private int getCommentScore(CommentEntry entry) {
    int score = entry.comment.getUp() - entry.comment.getDown();
    score += entry.vote.getVoteValue() - entry.baseVote.getVoteValue();
    return score;
  }

  private void doOnAuthorClicked(Comment comment) {
    if (commentActionListener != null) commentActionListener.onCommentAuthorClicked(comment);
  }

  private void doAnswer(Comment comment) {
    if (commentActionListener != null) commentActionListener.onAnswerClicked(comment);
  }

  private boolean doVote(CommentEntry entry, Vote vote) {
    if (commentActionListener == null) return false;

    boolean performVote = commentActionListener.onCommentVoteClicked(entry.comment, vote);
    if (performVote) {
      entry.vote = vote;
    }

    return performVote;
  }

  @Override
  public int getItemCount() {
    return comments.size();
  }

  @Override
  public long getItemId(int position) {
    return comments.get(position).comment.getId();
  }

  public void setCommentActionListener(CommentActionListener commentActionListener) {
    this.commentActionListener = commentActionListener;
  }

  public static class CommentView extends RecyclerView.ViewHolder {
    final TextView comment;
    final VoteView vote;
    final SenderInfoView senderInfo;
    final TextView kFav;

    public CommentView(View itemView) {
      super(itemView);

      // get the subviews
      comment = findById(itemView, R.id.comment);
      vote = ButterKnife.findById(itemView, R.id.voting);
      senderInfo = ButterKnife.findById(itemView, R.id.sender_info);
      kFav = ButterKnife.findById(itemView, R.id.kfav);
    }

    public void setCommentDepth(int depth) {
      ((CommentSpacerView) itemView).setDepth(depth);
    }
  }

  public interface CommentActionListener {

    boolean onCommentVoteClicked(Comment comment, Vote vote);

    void onAnswerClicked(Comment comment);

    void onCommentAuthorClicked(Comment comment);

    void onCommentMarkAsFavoriteClicked(Comment comment, boolean markAsFavorite);
  }

  /**
   * "Flattens" a list of hierarchical comments to a sorted list of comments.
   *
   * @param comments The comments to sort
   */
  private List<Comment> sort(Collection<Comment> comments, String op) {
    ImmutableListMultimap<Long, Comment> byParent = Multimaps.index(comments, Comment::getParent);

    ArrayList<Comment> result = new ArrayList<>();
    appendChildComments(result, byParent, 0, op);
    return result;
  }

  private void appendChildComments(
      List<Comment> target, ListMultimap<Long, Comment> byParent, long id, String op) {

    Ordering<Comment> ordering = COMMENT_BY_CONFIDENCE;
    if (op != null && prioritizeOpComments) {
      ordering =
          Ordering.natural()
              .reverse()
              .onResultOf((Comment c) -> op.equalsIgnoreCase(c.getName()))
              .compound(ordering);
    }

    List<Comment> children = ordering.sortedCopy(byParent.get(id));
    for (Comment child : children) {
      target.add(child);
      appendChildComments(target, byParent, (int) child.getId(), op);
    }
  }

  private static int getCommentDepth(Map<Long, Comment> byId, Comment comment) {
    int depth = 0;
    while (comment != null) {
      depth++;
      comment = byId.get(comment.getParent());
    }

    return Math.min(8, depth);
  }

  private static final Ordering<Comment> COMMENT_BY_CONFIDENCE =
      Ordering.natural().reverse().onResultOf(Comment::getConfidence);

  private static class CommentEntry {
    final Comment comment;
    final Vote baseVote;
    final int depth;

    Vote vote;

    public CommentEntry(Comment comment, Vote baseVote, int depth) {
      this.comment = comment;
      this.baseVote = baseVote;
      this.depth = depth;
      this.vote = baseVote;
    }
  }
}