diff --git a/app/src/main/java/com/fastaccess/data/dao/IssuesPageable.java b/app/src/main/java/com/fastaccess/data/dao/IssuesPageable.java new file mode 100644 index 00000000..99e8b365 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/IssuesPageable.java @@ -0,0 +1,53 @@ +package com.fastaccess.data.dao; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Created by Kosh on 15 Nov 2016, 7:04 PM + */ + + +@Getter @Setter @NoArgsConstructor +public class IssuesPageable implements Parcelable { + + public int first; + public int next; + public int prev; + public int last; + public int totalCount; + public boolean incompleteResults; + public List items; + + @Override public int describeContents() { return 0; } + + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.first); + dest.writeInt(this.next); + dest.writeInt(this.prev); + dest.writeInt(this.last); + dest.writeInt(this.totalCount); + dest.writeByte(this.incompleteResults ? (byte) 1 : (byte) 0); + } + + @SuppressWarnings("WeakerAccess") protected IssuesPageable(Parcel in) { + this.first = in.readInt(); + this.next = in.readInt(); + this.prev = in.readInt(); + this.last = in.readInt(); + this.totalCount = in.readInt(); + this.incompleteResults = in.readByte() != 0; + } + + public static final Creator CREATOR = new Creator() { + @Override public IssuesPageable createFromParcel(Parcel source) {return new IssuesPageable(source);} + + @Override public IssuesPageable[] newArray(int size) {return new IssuesPageable[size];} + }; +} diff --git a/app/src/main/java/com/fastaccess/data/dao/PullRequestStatusModel.java b/app/src/main/java/com/fastaccess/data/dao/PullRequestStatusModel.java index bf37f119..4cb407b1 100644 --- a/app/src/main/java/com/fastaccess/data/dao/PullRequestStatusModel.java +++ b/app/src/main/java/com/fastaccess/data/dao/PullRequestStatusModel.java @@ -3,6 +3,7 @@ package com.fastaccess.data.dao; import android.os.Parcel; import android.os.Parcelable; +import com.fastaccess.data.dao.timeline.Timeline; import com.fastaccess.data.dao.types.StatusStateType; import java.util.Date; @@ -15,7 +16,7 @@ import lombok.Setter; * Created by Kosh on 10 Apr 2017, 3:15 AM */ -@Getter @Setter public class PullRequestStatusModel implements Parcelable { +@Getter @Setter public class PullRequestStatusModel extends Timeline implements Parcelable { private StatusStateType state; private String sha; diff --git a/app/src/main/java/com/fastaccess/data/dao/ReviewCommentModel.java b/app/src/main/java/com/fastaccess/data/dao/ReviewCommentModel.java index 9c253401..b9a60ccf 100644 --- a/app/src/main/java/com/fastaccess/data/dao/ReviewCommentModel.java +++ b/app/src/main/java/com/fastaccess/data/dao/ReviewCommentModel.java @@ -4,6 +4,7 @@ import android.os.Parcel; import android.os.Parcelable; import com.fastaccess.data.dao.model.User; +import com.fastaccess.data.dao.timeline.Timeline; import java.util.Date; @@ -14,7 +15,7 @@ import lombok.Setter; * Created by Kosh on 04 May 2017, 7:10 PM */ -@Getter @Setter public class ReviewCommentModel implements Parcelable { +@Getter @Setter public class ReviewCommentModel extends Timeline implements Parcelable { private long id; private String url; diff --git a/app/src/main/java/com/fastaccess/data/dao/TimelineModel.java b/app/src/main/java/com/fastaccess/data/dao/TimelineModel.java index 5cbabdc8..adb7e778 100644 --- a/app/src/main/java/com/fastaccess/data/dao/TimelineModel.java +++ b/app/src/main/java/com/fastaccess/data/dao/TimelineModel.java @@ -2,289 +2,96 @@ package com.fastaccess.data.dao; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; import com.fastaccess.data.dao.model.Comment; import com.fastaccess.data.dao.model.Issue; -import com.fastaccess.data.dao.model.IssueEvent; import com.fastaccess.data.dao.model.PullRequest; +import com.fastaccess.data.dao.timeline.GenericEvent; import com.fastaccess.data.dao.types.IssueEventType; -import com.fastaccess.data.dao.types.ReviewStateType; -import com.fastaccess.helper.InputHelper; -import java.util.ArrayList; -import java.util.Date; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; +import io.reactivex.Observable; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.NonNull; import lombok.Setter; -import static com.annimon.stream.Collectors.toList; - /** * Created by Kosh on 30 Mar 2017, 9:03 PM */ @Getter @Setter @NoArgsConstructor public class TimelineModel implements Parcelable { public static final int HEADER = 0; - public static final int STATUS = 1; - public static final int REVIEW = 2; - public static final int GROUPED_REVIEW = 3; - public static final int EVENT = 4; - public static final int COMMENT = 5; + public static final int LINE_COMMENT = 1; + public static final int EVENT = 2; + public static final int COMMENT = 3; + public static final int STATUS = 4; - private int type; - private Issue issue; + private IssueEventType event; private Comment comment; - private IssueEvent event; - private PullRequest pullRequest; - private PullRequestStatusModel status; - private ReviewModel review; - private GroupedReviewModel groupedReview; + private GenericEvent genericEvent; private ReviewCommentModel reviewComment; - private Date sortedDate; + private PullRequestStatusModel status; + private Issue issue; + private PullRequest pullRequest; - private TimelineModel(Issue issue) { - this.type = HEADER; + public TimelineModel(Issue issue) { this.issue = issue; - this.sortedDate = issue.getCreatedAt(); } - private TimelineModel(PullRequest pullRequest) { - this.type = HEADER; + public TimelineModel(PullRequest pullRequest) { this.pullRequest = pullRequest; - this.sortedDate = pullRequest.getCreatedAt(); } - private TimelineModel(Comment comment) { - this.type = COMMENT; + public TimelineModel(Comment comment) { this.comment = comment; - this.sortedDate = comment.getCreatedAt() == null ? new Date() : comment.getCreatedAt(); + this.event = IssueEventType.commented; } - private TimelineModel(IssueEvent event) { - this.type = EVENT; - this.event = event; - this.sortedDate = event.getCreatedAt(); + public TimelineModel(PullRequestStatusModel statusModel) { + this.status = statusModel; } - private TimelineModel(PullRequestStatusModel status) { - this.type = STATUS; - this.status = status; - this.sortedDate = status.getCreatedAt(); - } - - private TimelineModel(ReviewModel review) { - this.type = REVIEW; - this.review = review; - this.sortedDate = review.getSubmittedAt(); - } - - private TimelineModel(GroupedReviewModel groupedReview) { - this.type = GROUPED_REVIEW; - this.groupedReview = groupedReview; - this.sortedDate = groupedReview.getDate(); - } - - @NonNull public static TimelineModel constructHeader(@NonNull Issue issue) { - return new TimelineModel(issue); - } - - @NonNull public static TimelineModel constructHeader(@NonNull PullRequest pullRequest) { - return new TimelineModel(pullRequest); - } - - @NonNull public static TimelineModel constructComment(@NonNull Comment comment) { - return new TimelineModel(comment); - } - - @NonNull public static List construct(@Nullable List commentList) { - ArrayList list = new ArrayList<>(); - if (commentList != null && !commentList.isEmpty()) { - list.addAll(Stream.of(commentList) - .map(TimelineModel::new) - .collect(Collectors.toList())); - } - return list; - } - - @NonNull public static List construct(@Nullable List commentList, @Nullable List eventList) { - ArrayList list = new ArrayList<>(); - if (commentList != null && !commentList.isEmpty()) { - list.addAll(Stream.of(commentList) - .map(TimelineModel::new) - .collect(Collectors.toList())); - } - if (eventList != null && !eventList.isEmpty()) { - list.addAll(constructLabels(eventList)); - } - - return Stream.of(list).sorted((o1, o2) -> { - if (o1.getEvent() != null && o2.getComment() != null) { - return o1.getEvent().getCreatedAt().compareTo(o2.getComment().getCreatedAt()); - } else if (o1.getComment() != null && o2.getEvent() != null) { - return o1.getComment().getCreatedAt().compareTo(o2.getEvent().getCreatedAt()); - } else { - return Integer.valueOf(o1.getType()).compareTo(o2.getType()); + public int getType() { + if (getEvent() != null) { + switch (getEvent()) { + case commented: + return COMMENT; + case line_commented: + return LINE_COMMENT; + default: + return EVENT; } - }).collect(Collectors.toList()); - } - - @NonNull public static List construct(@Nullable List commentList, @Nullable List eventList, - @Nullable PullRequestStatusModel status, @Nullable List reviews, - @Nullable List reviewComments) { - ArrayList list = new ArrayList<>(); - if (status != null) { - list.add(new TimelineModel(status)); + } else { + if (issue != null || pullRequest != null) return HEADER; + if (status != null) return STATUS; + return EVENT; } - if (reviews != null && !reviews.isEmpty()) { - list.addAll(constructReviews(reviews, reviewComments)); - } - if (commentList != null && !commentList.isEmpty()) { - list.addAll(Stream.of(commentList) - .map(TimelineModel::new) - .collect(Collectors.toList())); - } - if (eventList != null && !eventList.isEmpty()) { - list.addAll(constructLabels(eventList)); - } - - return Stream.of(list).sortBy(model -> { - if (model.getSortedDate() != null) { - return model.getSortedDate().getTime(); - } else { - return (long) model.getType(); - } - }).collect(Collectors.toList()); - } - - @NonNull private static List constructLabels(@NonNull List eventList) { - List models = new ArrayList<>(); - Map> issueEventMap = Stream.of(eventList) - .filter(value -> value.getEvent() != null && value.getEvent() != IssueEventType.subscribed && - value.getEvent() != IssueEventType.unsubscribed && value.getEvent() != IssueEventType.mentioned) - .collect(Collectors.groupingBy(issueEvent -> { - if (issueEvent.getAssigner() != null && issueEvent.getAssignee() != null) { - return issueEvent.getAssigner().getLogin(); - } - return issueEvent.getActor().getLogin(); - })); - if (issueEventMap != null && !issueEventMap.isEmpty()) { - for (Map.Entry> stringListEntry : issueEventMap.entrySet()) { - List labelModels = new ArrayList<>(); - List events = stringListEntry.getValue(); - IssueEvent toAdd = null; - for (IssueEvent event : events) { - if (event.getEvent() == IssueEventType.labeled || event.getEvent() == IssueEventType.unlabeled) { - if (toAdd == null) { - toAdd = event; - } - long time = toAdd.getCreatedAt().after(event.getCreatedAt()) ? (toAdd.getCreatedAt().getTime() - event - .getCreatedAt().getTime()) : (event.getCreatedAt().getTime() - toAdd.getCreatedAt().getTime()); - if (TimeUnit.MINUTES.toMinutes(time) <= 2 && toAdd.getEvent() == event.getEvent()) { - labelModels.add(event.getLabel()); - } else { - models.add(new TimelineModel(event)); - } - } else { - models.add(new TimelineModel(event)); - } - } - if (toAdd != null) { - toAdd.setLabels(labelModels); - models.add(new TimelineModel(toAdd)); - } - } - } - return Stream.of(models) - .sortBy(TimelineModel::getSortedDate) - .toList(); - } - - @NonNull private static List constructReviews - (@NonNull List reviews, @Nullable List comments) { - List models = new ArrayList<>(); - if (comments == null || comments.isEmpty()) { - models.addAll(Stream.of(reviews) - .map(TimelineModel::new) - .collect(Collectors.toList())); - } else { // this is how bad github API is. - Map> mappedComments = Stream.of(comments) - .collect(Collectors.groupingBy(ReviewCommentModel::getOriginalPosition, LinkedHashMap::new, - Collectors.mapping(o -> o, toList()))); - for (Map.Entry> entry : mappedComments.entrySet()) { - List reviewCommentModels = entry.getValue(); - GroupedReviewModel groupedReviewModel = new GroupedReviewModel(); - if (!reviewCommentModels.isEmpty()) { - ReviewCommentModel reviewCommentModel = reviewCommentModels.get(0); - groupedReviewModel.setPath(reviewCommentModel.getPath()); - groupedReviewModel.setDiffText(reviewCommentModel.getDiffHunk()); - groupedReviewModel.setDate(reviewCommentModel.getCreatedAt()); - groupedReviewModel.setPosition(reviewCommentModel.getOriginalPosition()); - groupedReviewModel.setId(reviewCommentModel.getId()); - } - for (ReviewCommentModel reviewCommentModel : reviewCommentModels) { - if (reviewCommentModel.getCreatedAt() != null) { - groupedReviewModel.setDate(reviewCommentModel.getCreatedAt()); - break; - } - } - groupedReviewModel.setComments(reviewCommentModels); - models.add(new TimelineModel(groupedReviewModel)); - } - models.addAll(Stream.of(reviews) - .filter(reviewModel -> !InputHelper.isEmpty(reviewModel.getBody()) || reviewModel.getState() == ReviewStateType.APPROVED) - .map(TimelineModel::new) - .collect(Collectors.toList())); - } - return models; - } - - @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - TimelineModel model = (TimelineModel) o; - return (comment != null && model.getComment() != null) && (comment.getId() == model.comment.getId()); - } - - @Override public int hashCode() { - return comment != null ? (int) comment.getId() : 0; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(this.type); - dest.writeParcelable(this.issue, flags); + dest.writeInt(this.event == null ? -1 : this.event.ordinal()); dest.writeParcelable(this.comment, flags); - dest.writeParcelable(this.event, flags); - dest.writeParcelable(this.pullRequest, flags); - dest.writeParcelable(this.status, flags); - dest.writeParcelable(this.review, flags); - dest.writeParcelable(this.groupedReview, flags); + dest.writeParcelable(this.genericEvent, flags); dest.writeParcelable(this.reviewComment, flags); - dest.writeLong(this.sortedDate != null ? this.sortedDate.getTime() : -1); + dest.writeParcelable(this.status, flags); + dest.writeParcelable(this.issue, flags); + dest.writeParcelable(this.pullRequest, flags); } protected TimelineModel(Parcel in) { - this.type = in.readInt(); - this.issue = in.readParcelable(Issue.class.getClassLoader()); + int tmpEvent = in.readInt(); + this.event = tmpEvent == -1 ? null : IssueEventType.values()[tmpEvent]; this.comment = in.readParcelable(Comment.class.getClassLoader()); - this.event = in.readParcelable(IssueEvent.class.getClassLoader()); - this.pullRequest = in.readParcelable(PullRequest.class.getClassLoader()); - this.status = in.readParcelable(PullRequestStatusModel.class.getClassLoader()); - this.review = in.readParcelable(ReviewModel.class.getClassLoader()); - this.groupedReview = in.readParcelable(GroupedReviewModel.class.getClassLoader()); + this.genericEvent = in.readParcelable(GenericEvent.class.getClassLoader()); this.reviewComment = in.readParcelable(ReviewCommentModel.class.getClassLoader()); - long tmpSortedDate = in.readLong(); - this.sortedDate = tmpSortedDate == -1 ? null : new Date(tmpSortedDate); + this.status = in.readParcelable(PullRequestStatusModel.class.getClassLoader()); + this.issue = in.readParcelable(Issue.class.getClassLoader()); + this.pullRequest = in.readParcelable(PullRequest.class.getClassLoader()); } public static final Creator CREATOR = new Creator() { @@ -292,4 +99,40 @@ import static com.annimon.stream.Collectors.toList; @Override public TimelineModel[] newArray(int size) {return new TimelineModel[size];} }; + + public static TimelineModel constructHeader(Issue issue) { + return new TimelineModel(issue); + } + + public static TimelineModel constructHeader(PullRequest pullRequest) { + return new TimelineModel(pullRequest); + } + + public static TimelineModel constructComment(Comment comment) { + return new TimelineModel(comment); + } + + @NonNull public static Observable> construct(@Nullable List comments) { + if (comments == null || comments.isEmpty()) return Observable.empty(); + return Observable.fromIterable(comments) + .map(TimelineModel::new) + .toList() + .toObservable(); + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TimelineModel that = (TimelineModel) o; + if (comment != null) { + return comment.equals(that.comment); + } else if (reviewComment != null) { + return reviewComment.equals(that.reviewComment); + } + return false; + } + + @Override public int hashCode() { + return comment != null ? comment.hashCode() : reviewComment != null ? reviewComment.hashCode() : -1; + } } diff --git a/app/src/main/java/com/fastaccess/data/dao/timeline/AuthorModel.java b/app/src/main/java/com/fastaccess/data/dao/timeline/AuthorModel.java new file mode 100644 index 00000000..68713dc9 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/timeline/AuthorModel.java @@ -0,0 +1,38 @@ +package com.fastaccess.data.dao.timeline; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Date; + +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter public class AuthorModel implements Parcelable { + private String name; + private String email; + private Date date; + + public AuthorModel() {} + + @Override public int describeContents() { return 0; } + + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.name); + dest.writeString(this.email); + dest.writeLong(this.date != null ? this.date.getTime() : -1); + } + + protected AuthorModel(Parcel in) { + this.name = in.readString(); + this.email = in.readString(); + long tmpDate = in.readLong(); + this.date = tmpDate == -1 ? null : new Date(tmpDate); + } + + public static final Creator CREATOR = new Creator() { + @Override public AuthorModel createFromParcel(Parcel source) {return new AuthorModel(source);} + + @Override public AuthorModel[] newArray(int size) {return new AuthorModel[size];} + }; +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/data/dao/timeline/CommentEvent.java b/app/src/main/java/com/fastaccess/data/dao/timeline/CommentEvent.java new file mode 100644 index 00000000..837576c6 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/timeline/CommentEvent.java @@ -0,0 +1,126 @@ +package com.fastaccess.data.dao.timeline; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.fastaccess.data.dao.ReactionsModel; +import com.fastaccess.data.dao.model.Comment; +import com.fastaccess.data.dao.model.User; + +import java.util.Date; + +import lombok.Getter; +import lombok.Setter; + +/** + * Created by Kosh on 16 Mar 2017, 7:24 PM + */ +@Getter @Setter public class CommentEvent implements Parcelable { + private long id; + private User user; + private String url; + private String body; + private String bodyHtml; + private String htmlUrl; + private Date createdAt; + private Date updatedAt; + private int position; + private int line; + private String path; + private String commitId; + private String repoId; + private String login; + private String gistId; + private String issueId; + private String pullRequestId; + private ReactionsModel reactions; + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Comment that = (Comment) o; + return id == that.getId(); + + } + + @Override public int hashCode() { + return (int) (id ^ (id >>> 32)); + } + + public CommentEvent() {} + + @Override public String toString() { + return "CommentEvent{" + + "id=" + id + + ", user=" + user + + ", url='" + url + '\'' + + ", body='" + body + '\'' + + ", bodyHtml='" + bodyHtml + '\'' + + ", htmlUrl='" + htmlUrl + '\'' + + ", createdAt=" + createdAt + + ", updatedAt=" + updatedAt + + ", position=" + position + + ", line=" + line + + ", path='" + path + '\'' + + ", commitId='" + commitId + '\'' + + ", repoId='" + repoId + '\'' + + ", login='" + login + '\'' + + ", gistId='" + gistId + '\'' + + ", issueId='" + issueId + '\'' + + ", pullRequestId='" + pullRequestId + '\'' + + ", reactions=" + reactions + + '}'; + } + + @Override public int describeContents() { return 0; } + + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(this.id); + dest.writeParcelable(this.user, flags); + dest.writeString(this.url); + dest.writeString(this.body); + dest.writeString(this.bodyHtml); + dest.writeString(this.htmlUrl); + dest.writeLong(this.createdAt != null ? this.createdAt.getTime() : -1); + dest.writeLong(this.updatedAt != null ? this.updatedAt.getTime() : -1); + dest.writeInt(this.position); + dest.writeInt(this.line); + dest.writeString(this.path); + dest.writeString(this.commitId); + dest.writeString(this.repoId); + dest.writeString(this.login); + dest.writeString(this.gistId); + dest.writeString(this.issueId); + dest.writeString(this.pullRequestId); + dest.writeParcelable(this.reactions, flags); + } + + protected CommentEvent(Parcel in) { + this.id = in.readLong(); + this.user = in.readParcelable(User.class.getClassLoader()); + this.url = in.readString(); + this.body = in.readString(); + this.bodyHtml = in.readString(); + this.htmlUrl = in.readString(); + long tmpCreatedAt = in.readLong(); + this.createdAt = tmpCreatedAt == -1 ? null : new Date(tmpCreatedAt); + long tmpUpdatedAt = in.readLong(); + this.updatedAt = tmpUpdatedAt == -1 ? null : new Date(tmpUpdatedAt); + this.position = in.readInt(); + this.line = in.readInt(); + this.path = in.readString(); + this.commitId = in.readString(); + this.repoId = in.readString(); + this.login = in.readString(); + this.gistId = in.readString(); + this.issueId = in.readString(); + this.pullRequestId = in.readString(); + this.reactions = in.readParcelable(ReactionsModel.class.getClassLoader()); + } + + public static final Creator CREATOR = new Creator() { + @Override public CommentEvent createFromParcel(Parcel source) {return new CommentEvent(source);} + + @Override public CommentEvent[] newArray(int size) {return new CommentEvent[size];} + }; +} diff --git a/app/src/main/java/com/fastaccess/data/dao/timeline/GenericEvent.java b/app/src/main/java/com/fastaccess/data/dao/timeline/GenericEvent.java new file mode 100644 index 00000000..fe7807b0 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/timeline/GenericEvent.java @@ -0,0 +1,119 @@ +package com.fastaccess.data.dao.timeline; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.fastaccess.data.dao.LabelModel; +import com.fastaccess.data.dao.MilestoneModel; +import com.fastaccess.data.dao.RenameModel; +import com.fastaccess.data.dao.TeamsModel; +import com.fastaccess.data.dao.model.Issue; +import com.fastaccess.data.dao.model.PullRequest; +import com.fastaccess.data.dao.model.User; +import com.fastaccess.data.dao.types.IssueEventType; + +import java.util.Date; +import java.util.List; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Created by kosh on 25/07/2017. + */ + +@NoArgsConstructor @Getter @Setter public class GenericEvent implements Parcelable { + + private long id; + private String url; + private String commitId; + private String commitUrl; + private String message; + private String sha; + private String htmlUrl; + private Date createdAt; + private User actor; + private User requestedReviewer; + private User reviewRequester; + private User assigner; + private User assignee; + private User author; + private User committer; + private LabelModel label; + private TeamsModel requestedTeam; + private MilestoneModel milestone; + private RenameModel rename; + private SourceModel source; + private Issue issue; + private PullRequest pullRequest; + private ParentsModel tree; + private List parents; + private IssueEventType event; + + @Override public int describeContents() { return 0; } + + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(this.id); + dest.writeString(this.url); + dest.writeString(this.commitId); + dest.writeString(this.commitUrl); + dest.writeString(this.message); + dest.writeString(this.sha); + dest.writeString(this.htmlUrl); + dest.writeLong(this.createdAt != null ? this.createdAt.getTime() : -1); + dest.writeParcelable(this.actor, flags); + dest.writeParcelable(this.requestedReviewer, flags); + dest.writeParcelable(this.reviewRequester, flags); + dest.writeParcelable(this.assigner, flags); + dest.writeParcelable(this.assignee, flags); + dest.writeParcelable(this.author, flags); + dest.writeParcelable(this.committer, flags); + dest.writeParcelable(this.label, flags); + dest.writeParcelable(this.requestedTeam, flags); + dest.writeParcelable(this.milestone, flags); + dest.writeParcelable(this.rename, flags); + dest.writeParcelable(this.source, flags); + dest.writeParcelable(this.issue, flags); + dest.writeParcelable(this.pullRequest, flags); + dest.writeParcelable(this.tree, flags); + dest.writeTypedList(this.parents); + dest.writeInt(this.event == null ? -1 : this.event.ordinal()); + } + + protected GenericEvent(Parcel in) { + this.id = in.readLong(); + this.url = in.readString(); + this.commitId = in.readString(); + this.commitUrl = in.readString(); + this.message = in.readString(); + this.sha = in.readString(); + this.htmlUrl = in.readString(); + long tmpCreatedAt = in.readLong(); + this.createdAt = tmpCreatedAt == -1 ? null : new Date(tmpCreatedAt); + this.actor = in.readParcelable(User.class.getClassLoader()); + this.requestedReviewer = in.readParcelable(User.class.getClassLoader()); + this.reviewRequester = in.readParcelable(User.class.getClassLoader()); + this.assigner = in.readParcelable(User.class.getClassLoader()); + this.assignee = in.readParcelable(User.class.getClassLoader()); + this.author = in.readParcelable(User.class.getClassLoader()); + this.committer = in.readParcelable(User.class.getClassLoader()); + this.label = in.readParcelable(LabelModel.class.getClassLoader()); + this.requestedTeam = in.readParcelable(TeamsModel.class.getClassLoader()); + this.milestone = in.readParcelable(MilestoneModel.class.getClassLoader()); + this.rename = in.readParcelable(RenameModel.class.getClassLoader()); + this.source = in.readParcelable(SourceModel.class.getClassLoader()); + this.issue = in.readParcelable(Issue.class.getClassLoader()); + this.pullRequest = in.readParcelable(PullRequest.class.getClassLoader()); + this.tree = in.readParcelable(ParentsModel.class.getClassLoader()); + this.parents = in.createTypedArrayList(ParentsModel.CREATOR); + int tmpEvent = in.readInt(); + this.event = tmpEvent == -1 ? null : IssueEventType.values()[tmpEvent]; + } + + public static final Creator CREATOR = new Creator() { + @Override public GenericEvent createFromParcel(Parcel source) {return new GenericEvent(source);} + + @Override public GenericEvent[] newArray(int size) {return new GenericEvent[size];} + }; +} diff --git a/app/src/main/java/com/fastaccess/data/dao/timeline/ParentsModel.java b/app/src/main/java/com/fastaccess/data/dao/timeline/ParentsModel.java new file mode 100644 index 00000000..a0e546f6 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/timeline/ParentsModel.java @@ -0,0 +1,35 @@ +package com.fastaccess.data.dao.timeline; + +import android.os.Parcel; +import android.os.Parcelable; + +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter public class ParentsModel implements Parcelable { + private String sha; + private String url; + private String htmlUrl; + + @Override public int describeContents() { return 0; } + + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.sha); + dest.writeString(this.url); + dest.writeString(this.htmlUrl); + } + + public ParentsModel() {} + + protected ParentsModel(Parcel in) { + this.sha = in.readString(); + this.url = in.readString(); + this.htmlUrl = in.readString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public ParentsModel createFromParcel(Parcel source) {return new ParentsModel(source);} + + @Override public ParentsModel[] newArray(int size) {return new ParentsModel[size];} + }; +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/data/dao/timeline/SourceModel.java b/app/src/main/java/com/fastaccess/data/dao/timeline/SourceModel.java new file mode 100644 index 00000000..821b4958 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/timeline/SourceModel.java @@ -0,0 +1,47 @@ +package com.fastaccess.data.dao.timeline; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.fastaccess.data.dao.model.Commit; +import com.fastaccess.data.dao.model.Issue; +import com.fastaccess.data.dao.model.PullRequest; + +import lombok.Getter; +import lombok.Setter; + +/** + * Created by kosh on 26/07/2017. + */ + +@Getter @Setter public class SourceModel implements Parcelable { + + private String type; + private Issue issue; + private PullRequest pullRequest; + private Commit commit; + + @Override public int describeContents() { return 0; } + + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.type); + dest.writeParcelable(this.issue, flags); + dest.writeParcelable(this.pullRequest, flags); + dest.writeParcelable(this.commit, flags); + } + + public SourceModel() {} + + protected SourceModel(Parcel in) { + this.type = in.readString(); + this.issue = in.readParcelable(Issue.class.getClassLoader()); + this.pullRequest = in.readParcelable(PullRequest.class.getClassLoader()); + this.commit = in.readParcelable(Commit.class.getClassLoader()); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public SourceModel createFromParcel(Parcel source) {return new SourceModel(source);} + + @Override public SourceModel[] newArray(int size) {return new SourceModel[size];} + }; +} diff --git a/app/src/main/java/com/fastaccess/data/dao/timeline/Timeline.java b/app/src/main/java/com/fastaccess/data/dao/timeline/Timeline.java new file mode 100644 index 00000000..e5a49527 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/timeline/Timeline.java @@ -0,0 +1,73 @@ +package com.fastaccess.data.dao.timeline; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.fastaccess.data.dao.PullRequestStatusModel; +import com.fastaccess.data.dao.ReviewCommentModel; +import com.fastaccess.data.dao.model.Comment; +import com.fastaccess.data.dao.types.IssueEventType; + +import lombok.Getter; +import lombok.Setter; + +/** + * Created by kosh on 25/07/2017. + */ + +@Getter @Setter public class Timeline implements Parcelable { + public static final int HEADER = 0; + public static final int LINE_COMMENT = 1; + public static final int EVENT = 2; + public static final int COMMENT = 3; + public static final int STATUS = 4; + + private IssueEventType event; + private Comment comment; + private GenericEvent genericEvent; + private ReviewCommentModel reviewComment; + private PullRequestStatusModel statusModel; + + public int getType() { + if (getEvent() != null) { + switch (getEvent()) { + case commented: + return COMMENT; + case line_commented: + return LINE_COMMENT; + default: + return EVENT; + } + } else { + if (statusModel != null) return STATUS; + return EVENT; + } + } + + public Timeline() {} + + @Override public int describeContents() { return 0; } + + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.event == null ? -1 : this.event.ordinal()); + dest.writeParcelable(this.comment, flags); + dest.writeParcelable(this.genericEvent, flags); + dest.writeParcelable(this.reviewComment, flags); + dest.writeParcelable(this.statusModel, flags); + } + + protected Timeline(Parcel in) { + int tmpEvent = in.readInt(); + this.event = tmpEvent == -1 ? null : IssueEventType.values()[tmpEvent]; + this.comment = in.readParcelable(Comment.class.getClassLoader()); + this.genericEvent = in.readParcelable(GenericEvent.class.getClassLoader()); + this.reviewComment = in.readParcelable(ReviewCommentModel.class.getClassLoader()); + this.statusModel = in.readParcelable(PullRequestStatusModel.class.getClassLoader()); + } + + public static final Creator CREATOR = new Creator() { + @Override public Timeline createFromParcel(Parcel source) {return new Timeline(source);} + + @Override public Timeline[] newArray(int size) {return new Timeline[size];} + }; +} diff --git a/app/src/main/java/com/fastaccess/data/dao/types/IssueEventType.java b/app/src/main/java/com/fastaccess/data/dao/types/IssueEventType.java index b841e2c2..667c2114 100644 --- a/app/src/main/java/com/fastaccess/data/dao/types/IssueEventType.java +++ b/app/src/main/java/com/fastaccess/data/dao/types/IssueEventType.java @@ -1,5 +1,9 @@ package com.fastaccess.data.dao.types; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.annimon.stream.Stream; import com.fastaccess.R; import com.google.gson.annotations.SerializedName; @@ -7,7 +11,7 @@ public enum IssueEventType { assigned(R.drawable.ic_profile), closed(R.drawable.ic_issue_closed), commented(R.drawable.ic_comment), - committed(R.drawable.ic_announcement), + committed(R.drawable.ic_push), demilestoned(R.drawable.ic_milestone), head_ref_deleted(R.drawable.ic_trash), head_ref_restored(R.drawable.ic_redo), @@ -27,7 +31,9 @@ public enum IssueEventType { review_requested(R.drawable.ic_eye), review_dismissed(R.drawable.ic_eye_off), review_request_removed(R.drawable.ic_eye_off), - @SerializedName("cross-referenced")crossReferenced(R.drawable.ic_format_quote); + @SerializedName("cross-referenced")cross_referenced(R.drawable.ic_format_quote), + @SerializedName("line-commented")line_commented(R.drawable.ic_comment), + reviewed(R.drawable.ic_eye); int iconResId; @@ -36,4 +42,12 @@ public enum IssueEventType { public int getIconResId() { return iconResId == 0 ? R.drawable.ic_label : iconResId; } + + @Nullable public static IssueEventType getType(@NonNull String type) { + return Stream.of(values()) + .filter(value -> value.name().toLowerCase().equalsIgnoreCase(type.toLowerCase() + .replaceAll("-", "_"))) + .findFirst() + .orElse(null); + } } \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/data/service/IssueService.java b/app/src/main/java/com/fastaccess/data/service/IssueService.java index 82767c18..6a8835db 100644 --- a/app/src/main/java/com/fastaccess/data/service/IssueService.java +++ b/app/src/main/java/com/fastaccess/data/service/IssueService.java @@ -7,14 +7,17 @@ import com.fastaccess.data.dao.AssigneesRequestModel; import com.fastaccess.data.dao.CommentRequestModel; import com.fastaccess.data.dao.CreateIssueModel; import com.fastaccess.data.dao.IssueRequestModel; +import com.fastaccess.data.dao.IssuesPageable; import com.fastaccess.data.dao.LabelModel; import com.fastaccess.data.dao.Pageable; import com.fastaccess.data.dao.model.Comment; import com.fastaccess.data.dao.model.Issue; import com.fastaccess.data.dao.model.IssueEvent; +import com.google.gson.JsonObject; import java.util.List; +import io.reactivex.Observable; import retrofit2.Response; import retrofit2.http.Body; import retrofit2.http.DELETE; @@ -25,7 +28,6 @@ import retrofit2.http.POST; import retrofit2.http.PUT; import retrofit2.http.Path; import retrofit2.http.Query; -import io.reactivex.Observable; public interface IssueService { @@ -49,6 +51,12 @@ public interface IssueService { Observable> getTimeline(@Path("owner") String owner, @Path("repo") String repo, @Path("issue_number") int issue_number); + @GET("repos/{owner}/{repo}/issues/{issue_number}/timeline") + @Headers("Accept: application/vnd.github.mockingbird-preview,application/vnd.github.VERSION.full+json," + + " application/vnd.github.squirrel-girl-preview") + Observable> getTimeline(@Path("owner") String owner, @Path("repo") String repo, + @Path("issue_number") int issue_number, @Query("page") int page); + @POST("repos/{owner}/{repo}/issues") Observable createIssue(@Path("owner") String owner, @Path("repo") String repo, @Body IssueRequestModel issue); diff --git a/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java b/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java index 3d1eccbf..aea0a71c 100644 --- a/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java +++ b/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.media.AudioManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; @@ -91,6 +92,7 @@ public class NotificationSchedulerJobTask extends JobService { public static void scheduleJob(@NonNull Context context, int duration, boolean cancel) { if (AppHelper.isGoogleAvailable(context)) { FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context)); + dispatcher.cancel(SINGLE_JOB_ID); if (cancel) dispatcher.cancel(JOB_ID); if (duration == -1) { dispatcher.cancel(JOB_ID); @@ -254,7 +256,7 @@ public class NotificationSchedulerJobTask extends JobService { new Intent(getApplicationContext(), NotificationActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); return getNotification(thread.getSubject().getTitle(), thread.getRepository().getFullName()) .setDefaults(PrefGetter.isNotificationSoundEnabled() ? NotificationCompat.DEFAULT_ALL : 0) - .setSound(PrefGetter.getNotificationSound()) + .setSound(PrefGetter.getNotificationSound(), AudioManager.STREAM_NOTIFICATION) .setContentIntent(toNotificationActivity ? pendingIntent : getPendingIntent(thread.getId(), thread.getSubject().getUrl())) .addAction(R.drawable.ic_github, getString(R.string.open), getPendingIntent(thread.getId(), thread .getSubject().getUrl())) diff --git a/app/src/main/java/com/fastaccess/provider/timeline/TimelineConverter.java b/app/src/main/java/com/fastaccess/provider/timeline/TimelineConverter.java new file mode 100644 index 00000000..b84a39a4 --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/timeline/TimelineConverter.java @@ -0,0 +1,68 @@ +package com.fastaccess.provider.timeline; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.fastaccess.data.dao.ReviewCommentModel; +import com.fastaccess.data.dao.TimelineModel; +import com.fastaccess.data.dao.model.Comment; +import com.fastaccess.data.dao.timeline.GenericEvent; +import com.fastaccess.data.dao.types.IssueEventType; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.provider.rest.RestProvider; +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +import java.util.List; + +import io.reactivex.Observable; + +/** + * Created by kosh on 26/07/2017. + */ + +public class TimelineConverter { + + @NonNull public static Observable convert(@Nullable List jsonObjects) { + if (jsonObjects == null) return Observable.empty(); + Gson gson = RestProvider.gson; + return Observable.fromIterable(jsonObjects) + .map(jsonObject -> { + String event = jsonObject.get("event").getAsString(); + TimelineModel timeline = new TimelineModel(); + if (!InputHelper.isEmpty(event)) { + IssueEventType type = IssueEventType.getType(event); + timeline.setEvent(type); + if (type != null) { + if (type == IssueEventType.commented) { + timeline.setComment(getComment(jsonObject, gson)); + } else if (type == IssueEventType.line_commented) { + timeline.setReviewComment(getReviewComment(jsonObject, gson)); + } else { + timeline.setGenericEvent(getGenericEvent(jsonObject, gson)); + } + } + } else { + timeline.setGenericEvent(getGenericEvent(jsonObject, gson)); + } + return timeline; + }) + .filter(timeline -> timeline != null && filterEvents(timeline.getEvent())); + } + + private static GenericEvent getGenericEvent(@NonNull JsonObject object, @NonNull Gson gson) { + return gson.fromJson(object, GenericEvent.class); + } + + private static ReviewCommentModel getReviewComment(@NonNull JsonObject object, @NonNull Gson gson) { + return gson.fromJson(object, ReviewCommentModel.class); + } + + private static Comment getComment(@NonNull JsonObject object, @NonNull Gson gson) { + return gson.fromJson(object, Comment.class); + } + + private static boolean filterEvents(@Nullable IssueEventType type) { + return type != IssueEventType.subscribed && type != IssueEventType.unsubscribed && type != IssueEventType.mentioned; + } +} diff --git a/app/src/main/java/com/fastaccess/provider/timeline/TimelineProvider.java b/app/src/main/java/com/fastaccess/provider/timeline/TimelineProvider.java index 7f295e82..be361ba9 100644 --- a/app/src/main/java/com/fastaccess/provider/timeline/TimelineProvider.java +++ b/app/src/main/java/com/fastaccess/provider/timeline/TimelineProvider.java @@ -8,14 +8,14 @@ import android.text.style.BackgroundColorSpan; import com.fastaccess.R; import com.fastaccess.data.dao.LabelModel; -import com.fastaccess.data.dao.model.IssueEvent; import com.fastaccess.data.dao.model.User; +import com.fastaccess.data.dao.timeline.GenericEvent; +import com.fastaccess.data.dao.timeline.SourceModel; import com.fastaccess.data.dao.types.IssueEventType; import com.fastaccess.helper.InputHelper; import com.fastaccess.helper.ParseDateFormat; import com.fastaccess.helper.PrefGetter; import com.fastaccess.helper.ViewHelper; -import com.fastaccess.ui.widgets.LabelSpan; import com.fastaccess.ui.widgets.SpannableBuilder; import com.zzhoujay.markdown.style.CodeSpan; @@ -27,9 +27,14 @@ import java.util.Date; public class TimelineProvider { - @NonNull public static SpannableBuilder getStyledEvents(@NonNull IssueEvent issueEventModel, @NonNull Context context, boolean isMerged) { + @NonNull public static SpannableBuilder getStyledEvents(@NonNull GenericEvent issueEventModel, + @NonNull Context context, boolean isMerged) { IssueEventType event = issueEventModel.getEvent(); SpannableBuilder spannableBuilder = SpannableBuilder.builder(); + Date date = issueEventModel.getCreatedAt() != null + ? issueEventModel.getCreatedAt() + : issueEventModel.getAuthor() != null + ? issueEventModel.getAuthor().getDate() : null; if (event != null) { String to = context.getString(R.string.to); String from = context.getString(R.string.from); @@ -46,20 +51,25 @@ public class TimelineProvider { int color = Color.parseColor("#" + labelModel.getColor()); spannableBuilder.append(" ").append(" " + labelModel.getName() + " ", new CodeSpan(color, ViewHelper.generateTextColor(color), 5)); spannableBuilder.append(" ").append(getDate(issueEventModel.getCreatedAt())); + } else if (event == IssueEventType.committed) { + spannableBuilder.append(issueEventModel.getMessage().replaceAll("\n", " ")) + .append(" ") + .url(substring(issueEventModel.getSha())); } else { User user = null; if (issueEventModel.getAssignee() != null && issueEventModel.getAssigner() != null) { user = issueEventModel.getAssigner(); } else if (issueEventModel.getActor() != null) { user = issueEventModel.getActor(); + } else if (issueEventModel.getAuthor() != null) { + user = issueEventModel.getAuthor(); } if (user != null) { spannableBuilder.bold(user.getLogin()); } - if ((event == IssueEventType.review_requested || (event == IssueEventType.review_dismissed || event == IssueEventType.review_request_removed)) && user != null) { - appendReviews(issueEventModel, event, spannableBuilder, from, user); + appendReviews(issueEventModel, event, spannableBuilder, from, issueEventModel.getReviewRequester()); } else if (event == IssueEventType.closed || event == IssueEventType.reopened) { if (isMerged) { spannableBuilder.append(" ").append(IssueEventType.merged.name()); @@ -76,7 +86,6 @@ public class TimelineProvider { .append(in) .append(" ") .url(substring(issueEventModel.getCommitId())); - } } else if (event == IssueEventType.assigned || event == IssueEventType.unassigned) { spannableBuilder @@ -123,18 +132,38 @@ public class TimelineProvider { .append("commit") .append(" ") .url(substring(issueEventModel.getCommitId())); + } else if (event == IssueEventType.cross_referenced) { + SourceModel sourceModel = issueEventModel.getSource(); + if (sourceModel != null) { + SpannableBuilder title = SpannableBuilder.builder(); + if (sourceModel.getIssue() != null) { + title.url("#" + sourceModel.getIssue().getNumber()); + } else if (sourceModel.getPullRequest() != null) { + title.url("#" + sourceModel.getPullRequest().getNumber()); + } else if (sourceModel.getCommit() != null) { + title.url(substring(sourceModel.getCommit().getSha())); + } + if (!InputHelper.isEmpty(title)) { + spannableBuilder.append(" ") + .append(thisString) + .append(" in ") + .append(sourceModel.getType()) + .append(" ") + .append(title); + } + } } - spannableBuilder.append(" ").append(getDate(issueEventModel.getCreatedAt())); + spannableBuilder.append(" ").append(getDate(date)); } } return spannableBuilder; } - private static void appendReviews(@NonNull IssueEvent issueEventModel, @NonNull IssueEventType event, - @NonNull SpannableBuilder spannableBuilder, @NonNull String from, @NonNull User user) { + private static void appendReviews(@NonNull GenericEvent issueEventModel, @NonNull IssueEventType event, + @NonNull SpannableBuilder spannableBuilder, @NonNull String from, + @NonNull User user) { spannableBuilder.append(" "); - User reviewer = issueEventModel.getRequestedReviewer() != null - ? issueEventModel.getRequestedReviewer() : issueEventModel.getIssue() != null ? issueEventModel.getIssue().getUser() : null; + User reviewer = issueEventModel.getRequestedReviewer(); if (reviewer != null && user.getLogin().equalsIgnoreCase(reviewer.getLogin())) { spannableBuilder .append(event == IssueEventType.review_requested @@ -158,11 +187,6 @@ public class TimelineProvider { } } - public static void appendLabels(@NonNull LabelModel labelModel, @NonNull SpannableBuilder spannableBuilder) { - int color = Color.parseColor("#" + labelModel.getColor()); - spannableBuilder.append(" ").append(" " + labelModel.getName() + " ", new LabelSpan(color)); - } - @NonNull private static CharSequence getDate(@Nullable Date date) { return ParseDateFormat.getTimeAgo(date); } diff --git a/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/DrawableGetter.java b/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/DrawableGetter.java index e461c72a..9d421232 100644 --- a/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/DrawableGetter.java +++ b/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/DrawableGetter.java @@ -3,14 +3,12 @@ package com.fastaccess.provider.timeline.handler.drawable; import android.content.Context; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; import android.text.Html; import android.widget.TextView; import com.bumptech.glide.GenericRequestBuilder; import com.bumptech.glide.Glide; import com.fastaccess.R; -import com.fastaccess.helper.Logger; import java.lang.ref.WeakReference; import java.util.HashSet; @@ -30,23 +28,10 @@ public class DrawableGetter implements Html.ImageGetter, Drawable.Callback { this.cachedTargets = new HashSet<>(); } - public void clear(@NonNull DrawableGetter drawableGetter) { - if (drawableGetter.cachedTargets != null) { - for (GifBitmapTarget target : drawableGetter.cachedTargets) { - Logger.e("is clearing"); - Glide.clear(target); - } - } - } - @Override public Drawable getDrawable(@NonNull String url) { final UrlDrawable urlDrawable = new UrlDrawable(); if (container != null && container.get() != null) { Context context = container.get().getContext(); - Drawable drawable = ContextCompat.getDrawable(context, R.drawable.ic_image); - urlDrawable.setDrawable(drawable); - container.get().setText(container.get().getText()); - container.get().invalidate(); final GenericRequestBuilder load = Glide.with(context) .load(url) .dontAnimate(); @@ -66,4 +51,12 @@ public class DrawableGetter implements Html.ImageGetter, Drawable.Callback { @Override public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) {} @Override public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) {} + + public void clear(@NonNull DrawableGetter drawableGetter) { + if (drawableGetter.cachedTargets != null) { + for (GifBitmapTarget target : drawableGetter.cachedTargets) { + Glide.clear(target); + } + } + } } diff --git a/app/src/main/java/com/fastaccess/ui/adapter/IssuePullsTimelineAdapter.java b/app/src/main/java/com/fastaccess/ui/adapter/IssuePullsTimelineAdapter.java index f6ec0f3f..e83d2d77 100644 --- a/app/src/main/java/com/fastaccess/ui/adapter/IssuePullsTimelineAdapter.java +++ b/app/src/main/java/com/fastaccess/ui/adapter/IssuePullsTimelineAdapter.java @@ -7,11 +7,10 @@ import android.view.ViewGroup; import com.fastaccess.data.dao.TimelineModel; import com.fastaccess.ui.adapter.callback.OnToggleView; import com.fastaccess.ui.adapter.callback.ReactionsCallback; -import com.fastaccess.ui.adapter.viewholder.GroupedReviewsViewHolder; import com.fastaccess.ui.adapter.viewholder.IssueDetailsViewHolder; import com.fastaccess.ui.adapter.viewholder.IssueTimelineViewHolder; import com.fastaccess.ui.adapter.viewholder.PullStatusViewHolder; -import com.fastaccess.ui.adapter.viewholder.ReviewsViewHolder; +import com.fastaccess.ui.adapter.viewholder.ReviewCommentsViewHolder; import com.fastaccess.ui.adapter.viewholder.TimelineCommentsViewHolder; import com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.timeline.timeline.PullRequestTimelineMvp.ReviewCommentCallback; import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; @@ -59,12 +58,14 @@ public class IssuePullsTimelineAdapter extends BaseRecyclerAdapter impl } @Override public void bind(@NonNull TimelineModel model) { - GroupedReviewModel groupedReviewModel = model.getGroupedReview(); - this.pathText = groupedReviewModel.getDiffText(); - name.setText(groupedReviewModel.getPath()); - stateImage.setImageResource(R.drawable.ic_eye); - if (groupedReviewModel.getComments() == null || groupedReviewModel.getComments().isEmpty()) { - nestedRecyclerView.setVisibility(View.GONE); - nestedRecyclerView.setAdapter(null); - } else { - nestedRecyclerView.setVisibility(View.VISIBLE); - nestedRecyclerView.setAdapter(new ReviewCommentsAdapter(groupedReviewModel.getComments(), this, - onToggleView, reactionsCallback, repoOwner, poster)); - nestedRecyclerView.addDivider(); - } - onToggle(onToggleView.isCollapsed(getId()), false); +// GroupedReviewModel groupedReviewModel = model.getGroupedReview(); +// this.pathText = groupedReviewModel.getDiffText(); +// name.setText(groupedReviewModel.getPath()); +// stateImage.setImageResource(R.drawable.ic_eye); +// if (groupedReviewModel.getComments() == null || groupedReviewModel.getComments().isEmpty()) { +// nestedRecyclerView.setVisibility(View.GONE); +// nestedRecyclerView.setAdapter(null); +// } else { +// nestedRecyclerView.setVisibility(View.VISIBLE); +// nestedRecyclerView.setAdapter(new ReviewCommentsAdapter(groupedReviewModel.getComments(), this, +// onToggleView, reactionsCallback, repoOwner, poster)); +// nestedRecyclerView.addDivider(); +// } +// onToggle(onToggleView.isCollapsed(getId()), false); } diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/IssueTimelineViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/IssueTimelineViewHolder.java index 93da8528..7a72f9a7 100644 --- a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/IssueTimelineViewHolder.java +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/IssueTimelineViewHolder.java @@ -1,27 +1,21 @@ package com.fastaccess.ui.adapter.viewholder; -import android.graphics.Color; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.View; import android.view.ViewGroup; import com.fastaccess.R; -import com.fastaccess.data.dao.LabelModel; import com.fastaccess.data.dao.TimelineModel; -import com.fastaccess.data.dao.model.IssueEvent; +import com.fastaccess.data.dao.timeline.GenericEvent; import com.fastaccess.data.dao.types.IssueEventType; import com.fastaccess.helper.InputHelper; -import com.fastaccess.helper.Logger; -import com.fastaccess.helper.ParseDateFormat; import com.fastaccess.provider.scheme.LinkParserHelper; import com.fastaccess.provider.timeline.TimelineProvider; import com.fastaccess.provider.timeline.handler.drawable.DrawableGetter; import com.fastaccess.ui.widgets.AvatarLayout; import com.fastaccess.ui.widgets.FontTextView; import com.fastaccess.ui.widgets.ForegroundImageView; -import com.fastaccess.ui.widgets.LabelSpan; -import com.fastaccess.ui.widgets.SpannableBuilder; import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; @@ -48,7 +42,7 @@ public class IssueTimelineViewHolder extends BaseViewHolder { } @Override public void bind(@NonNull TimelineModel timelineModel) { - IssueEvent issueEventModel = timelineModel.getEvent(); + GenericEvent issueEventModel = timelineModel.getGenericEvent(); IssueEventType event = issueEventModel.getEvent(); if (issueEventModel.getAssignee() != null && issueEventModel.getAssigner() != null) { avatarLayout.setUrl(issueEventModel.getAssigner().getAvatarUrl(), issueEventModel.getAssigner().getLogin(), @@ -57,35 +51,20 @@ public class IssueTimelineViewHolder extends BaseViewHolder { if (issueEventModel.getActor() != null) { avatarLayout.setUrl(issueEventModel.getActor().getAvatarUrl(), issueEventModel.getActor().getLogin(), false, LinkParserHelper.isEnterprise(issueEventModel.getUrl())); + } else if (issueEventModel.getAuthor() != null) { + avatarLayout.setUrl(issueEventModel.getAuthor().getAvatarUrl(), issueEventModel.getAuthor().getLogin(), + false, LinkParserHelper.isEnterprise(issueEventModel.getUrl())); } } if (event != null) { stateImage.setContentDescription(event.name()); stateImage.setImageResource(event.getIconResId()); } - if (issueEventModel.getLabels() == null || issueEventModel.getLabels().isEmpty()) { - if (event != null) { - stateText.setText(TimelineProvider.getStyledEvents(issueEventModel, itemView.getContext(), isMerged)); - } else { - stateText.setText(""); - stateImage.setImageResource(R.drawable.ic_label); - } + if (event != null) { + stateText.setText(TimelineProvider.getStyledEvents(issueEventModel, itemView.getContext(), isMerged)); } else { - if (event != null) { - SpannableBuilder spannableBuilder = SpannableBuilder.builder(); - if (issueEventModel.getAssignee() != null && issueEventModel.getAssigner() != null) { - spannableBuilder.bold(issueEventModel.getAssigner().getLogin(), new LabelSpan(Color.TRANSPARENT)); - } else if (issueEventModel.getActor() != null) { - spannableBuilder.bold(issueEventModel.getActor().getLogin(), new LabelSpan(Color.TRANSPARENT)); - } - spannableBuilder.append(" ").append(event.name().replaceAll("_", " "), new LabelSpan(Color.TRANSPARENT)); - for (LabelModel labelModel : issueEventModel.getLabels()) { - TimelineProvider.appendLabels(labelModel, spannableBuilder); - } - spannableBuilder.append(" ").append(ParseDateFormat.getTimeAgo(issueEventModel.getCreatedAt()), new LabelSpan(Color.TRANSPARENT)); - stateText.setText(spannableBuilder); - stateImage.setImageResource(R.drawable.ic_label); - } + stateText.setText(""); + stateImage.setImageResource(R.drawable.ic_label); } itemView.setEnabled(!InputHelper.isEmpty(issueEventModel.getCommitUrl())); } diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/ReviewsViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/ReviewsViewHolder.java index bba23357..f4a0b35d 100644 --- a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/ReviewsViewHolder.java +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/ReviewsViewHolder.java @@ -6,17 +6,11 @@ import android.view.View; import android.view.ViewGroup; import com.fastaccess.R; -import com.fastaccess.data.dao.ReviewModel; import com.fastaccess.data.dao.TimelineModel; -import com.fastaccess.helper.InputHelper; -import com.fastaccess.helper.ParseDateFormat; -import com.fastaccess.provider.scheme.LinkParserHelper; -import com.fastaccess.provider.timeline.HtmlHelper; import com.fastaccess.provider.timeline.handler.drawable.DrawableGetter; import com.fastaccess.ui.widgets.AvatarLayout; import com.fastaccess.ui.widgets.FontTextView; import com.fastaccess.ui.widgets.ForegroundImageView; -import com.fastaccess.ui.widgets.SpannableBuilder; import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; @@ -44,31 +38,31 @@ public class ReviewsViewHolder extends BaseViewHolder { } @Override public void bind(@NonNull TimelineModel model) { - ReviewModel review = model.getReview(); - if (review != null) { - if (review.getUser() != null) { - avatarLayout.setUrl(review.getUser().getAvatarUrl(), review.getUser().getLogin(), false, - LinkParserHelper.isEnterprise(review.getUser().getHtmlUrl())); - } else { - avatarLayout.setUrl(null, null, false, false); - } - if (review.getState() != null) { - stateImage.setImageResource(review.getState().getDrawableRes()); - } - if (review.getUser() != null) { - stateText.setText(SpannableBuilder.builder().append(review.getUser().getLogin()) - .append(" ") - .append(review.getState() != null ? stateText.getResources().getString(review.getState().getStringRes()) : "") - .append(" ") - .append(ParseDateFormat.getTimeAgo(review.getSubmittedAt()))); - } - if (!InputHelper.isEmpty(review.getBody())) { - body.setVisibility(View.VISIBLE); - HtmlHelper.htmlIntoTextView(body, review.getBody()); - } else { - body.setVisibility(View.GONE); - } - } +// ReviewModel review = model.getReview(); +// if (review != null) { +// if (review.getUser() != null) { +// avatarLayout.setUrl(review.getUser().getAvatarUrl(), review.getUser().getLogin(), false, +// LinkParserHelper.isEnterprise(review.getUser().getHtmlUrl())); +// } else { +// avatarLayout.setUrl(null, null, false, false); +// } +// if (review.getState() != null) { +// stateImage.setImageResource(review.getState().getDrawableRes()); +// } +// if (review.getUser() != null) { +// stateText.setText(SpannableBuilder.builder().append(review.getUser().getLogin()) +// .append(" ") +// .append(review.getState() != null ? stateText.getResources().getString(review.getState().getStringRes()) : "") +// .append(" ") +// .append(ParseDateFormat.getTimeAgo(review.getSubmittedAt()))); +// } +// if (!InputHelper.isEmpty(review.getBody())) { +// body.setVisibility(View.VISIBLE); +// HtmlHelper.htmlIntoTextView(body, review.getBody()); +// } else { +// body.setVisibility(View.GONE); +// } +// } } @Override protected void onViewIsDetaching() { diff --git a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java index a9cb1cda..cd8c00c8 100644 --- a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java +++ b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java @@ -22,6 +22,7 @@ import android.view.View; import android.view.ViewTreeObserver; import android.widget.Toast; +import com.bumptech.glide.Glide; import com.evernote.android.state.State; import com.evernote.android.state.StateSaver; import com.fastaccess.App; @@ -70,7 +71,6 @@ public abstract class BaseActivity extends TiPresenter impl manageDisposable( RxHelper.getObservable(observable) .doOnSubscribe(disposable -> onSubscribed(cancelable)) - .subscribe(onNext, this::onError, () -> apiCalled = true) + .subscribe(onNext, this::onError, () -> { + apiCalled = true; + sendToView(BaseMvp.FAView::hideProgress); + }) ); } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/comments/CommitCommentsPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/comments/CommitCommentsPresenter.java index 4859da76..40f15f4a 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/comments/CommitCommentsPresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/comments/CommitCommentsPresenter.java @@ -17,6 +17,7 @@ import com.fastaccess.helper.RxHelper; import com.fastaccess.provider.rest.RestProvider; import com.fastaccess.provider.timeline.CommentsHelper; import com.fastaccess.provider.timeline.ReactionsProvider; +import com.fastaccess.ui.base.mvp.BaseMvp; import com.fastaccess.ui.base.mvp.presenter.BasePresenter; import java.util.ArrayList; @@ -65,10 +66,12 @@ class CommitCommentsPresenter extends BasePresenter impl } setCurrentPage(page); makeRestCall(RestProvider.getRepoService(isEnterprise()).getCommitComments(login, repoId, sha, page) - .flatMap(listResponse -> { - lastPage = listResponse.getLast(); - return Observable.just(TimelineModel.construct(listResponse.getItems())); - }), listResponse -> sendToView(view -> view.onNotifyAdapter(listResponse, page))); + .flatMap(listResponse -> { + lastPage = listResponse.getLast(); + return TimelineModel.construct(listResponse.getItems()); + }) + .doFinally(() -> sendToView(BaseMvp.FAView::hideProgress)), + listResponse -> sendToView(view -> view.onNotifyAdapter(listResponse, page))); } @Override public void onFragmentCreated(@Nullable Bundle bundle) { @@ -103,7 +106,7 @@ class CommitCommentsPresenter extends BasePresenter impl @Override public void onWorkOffline() { if (comments.isEmpty()) { manageDisposable(RxHelper.getObservable(Comment.getCommitComments(repoId(), login(), sha).toObservable()) - .flatMap(comments -> Observable.just(TimelineModel.construct(comments))) + .flatMap(TimelineModel::construct) .subscribe(models -> sendToView(view -> view.onNotifyAdapter(models, 1)))); } else { sendToView(CommitCommentsMvp.View::hideProgress); diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelinePresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelinePresenter.java index 3f8a805b..533dff6b 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelinePresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelinePresenter.java @@ -12,8 +12,8 @@ import com.fastaccess.R; import com.fastaccess.data.dao.TimelineModel; import com.fastaccess.data.dao.model.Comment; import com.fastaccess.data.dao.model.Issue; -import com.fastaccess.data.dao.model.IssueEvent; import com.fastaccess.data.dao.model.Login; +import com.fastaccess.data.dao.timeline.GenericEvent; import com.fastaccess.data.dao.types.ReactionTypes; import com.fastaccess.helper.ActivityHelper; import com.fastaccess.helper.BundleConstant; @@ -22,6 +22,7 @@ import com.fastaccess.provider.rest.RestProvider; import com.fastaccess.provider.scheme.SchemeParser; import com.fastaccess.provider.timeline.CommentsHelper; import com.fastaccess.provider.timeline.ReactionsProvider; +import com.fastaccess.provider.timeline.TimelineConverter; import com.fastaccess.ui.base.mvp.BaseMvp; import com.fastaccess.ui.base.mvp.presenter.BasePresenter; import com.fastaccess.ui.modules.repos.issues.create.CreateIssueActivity; @@ -77,7 +78,7 @@ import lombok.Getter; onHandleReaction(v.getId(), item.getComment().getId(), ReactionsProvider.COMMENT); } } else if (item.getType() == TimelineModel.EVENT) { - IssueEvent issueEventModel = item.getEvent(); + GenericEvent issueEventModel = item.getGenericEvent(); if (issueEventModel.getCommitUrl() != null) { SchemeParser.launchUri(v.getContext(), Uri.parse(issueEventModel.getCommitUrl())); } @@ -215,22 +216,15 @@ import lombok.Getter; String login = parameter.getLogin(); String repoId = parameter.getRepoId(); int number = parameter.getNumber(); - Observable> observable; - if (page > 1) { - observable = RestProvider.getIssueService(isEnterprise()).getIssueComments(login, repoId, number, page) - .map(comments -> { - lastPage = comments != null ? comments.getLast() : 0; - return TimelineModel.construct(comments != null ? comments.getItems() : null); - }); - } else { - observable = Observable.zip(RestProvider.getIssueService(isEnterprise()).getTimeline(login, repoId, number), - RestProvider.getIssueService(isEnterprise()).getIssueComments(login, repoId, number, page), - (issueEventPageable, commentPageable) -> { - lastPage = commentPageable != null ? commentPageable.getLast() : 0; - return TimelineModel.construct(commentPageable != null ? commentPageable.getItems() : null, - issueEventPageable != null ? issueEventPageable.getItems() : null); - }); - } - makeRestCall(observable, models -> sendToView(view -> view.onNotifyAdapter(models, page))); + Observable> observable = RestProvider.getIssueService(isEnterprise()) + .getTimeline(login, repoId, number, page) + .flatMap(response -> { + if (response != null) { + lastPage = response.getLast(); + } + return TimelineConverter.convert(response != null ? response.getItems() : null); + }).toList() + .toObservable(); + makeRestCall(observable, timeline -> sendToView(view -> view.onNotifyAdapter(timeline, page))); } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelineFragment.java b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelineFragment.java index 794c1030..18019dd3 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelineFragment.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelineFragment.java @@ -305,16 +305,16 @@ public class PullRequestTimelineFragment extends BaseFragment, diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelinePresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelinePresenter.java index f607b4ab..4b9a52ef 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelinePresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelinePresenter.java @@ -1,7 +1,5 @@ package com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.timeline.timeline; -import android.app.Activity; -import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -10,11 +8,10 @@ import android.widget.PopupMenu; import com.fastaccess.R; import com.fastaccess.data.dao.EditReviewCommentModel; -import com.fastaccess.data.dao.GroupedReviewModel; +import com.fastaccess.data.dao.PullRequestStatusModel; import com.fastaccess.data.dao.ReviewCommentModel; import com.fastaccess.data.dao.TimelineModel; import com.fastaccess.data.dao.model.Comment; -import com.fastaccess.data.dao.model.IssueEvent; import com.fastaccess.data.dao.model.Login; import com.fastaccess.data.dao.model.PullRequest; import com.fastaccess.data.dao.types.ReactionTypes; @@ -22,12 +19,11 @@ import com.fastaccess.helper.ActivityHelper; import com.fastaccess.helper.BundleConstant; import com.fastaccess.helper.InputHelper; import com.fastaccess.provider.rest.RestProvider; -import com.fastaccess.provider.scheme.SchemeParser; import com.fastaccess.provider.timeline.CommentsHelper; import com.fastaccess.provider.timeline.ReactionsProvider; +import com.fastaccess.provider.timeline.TimelineConverter; import com.fastaccess.ui.base.mvp.BaseMvp; import com.fastaccess.ui.base.mvp.presenter.BasePresenter; -import com.fastaccess.ui.modules.repos.issues.create.CreateIssueActivity; import java.util.ArrayList; import java.util.List; @@ -46,99 +42,99 @@ public class PullRequestTimelinePresenter extends BasePresenter { - if (getView() == null) return false; - if (item1.getItemId() == R.id.delete) { - getView().onShowDeleteMsg(item.getComment().getId()); - } else if (item1.getItemId() == R.id.reply) { - getView().onReply(item.getComment().getUser(), item.getComment().getBody()); - } else if (item1.getItemId() == R.id.edit) { - getView().onEditComment(item.getComment()); - } else if (item1.getItemId() == R.id.share) { - ActivityHelper.shareUrl(v.getContext(), item.getComment().getHtmlUrl()); - } - return true; - }); - popupMenu.show(); - } else { - onHandleReaction(v.getId(), item.getComment().getId(), ReactionsProvider.COMMENT); - } - } else if (item.getType() == TimelineModel.EVENT) { - IssueEvent issueEventModel = item.getEvent(); - if (issueEventModel.getCommitUrl() != null) { - SchemeParser.launchUri(v.getContext(), Uri.parse(issueEventModel.getCommitUrl())); - } - } else if (item.getType() == TimelineModel.HEADER) { - if (v.getId() == R.id.commentMenu) { - PopupMenu popupMenu = new PopupMenu(v.getContext(), v); - popupMenu.inflate(R.menu.comments_menu); - String username = Login.getUser().getLogin(); - boolean isOwner = CommentsHelper.isOwner(username, item.getPullRequest().getLogin(), - item.getPullRequest().getUser().getLogin()); - popupMenu.getMenu().findItem(R.id.edit).setVisible(isOwner); - popupMenu.setOnMenuItemClickListener(item1 -> { - if (getView() == null) return false; - if (item1.getItemId() == R.id.reply) { - getView().onReply(item.getPullRequest().getUser(), item.getPullRequest().getBody()); - } else if (item1.getItemId() == R.id.edit) { - Activity activity = ActivityHelper.getActivity(v.getContext()); - if (activity == null) return false; - CreateIssueActivity.startForResult(activity, - item.getPullRequest().getLogin(), item.getPullRequest().getRepoId(), - item.getPullRequest(), isEnterprise()); - } else if (item1.getItemId() == R.id.share) { - ActivityHelper.shareUrl(v.getContext(), item.getPullRequest().getHtmlUrl()); - } - return true; - }); - popupMenu.show(); - } else { - onHandleReaction(v.getId(), item.getPullRequest().getNumber(), ReactionsProvider.HEADER); - } - } else if (item.getType() == TimelineModel.GROUPED_REVIEW) { - GroupedReviewModel reviewModel = item.getGroupedReview(); - if (v.getId() == R.id.addCommentPreview) { - EditReviewCommentModel model = new EditReviewCommentModel(); - model.setCommentPosition(-1); - model.setGroupPosition(position); - model.setInReplyTo(reviewModel.getId()); - getView().onReplyOrCreateReview(null, null, position, -1, model); - } - } - } +// if (getView() != null && getView().getPullRequest() != null) { +// if (item.getType() == TimelineModel.COMMENT) { +// PullRequest pullRequest = getView().getPullRequest(); +// if (v.getId() == R.id.commentMenu) { +// PopupMenu popupMenu = new PopupMenu(v.getContext(), v); +// popupMenu.inflate(R.menu.comments_menu); +// String username = Login.getUser().getLogin(); +// boolean isOwner = CommentsHelper.isOwner(username, pullRequest.getLogin(), item.getComment().getUser().getLogin()); +// popupMenu.getMenu().findItem(R.id.delete).setVisible(isOwner); +// popupMenu.getMenu().findItem(R.id.edit).setVisible(isOwner); +// popupMenu.setOnMenuItemClickListener(item1 -> { +// if (getView() == null) return false; +// if (item1.getItemId() == R.id.delete) { +// getView().onShowDeleteMsg(item.getComment().getId()); +// } else if (item1.getItemId() == R.id.reply) { +// getView().onReply(item.getComment().getUser(), item.getComment().getBody()); +// } else if (item1.getItemId() == R.id.edit) { +// getView().onEditComment(item.getComment()); +// } else if (item1.getItemId() == R.id.share) { +// ActivityHelper.shareUrl(v.getContext(), item.getComment().getHtmlUrl()); +// } +// return true; +// }); +// popupMenu.show(); +// } else { +// onHandleReaction(v.getId(), item.getComment().getId(), ReactionsProvider.COMMENT); +// } +// } else if (item.getType() == TimelineModel.EVENT) { +// IssueEvent issueEventModel = item.getEvent(); +// if (issueEventModel.getCommitUrl() != null) { +// SchemeParser.launchUri(v.getContext(), Uri.parse(issueEventModel.getCommitUrl())); +// } +// } else if (item.getType() == TimelineModel.HEADER) { +// if (v.getId() == R.id.commentMenu) { +// PopupMenu popupMenu = new PopupMenu(v.getContext(), v); +// popupMenu.inflate(R.menu.comments_menu); +// String username = Login.getUser().getLogin(); +// boolean isOwner = CommentsHelper.isOwner(username, item.getPullRequest().getLogin(), +// item.getPullRequest().getUser().getLogin()); +// popupMenu.getMenu().findItem(R.id.edit).setVisible(isOwner); +// popupMenu.setOnMenuItemClickListener(item1 -> { +// if (getView() == null) return false; +// if (item1.getItemId() == R.id.reply) { +// getView().onReply(item.getPullRequest().getUser(), item.getPullRequest().getBody()); +// } else if (item1.getItemId() == R.id.edit) { +// Activity activity = ActivityHelper.getActivity(v.getContext()); +// if (activity == null) return false; +// CreateIssueActivity.startForResult(activity, +// item.getPullRequest().getLogin(), item.getPullRequest().getRepoId(), +// item.getPullRequest(), isEnterprise()); +// } else if (item1.getItemId() == R.id.share) { +// ActivityHelper.shareUrl(v.getContext(), item.getPullRequest().getHtmlUrl()); +// } +// return true; +// }); +// popupMenu.show(); +// } else { +// onHandleReaction(v.getId(), item.getPullRequest().getNumber(), ReactionsProvider.HEADER); +// } +// } else if (item.getType() == TimelineModel.GROUPED_REVIEW) { +// GroupedReviewModel reviewModel = item.getGroupedReview(); +// if (v.getId() == R.id.addCommentPreview) { +// EditReviewCommentModel model = new EditReviewCommentModel(); +// model.setCommentPosition(-1); +// model.setGroupPosition(position); +// model.setInReplyTo(reviewModel.getId()); +// getView().onReplyOrCreateReview(null, null, position, -1, model); +// } +// } +// } } @Override public void onItemLongClick(int position, View v, TimelineModel item) { - if (getView() == null || getView().getPullRequest() == null) return; - if (item.getType() == TimelineModel.COMMENT || item.getType() == TimelineModel.HEADER) { - PullRequest pullRequest = getView().getPullRequest(); - String login = pullRequest.getLogin(); - String repoId = pullRequest.getRepoId(); - if (!InputHelper.isEmpty(login) && !InputHelper.isEmpty(repoId)) { - ReactionTypes type = ReactionTypes.get(v.getId()); - if (type != null) { - if (item.getType() == TimelineModel.HEADER) { - getView().showReactionsPopup(type, login, repoId, item.getPullRequest().getNumber(), ReactionsProvider.HEADER); - } else { - getView().showReactionsPopup(type, login, repoId, item.getComment().getId(), ReactionsProvider.COMMENT); - } - } else { - onItemClick(position, v, item); - } - } - } else { - onItemClick(position, v, item); - } +// if (getView() == null || getView().getPullRequest() == null) return; +// if (item.getType() == TimelineModel.COMMENT || item.getType() == TimelineModel.HEADER) { +// PullRequest pullRequest = getView().getPullRequest(); +// String login = pullRequest.getLogin(); +// String repoId = pullRequest.getRepoId(); +// if (!InputHelper.isEmpty(login) && !InputHelper.isEmpty(repoId)) { +// ReactionTypes type = ReactionTypes.get(v.getId()); +// if (type != null) { +// if (item.getType() == TimelineModel.HEADER) { +// getView().showReactionsPopup(type, login, repoId, item.getPullRequest().getNumber(), ReactionsProvider.HEADER); +// } else { +// getView().showReactionsPopup(type, login, repoId, item.getComment().getId(), ReactionsProvider.COMMENT); +// } +// } else { +// onItemClick(position, v, item); +// } +// } +// } else { +// onItemClick(position, v, item); +// } } @NonNull @Override public ArrayList getEvents() { @@ -298,28 +294,23 @@ public class PullRequestTimelinePresenter extends BasePresenter> observable; - if (page > 1) { - observable = RestProvider.getIssueService(isEnterprise()).getIssueComments(login, repoId, number, page) - .map(comments -> { - lastPage = comments != null ? comments.getLast() : 0; - return TimelineModel.construct(comments != null ? comments.getItems() : null); - }); - } else { - observable = Observable.zip(RestProvider.getIssueService(isEnterprise()).getTimeline(login, repoId, number), - RestProvider.getIssueService(isEnterprise()).getIssueComments(login, repoId, number, page), - RestProvider.getPullRequestService(isEnterprise()).getPullStatus(login, repoId, sha), - RestProvider.getReviewService(isEnterprise()).getReviews(login, repoId, number), - RestProvider.getReviewService(isEnterprise()).getPrReviewComments(login, repoId, number), - (issueEventPageable, commentPageable, statuses, reviews, reviewComments) -> { + Observable> timeline = RestProvider.getIssueService(isEnterprise()).getTimeline(login, repoId, number, page) + .flatMap(response -> { + lastPage = response != null ? response.getLast() : 0; + return TimelineConverter.convert(response != null ? response.getItems() : null); + }) + .toList() + .toObservable(); + if (page == 1) { + Observable status = RestProvider.getPullRequestService(isEnterprise()).getPullStatus(login, repoId, sha) + .map(statuses -> { if (statuses != null) { statuses.setMergable(isMergeable); } - lastPage = commentPageable != null ? commentPageable.getLast() : 0; - return TimelineModel.construct(commentPageable != null ? commentPageable.getItems() : null, - issueEventPageable.getItems(), statuses, reviews.getItems(), reviewComments.getItems()); + return statuses; }); + makeRestCall(status.map(TimelineModel::new), timelineModel -> sendToView(view -> view.onAddStatus(timelineModel))); } - makeRestCall(observable, models -> sendToView(view -> view.onNotifyAdapter(models, page))); + makeRestCall(timeline, timelineModels -> sendToView(view -> view.onNotifyAdapter(timelineModels, page))); } } diff --git a/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java b/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java index fa17830b..fd561761 100644 --- a/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java +++ b/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java @@ -75,15 +75,11 @@ public class AvatarLayout extends FrameLayout { this.isOrg = isOrg; this.isEnterprise = isEnterprise; avatar.setContentDescription(login); - if (url != null) { - if (login != null) { - TooltipCompat.setTooltipText(avatar, login); - } + if (login != null) { + TooltipCompat.setTooltipText(avatar, login); } else { avatar.setOnClickListener(null); - if (login != null) { - avatar.setOnLongClickListener(null); - } + avatar.setOnLongClickListener(null); } Glide.with(getContext()) .load(url) @@ -92,8 +88,7 @@ public class AvatarLayout extends FrameLayout { .dontAnimate() .into(avatar); } - - + private void setBackground() { if (PrefGetter.isRectAvatar()) { setBackgroundResource(R.drawable.rect_shape); diff --git a/app/src/main/res/layouts/row_layouts/layout/label_row_item.xml b/app/src/main/res/layouts/row_layouts/layout/label_row_item.xml index 62e3b0e5..0ba7da09 100644 --- a/app/src/main/res/layouts/row_layouts/layout/label_row_item.xml +++ b/app/src/main/res/layouts/row_layouts/layout/label_row_item.xml @@ -29,7 +29,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginStart="@dimen/spacing_xs_large" - android:ellipsize="end" + android:ellipsize="middle" android:maxLines="3" android:textColor="?android:textColorPrimary" android:transitionName="@string/title_transition" diff --git a/app/src/main/res/xml/behaviour_settings.xml b/app/src/main/res/xml/behaviour_settings.xml index 276a3bbf..7b5467a9 100644 --- a/app/src/main/res/xml/behaviour_settings.xml +++ b/app/src/main/res/xml/behaviour_settings.xml @@ -1,45 +1,38 @@ - - - diff --git a/build.gradle b/build.gradle index 7e5021a5..e389f670 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,7 @@ buildscript { butterKnifeVersion = '8.5.1' state_version = '1.1.0' lombokVersion = '1.12.6' - supportVersion = "26.0.0-beta2" -// supportVersion = "25.4.0" + supportVersion = "26.0.0" gms = "11.0.2" thirtyinchVersion = '0.8.0' retrofit = '2.3.0' @@ -24,10 +23,10 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.0-alpha7' + classpath 'com.android.tools.build:gradle:3.0.0-alpha8' classpath 'com.google.gms:google-services:3.0.0' classpath 'com.novoda:gradle-build-properties-plugin:0.3' - classpath 'com.dicedmelon.gradle:jacoco-android:0.1.1' + classpath 'com.dicedmelon.gradle:jacoco-android:0.1.2' classpath 'io.fabric.tools:gradle:1.22.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }