revamping of timeline to use different and more efficient api

This commit is contained in:
kosh 2017-07-26 23:24:32 +08:00
parent e9e0e4023b
commit 0ebcf9701c
31 changed files with 961 additions and 593 deletions

View File

@ -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<M> implements Parcelable {
public int first;
public int next;
public int prev;
public int last;
public int totalCount;
public boolean incompleteResults;
public List<M> 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<IssuesPageable> CREATOR = new Creator<IssuesPageable>() {
@Override public IssuesPageable createFromParcel(Parcel source) {return new IssuesPageable(source);}
@Override public IssuesPageable[] newArray(int size) {return new IssuesPageable[size];}
};
}

View File

@ -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;

View File

@ -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;

View File

@ -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<TimelineModel> construct(@Nullable List<Comment> commentList) {
ArrayList<TimelineModel> 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<TimelineModel> construct(@Nullable List<Comment> commentList, @Nullable List<IssueEvent> eventList) {
ArrayList<TimelineModel> 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<TimelineModel> construct(@Nullable List<Comment> commentList, @Nullable List<IssueEvent> eventList,
@Nullable PullRequestStatusModel status, @Nullable List<ReviewModel> reviews,
@Nullable List<ReviewCommentModel> reviewComments) {
ArrayList<TimelineModel> 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<TimelineModel> constructLabels(@NonNull List<IssueEvent> eventList) {
List<TimelineModel> models = new ArrayList<>();
Map<String, List<IssueEvent>> 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<String, List<IssueEvent>> stringListEntry : issueEventMap.entrySet()) {
List<LabelModel> labelModels = new ArrayList<>();
List<IssueEvent> 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<TimelineModel> constructReviews
(@NonNull List<ReviewModel> reviews, @Nullable List<ReviewCommentModel> comments) {
List<TimelineModel> 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<Integer, List<ReviewCommentModel>> mappedComments = Stream.of(comments)
.collect(Collectors.groupingBy(ReviewCommentModel::getOriginalPosition, LinkedHashMap::new,
Collectors.mapping(o -> o, toList())));
for (Map.Entry<Integer, List<ReviewCommentModel>> entry : mappedComments.entrySet()) {
List<ReviewCommentModel> 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<TimelineModel> CREATOR = new Creator<TimelineModel>() {
@ -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<List<TimelineModel>> construct(@Nullable List<Comment> 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;
}
}

View File

@ -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<AuthorModel> CREATOR = new Creator<AuthorModel>() {
@Override public AuthorModel createFromParcel(Parcel source) {return new AuthorModel(source);}
@Override public AuthorModel[] newArray(int size) {return new AuthorModel[size];}
};
}

View File

@ -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<CommentEvent> CREATOR = new Creator<CommentEvent>() {
@Override public CommentEvent createFromParcel(Parcel source) {return new CommentEvent(source);}
@Override public CommentEvent[] newArray(int size) {return new CommentEvent[size];}
};
}

View File

@ -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<ParentsModel> 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<GenericEvent> CREATOR = new Creator<GenericEvent>() {
@Override public GenericEvent createFromParcel(Parcel source) {return new GenericEvent(source);}
@Override public GenericEvent[] newArray(int size) {return new GenericEvent[size];}
};
}

View File

@ -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<ParentsModel> CREATOR = new Parcelable.Creator<ParentsModel>() {
@Override public ParentsModel createFromParcel(Parcel source) {return new ParentsModel(source);}
@Override public ParentsModel[] newArray(int size) {return new ParentsModel[size];}
};
}

View File

@ -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<SourceModel> CREATOR = new Parcelable.Creator<SourceModel>() {
@Override public SourceModel createFromParcel(Parcel source) {return new SourceModel(source);}
@Override public SourceModel[] newArray(int size) {return new SourceModel[size];}
};
}

View File

@ -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<Timeline> CREATOR = new Creator<Timeline>() {
@Override public Timeline createFromParcel(Parcel source) {return new Timeline(source);}
@Override public Timeline[] newArray(int size) {return new Timeline[size];}
};
}

View File

@ -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);
}
}

View File

@ -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<Pageable<IssueEvent>> 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<IssuesPageable<JsonObject>> 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<Issue> createIssue(@Path("owner") String owner, @Path("repo") String repo,
@Body IssueRequestModel issue);

View File

@ -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()))

View File

@ -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<TimelineModel> convert(@Nullable List<JsonObject> 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;
}
}

View File

@ -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);
}

View File

@ -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);
}
}
}
}

View File

@ -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<TimelineModel
return IssueTimelineViewHolder.newInstance(parent, this, isMerged);
} else if (viewType == TimelineModel.STATUS) {
return PullStatusViewHolder.newInstance(parent);
} else if (viewType == TimelineModel.REVIEW) {
return ReviewsViewHolder.newInstance(parent, this);
} else if (viewType == TimelineModel.GROUPED_REVIEW) {
return GroupedReviewsViewHolder.newInstance(parent, this, onToggleView, reactionsCallback,
reviewCommentCallback, repoOwner, poster);
} else if (viewType == TimelineModel.LINE_COMMENT) {
return ReviewCommentsViewHolder.newInstance(parent, this, onToggleView, reactionsCallback, repoOwner, poster);
// return ReviewsViewHolder.newInstance(parent, this);
}
// else if (viewType == TimelineModel.GROUPED_REVIEW) {
// return GroupedReviewsViewHolder.newInstance(parent, this, onToggleView, reactionsCallback,
// reviewCommentCallback, repoOwner, poster);
// }
return TimelineCommentsViewHolder.newInstance(parent, this, onToggleView, showEmojies,
reactionsCallback, repoOwner, poster);
}
@ -77,11 +78,15 @@ public class IssuePullsTimelineAdapter extends BaseRecyclerAdapter<TimelineModel
((IssueTimelineViewHolder) holder).bind(model);
} else if (model.getType() == TimelineModel.COMMENT) {
((TimelineCommentsViewHolder) holder).bind(model);
} else if (model.getType() == TimelineModel.REVIEW) {
((ReviewsViewHolder) holder).bind(model);
} else if (model.getType() == TimelineModel.GROUPED_REVIEW) {
((GroupedReviewsViewHolder) holder).bind(model);
} else {
} else if (model.getType() == TimelineModel.LINE_COMMENT) {
((ReviewCommentsViewHolder) holder).bind(model.getReviewComment());
}
// else if (model.getType() == TimelineModel.REVIEW) {
// ((ReviewsViewHolder) holder).bind(model);
// } else if (model.getType() == TimelineModel.GROUPED_REVIEW) {
// ((GroupedReviewsViewHolder) holder).bind(model);
// }
else {
if (model.getStatus() != null) ((PullStatusViewHolder) holder).bind(model.getStatus());
}
if (model.getType() != TimelineModel.COMMENT) {

View File

@ -7,11 +7,9 @@ import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.fastaccess.R;
import com.fastaccess.data.dao.GroupedReviewModel;
import com.fastaccess.data.dao.ReviewCommentModel;
import com.fastaccess.data.dao.TimelineModel;
import com.fastaccess.helper.ViewHelper;
import com.fastaccess.ui.adapter.ReviewCommentsAdapter;
import com.fastaccess.ui.adapter.callback.OnToggleView;
import com.fastaccess.ui.adapter.callback.ReactionsCallback;
import com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.timeline.timeline.PullRequestTimelineMvp;
@ -94,20 +92,20 @@ public class GroupedReviewsViewHolder extends BaseViewHolder<TimelineModel> 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);
}

View File

@ -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<TimelineModel> {
}
@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<TimelineModel> {
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()));
}

View File

@ -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<TimelineModel> {
}
@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() {

View File

@ -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<V extends BaseMvp.FAView, P extends BasePrese
@Nullable @BindView(R.id.drawer) public DrawerLayout drawer;
@Nullable @BindView(R.id.extrasNav) public NavigationView extraNav;
@Nullable @BindView(R.id.accountsNav) NavigationView accountsNav;
@Nullable @BindView(R.id.adView) AdView adView;
@State Bundle presenterStateBundle = new Bundle();
@ -113,7 +113,6 @@ public abstract class BaseActivity<V extends BaseMvp.FAView, P extends BasePrese
getPresenter().onRestoreInstanceState(presenterStateBundle);
}
setupToolbarAndStatusBar(toolbar);
showHideAds();
if (savedInstanceState == null) {
if (getIntent() != null) {
if (getIntent().getExtras() != null) {
@ -201,10 +200,12 @@ public abstract class BaseActivity<V extends BaseMvp.FAView, P extends BasePrese
@Override public void onRequireLogin() {
Toasty.warning(App.getInstance(), getString(R.string.unauthorized_user), Toast.LENGTH_LONG).show();
Glide glide = Glide.get(this);
glide.clearDiskCache();
glide.clearMemory();
PrefGetter.setToken(null);
PrefGetter.setEnterpriseUrl(null);
PrefGetter.setOtpCode(null);
PrefGetter.setEnterpriseOtpCode(null);
PrefGetter.resetEnterprise();
Login.logout();
Intent intent = new Intent(this, LoginChooserActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
@ -263,27 +264,6 @@ public abstract class BaseActivity<V extends BaseMvp.FAView, P extends BasePrese
@Override public void onScrollTop(int index) {}
@Override protected void onPause() {
if (adView != null && adView.isShown()) {
adView.pause();
}
super.onPause();
}
@Override protected void onResume() {
super.onResume();
if (adView != null && adView.isShown()) {
adView.resume();
}
}
@Override protected void onDestroy() {
if (adView != null && adView.isShown()) {
adView.destroy();
}
super.onDestroy();
}
@Override public boolean isEnterprise() {
return getPresenter() != null && getPresenter().isEnterprise();
}
@ -297,23 +277,6 @@ public abstract class BaseActivity<V extends BaseMvp.FAView, P extends BasePrese
setTaskDescription(new ActivityManager.TaskDescription(name, null, ViewHelper.getPrimaryDarkColor(this)));
}
protected void showHideAds() {
if (adView != null) {
boolean isAdsEnabled = PrefGetter.isAdsEnabled();
if (isAdsEnabled) {
adView.setVisibility(View.VISIBLE);
MobileAds.initialize(this, getString(R.string.banner_ad_unit_id));
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(getString(R.string.test_device_id))
.build();
adView.loadAd(adRequest);
} else {
adView.destroy();
adView.setVisibility(View.GONE);
}
}
}
protected void selectHome(boolean hideRepo) {
if (extraNav != null) {
if (hideRepo) {

View File

@ -101,7 +101,10 @@ public class BasePresenter<V extends BaseMvp.FAView> extends TiPresenter<V> 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);
})
);
}

View File

@ -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<CommitCommentsMvp.View> 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<CommitCommentsMvp.View> 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);

View File

@ -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<List<TimelineModel>> 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<List<TimelineModel>> 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)));
}
}

View File

@ -305,16 +305,16 @@ public class PullRequestTimelineFragment extends BaseFragment<PullRequestTimelin
@Override public void onRemoveReviewComment(int groupPosition, int commentPosition) {
hideProgress();
TimelineModel timelineModel = adapter.getItem(groupPosition);
if (timelineModel != null && timelineModel.getGroupedReview() != null) {
if (timelineModel.getGroupedReview().getComments() != null) {
timelineModel.getGroupedReview().getComments().remove(commentPosition);
if (timelineModel.getGroupedReview().getComments().isEmpty()) {
adapter.removeItem(groupPosition);
} else {
adapter.notifyItemChanged(groupPosition);
}
}
}
// if (timelineModel != null && timelineModel.getGroupedReview() != null) {
// if (timelineModel.getGroupedReview().getComments() != null) {
// timelineModel.getGroupedReview().getComments().remove(commentPosition);
// if (timelineModel.getGroupedReview().getComments().isEmpty()) {
// adapter.removeItem(groupPosition);
// } else {
// adapter.notifyItemChanged(groupPosition);
// }
// }
// }
}
@Override public void onSetHeader(@NonNull TimelineModel timelineModel) {
@ -336,6 +336,10 @@ public class PullRequestTimelineFragment extends BaseFragment<PullRequestTimelin
onSetHeader(TimelineModel.constructHeader(getPullRequest()));
}
@Override public void onAddStatus(@NonNull TimelineModel timelineModel) {
adapter.addItem(timelineModel, 1);
}
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
@ -372,21 +376,21 @@ public class PullRequestTimelineFragment extends BaseFragment<PullRequestTimelin
return;
}
TimelineModel timelineModel = adapter.getItem(commentModel.getGroupPosition());
if (isNew) {
if (timelineModel.getGroupedReview() != null && timelineModel.getGroupedReview().getComments() != null) {
timelineModel.getGroupedReview().getComments().add(commentModel.getCommentModel());
adapter.notifyItemChanged(commentModel.getGroupPosition());
} else {
onRefresh();
}
} else {
if (timelineModel.getGroupedReview() != null && timelineModel.getGroupedReview().getComments() != null) {
timelineModel.getGroupedReview().getComments().set(commentModel.getCommentPosition(), commentModel.getCommentModel());
adapter.notifyItemChanged(commentModel.getGroupPosition());
} else {
onRefresh();
}
}
// if (isNew) {
// if (timelineModel.getGroupedReview() != null && timelineModel.getGroupedReview().getComments() != null) {
// timelineModel.getGroupedReview().getComments().add(commentModel.getCommentModel());
// adapter.notifyItemChanged(commentModel.getGroupPosition());
// } else {
// onRefresh();
// }
// } else {
// if (timelineModel.getGroupedReview() != null && timelineModel.getGroupedReview().getComments() != null) {
// timelineModel.getGroupedReview().getComments().set(commentModel.getCommentPosition(), commentModel.getCommentModel());
// adapter.notifyItemChanged(commentModel.getGroupPosition());
// } else {
// onRefresh();
// }
// }
}
} else {
onRefresh(); // bundle size is too large? refresh the api

View File

@ -69,6 +69,8 @@ public interface PullRequestTimelineMvp {
@Nullable PullRequest getPullRequest();
void onUpdateHeader();
void onAddStatus(@NonNull TimelineModel timelineModel);
}
interface Presenter extends BaseMvp.FAPresenter, BaseViewHolder.OnItemClickListener<TimelineModel>,

View File

@ -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<PullRequestTimel
private int lastPage = Integer.MAX_VALUE;
@Override public void onItemClick(int position, View v, TimelineModel item) {
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);
}
}
}
// 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<TimelineModel> getEvents() {
@ -298,28 +294,23 @@ public class PullRequestTimelinePresenter extends BasePresenter<PullRequestTimel
}
private void loadEverything(String login, String repoId, int number, @NonNull String sha, boolean isMergeable, int page) {
Observable<List<TimelineModel>> 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<List<TimelineModel>> 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<PullRequestStatusModel> 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)));
}
}

View File

@ -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);

View File

@ -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"

View File

@ -1,45 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="false"
android:key="enable_ads"
android:icon="@drawable/ic_heart"
android:title="@string/enable_ads"/>
<SwitchPreference
android:defaultValue="true"
android:key="sent_via_enabled"
android:icon="@drawable/ic_checkbox"
android:key="sent_via_enabled"
android:summary="@string/enable_signature_box_summary"
android:title="@string/enable_signature_box"/>
<SwitchPreference
android:defaultValue="false"
android:key="sent_via"
android:icon="@drawable/ic_edit"
android:key="sent_via"
android:summary="@string/enable_signature_summary"
android:title="@string/enable_signature"/>
<SwitchPreference
android:defaultValue="false"
android:key="disable_auto_play_gif"
android:icon="@drawable/ic_image"
android:key="disable_auto_play_gif"
android:summary="@string/disable_auto_gif_summary"
android:title="@string/disable_auto_gif_title"/>
<SwitchPreference
android:defaultValue="false"
android:key="wrap_code"
android:icon="@drawable/ic_wrap_text"
android:key="wrap_code"
android:summary="@string/wrap_code_summary"
android:title="@string/wrap_code_title"/>
<SwitchPreference
android:defaultValue="false"
android:key="back_button"
android:icon="@drawable/ic_back"
android:key="back_button"
android:summary="@string/back_button_summary"
android:title="@string/back_button_title"/>

View File

@ -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"
}