Merge pull request #883 from k0shk0sh/pullrequest_timeline

Pullrequest timeline
This commit is contained in:
Kosh Sergani 2017-08-24 09:36:50 +02:00 committed by GitHub
commit d2b02cc78d
373 changed files with 43725 additions and 3778 deletions

View File

@ -8,6 +8,14 @@ jdk: oraclejdk8
sudo: required
before_install:
# Skip build if the commit message contains [skip travis] or [travis skip]
- >
echo "$TRAVIS_COMMIT_MESSAGE"
| grep -E '\[(skip travis|travis skip)\]'
&& echo "[skip travis] has been found, exiting."
&& exit 0 || true
before_script:
- (while sleep 3; do echo "y"; done) | $ANDROID_HOME/tools/bin/sdkmanager "platform-tools" "extras;android;m2repository" "extras;google;m2repository" "build-tools;26.0.0" "platforms;android-26"
@ -27,5 +35,10 @@ after_success:
- bash <(curl -s https://codecov.io/bash)
notifications:
slack: fasthub:mjJWGD8UpgSgKawKa5OqMNlR
email: false
webhooks:
urls:
- https://discordapp.com/api/webhooks/341508199612153857/_86ddhBkrka3SaPzYJtL2LBH-sKeVI_tSAp0L2h4cPoy2m4CLa4lpqqJu4rjxHs86V0i
on_success: always
on_failure: always
on_start: never
email: false

View File

@ -32,7 +32,7 @@ We have configured snapshots of FastHub, which can be downloaded from [AppVeyor
- Markdown and code highlighting support
- Notifications overview and "Mark all as read"
- Search users/orgs, repos, issues/prs & code.
- Pinned Repos
- FastHub & GitHub Pinned Repos
- Trending
- Wiki
- **Repositories**
@ -65,6 +65,7 @@ We have configured snapshots of FastHub, which can be downloaded from [AppVeyor
- Comment on Commits/Gists
- Manage Commit/Gist comments
- Create/Delete Gists
- Edit Gist & Gist Files
- React to Commit comments with reactions
- Comment on line number in Files/Code changes.
- **Orgs**
@ -73,6 +74,7 @@ We have configured snapshots of FastHub, which can be downloaded from [AppVeyor
- Teams & Teams repos
- Repos
- **Users**
- GitHub Pinned Repos
- Follow/Unfollow users
- View user feeds
- Contribution graph.
@ -177,7 +179,7 @@ Read the [**contribution guide**](.github/CONTRIBUTING.md) for more detailed inf
[Google+](https://plus.google.com/+CookiconsDesign) | [Twitter](https://twitter.com/mcookie)
Designer website [Cookicons](https://cookicons.co/).
**OLD FastHub** logo is designed by **Kevin Aguilar**.
**OLD FastHub** logo was designed by **Kevin Aguilar**.
[Google+](https://plus.google.com/+KevinAguilarC) | [Twitter](https://twitter.com/kevttob)
Designer at [221 Pixels](https://www.221pixels.com/).
Laus Deo Semper

View File

@ -1,8 +1,9 @@
apply plugin: 'com.android.application'
apply plugin: 'com.apollographql.android'
apply plugin: 'kotlin-android'
apply plugin: 'com.novoda.build-properties'
apply plugin: 'jacoco-android'
apply plugin: 'io.fabric'
if (isProduction) apply plugin: 'io.fabric'
buildProperties {
notThere {
@ -23,7 +24,7 @@ android {
}
}
compileSdkVersion 26
buildToolsVersion "26.0.0"
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "com.fastaccess.github"
minSdkVersion 21
@ -58,7 +59,6 @@ android {
}
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
ext.alwaysUpdateBuildId = false
}
}
@ -110,13 +110,12 @@ repositories {
maven { url "https://clojars.org/repo/" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven { url "https://jitpack.io" }
maven { url 'https://maven.fabric.io/public' }
if (isProduction) maven { url 'https://maven.fabric.io/public' }
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "com.android.support:appcompat-v7:${supportVersion}"
implementation 'com.jaredrummler:android-device-names:1.1.4'
implementation "com.android.support:design:${supportVersion}"
implementation "com.android.support:cardview-v7:${supportVersion}"
implementation "com.android.support:recyclerview-v7:${supportVersion}"
@ -129,10 +128,9 @@ dependencies {
implementation "com.squareup.retrofit2:converter-gson:${retrofit}"
implementation "com.squareup.retrofit2:adapter-rxjava2:${retrofit}"
implementation "com.github.bumptech.glide:glide:3.7.0"
// implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
implementation 'cn.gavinliu.android.lib:ShapedImageView:0.8.3'
implementation "com.jakewharton:butterknife:${butterKnifeVersion}"
implementation 'it.sephiroth.android.library.bottomnavigation:bottom-navigation:2.0.1-rc1'
implementation 'it.sephiroth.android.library.bottomnavigation:bottom-navigation:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
@ -146,26 +144,34 @@ dependencies {
implementation 'com.github.nightwhistler:HtmlSpanner:0.4'
implementation 'net.sourceforge.htmlcleaner:htmlcleaner:2.2'
implementation 'com.github.matthiasrobbers:shortbread:1.0.1'
implementation 'com.atlassian.commonmark:commonmark:0.9.0'
implementation "com.atlassian.commonmark:commonmark:${commonmark}"
implementation "com.atlassian.commonmark:commonmark-ext-autolink:${commonmark}"
implementation "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:${commonmark}"
implementation "com.atlassian.commonmark:commonmark-ext-gfm-tables:${commonmark}"
implementation "com.atlassian.commonmark:commonmark-ext-ins:${commonmark}"
implementation "com.atlassian.commonmark:commonmark-ext-yaml-front-matter:${commonmark}"
implementation "com.google.firebase:firebase-messaging:${gms}"
implementation "com.google.android.gms:play-services-ads:${gms}"
implementation "com.google.firebase:firebase-database:${gms}"
implementation "com.google.android.gms:play-services-base:${gms}"
implementation('com.github.b3er.rxfirebase:firebase-database-kotlin:11.2.0') { transitive = false }
implementation('com.github.b3er.rxfirebase:firebase-database:11.2.0') { transitive = false }
implementation 'com.firebase:firebase-jobdispatcher:0.7.0'
implementation('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') { transitive = true }
if (isProduction) implementation('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') { transitive = true }
implementation "com.github.miguelbcr:RxBillingService:0.0.3"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'org.jsoup:jsoup:1.10.2'
implementation "com.evernote:android-state:${state_version}"
implementation "petrov.kristiyan:colorpicker-library:1.1.4"
implementation 'com.apollographql.apollo:apollo-rx2-support:0.4.0'
implementation 'com.jaredrummler:android-device-names:1.1.4'
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:2.1.0'
compileOnly "org.projectlombok:lombok:${lombokVersion}"
kapt "io.requery:requery-processor:${requery}"
kapt "org.projectlombok:lombok:${lombokVersion}"
kapt "com.evernote:android-state-processor:${state_version}"
kapt "com.jakewharton:butterknife-compiler:${butterKnifeVersion}"
kapt 'com.github.matthiasrobbers:shortbread-compiler:1.0.1'
kapt "org.projectlombok:lombok:${lombokVersion}"
kapt "io.requery:requery-processor:${requery}"
// testImplementation "net.grandcentrix.thirtyinch:thirtyinch-test:$thirtyinchVersion"
testImplementation "junit:junit:${junitVersion}"
testImplementation "org.mockito:mockito-core:${mockitoVersion}"
@ -178,4 +184,5 @@ dependencies {
androidTestImplementation "com.android.support.test.espresso:espresso-core:${espresseVersion}"
}
apply plugin: 'ManifestClasspath'
apply plugin: 'com.google.gms.google-services'

View File

@ -83,7 +83,8 @@
<activity
android:name=".ui.modules.repos.issues.issue.details.IssuePagerActivity"
android:label="@string/issue"
android:parentActivityName=".ui.modules.repos.RepoPagerActivity">
android:parentActivityName=".ui.modules.repos.RepoPagerActivity"
android:windowSoftInputMode="stateAlwaysHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.modules.repos.RepoPagerActivity"/>
@ -103,7 +104,8 @@
<activity
android:name=".ui.modules.repos.code.commit.details.CommitPagerActivity"
android:label="@string/commit"
android:parentActivityName=".ui.modules.repos.RepoPagerActivity">
android:parentActivityName=".ui.modules.repos.RepoPagerActivity"
android:windowSoftInputMode="stateAlwaysHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.modules.repos.RepoPagerActivity"/>
@ -115,7 +117,8 @@
<activity
android:name=".ui.modules.editor.EditorActivity"
android:configChanges="keyboard|orientation|screenSize"
android:label="@string/markdown"/>
android:label="@string/markdown"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name=".ui.modules.gists.create.CreateGistActivity"
android:configChanges="keyboard|orientation|screenSize"
@ -123,7 +126,8 @@
<activity
android:name=".ui.modules.gists.gist.GistActivity"
android:label="@string/gist"
android:parentActivityName=".ui.modules.main.MainActivity">
android:parentActivityName=".ui.modules.main.MainActivity"
android:windowSoftInputMode="stateAlwaysHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.modules.main.MainActivity"/>
@ -170,8 +174,14 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.modules.repos.RepoPagerActivity"/>
</activity>
<activity android:name=".ui.modules.settings.SettingsActivity"/>
<activity android:name=".ui.modules.settings.category.SettingsCategoryActivity"/>
<activity
android:name=".ui.modules.settings.SettingsActivity"
android:configChanges="keyboard|orientation|screenSize"/>
<activity
android:name=".ui.modules.settings.category.SettingsCategoryActivity"
android:configChanges="keyboard|orientation|screenSize"/>
<activity
android:name=".ui.modules.repos.code.releases.ReleasesListActivity"
android:label="@string/releases"

View File

@ -0,0 +1,28 @@
query getPinnedRepos($login: String!) {
user(login: $login) {
pinnedRepositories(first: 100) {
edges {
node {
name
url
issues(states: OPEN) {
totalCount
}
pullRequests(states: OPEN) {
totalCount
}
stargazers {
totalCount
}
forks {
totalCount
}
primaryLanguage {
name
color
}
}
}
}
}
}

View File

@ -0,0 +1,521 @@
query PullRequestTimeline($owner: String!, $name: String!, $number: Int!, $page: String) {
repository(owner: $owner, name: $name) {
pullRequest(number: $number) {
pullRequestCommits: commits(last: 1) {
pullRequestCommit: nodes {
commit {
status {
state
contexts {
state
context
description
targetUrl
}
}
}
}
}
timeline(first: 30 after: $page) {
edges {
cursor
}
pageInfo {
hasNextPage
startCursor
endCursor
}
totalCount
nodes {
... on Commit {
id
oid
url
committedDate
messageHeadline
status {
state
}
author {
name
user {
login
url
avatarUrl
}
}
}
... on CommitCommentThread {
path
position
commit {
oid
}
comments(first: 30) {
edges {
node {
id
authorAssociation
bodyHTML
body
createdAt
reactionGroups {
viewerHasReacted
content
users {
totalCount
}
}
lastEditedAt
author {
avatarUrl
login
url
}
}
}
}
}
... on PullRequestReview {
id
url
bodyHTML
submittedAt
createdAt
state
author {
login
url
avatarUrl
}
comments(first: 30) {
edges {
node {
id
authorAssociation
bodyHTML
diffHunk
createdAt
url
originalPosition
path
position
author {
login
avatarUrl
url
}
reactionGroups {
viewerHasReacted
content
users {
totalCount
}
}
}
}
}
}
... on IssueComment {
id
bodyHTML
createdAt
updatedAt
viewerCanReact
viewerCanDelete
viewerCanUpdate
viewerDidAuthor
authorAssociation
lastEditedAt
author {
login
url
avatarUrl
}
reactionGroups {
viewerHasReacted
content
users {
totalCount
}
}
}
... on ClosedEvent {
createdAt
actor {
login
avatarUrl
url
}
commit {
oid
url
messageHeadline
}
}
... on ReopenedEvent {
createdAt
actor {
login
avatarUrl
url
}
}
... on MergedEvent {
id
url
createdAt
mergeRefName
commit {
oid
}
actor {
avatarUrl
login
url
}
}
... on ReferencedEvent {
id
createdAt
isCrossRepository
isDirectReference
actor {
avatarUrl
login
url
}
commitRepository {
nameWithOwner
url
}
commit {
oid
}
subject {
__typename
... on Issue {
title
number
}
... on PullRequest {
title
number
}
}
}
... on AssignedEvent {
id
createdAt
actor {
login
avatarUrl
url
}
user {
avatarUrl
login
url
}
}
... on UnassignedEvent {
id
createdAt
actor {
login
avatarUrl
url
}
user {
avatarUrl
login
url
}
}
... on LabeledEvent {
createdAt
actor {
login
avatarUrl
url
}
label {
color
name
}
}
... on UnlabeledEvent {
createdAt
actor {
login
avatarUrl
url
}
label {
color
name
}
}
... on MilestonedEvent {
createdAt
id
milestoneTitle
actor {
login
avatarUrl
url
}
}
... on DemilestonedEvent {
createdAt
id
milestoneTitle
actor {
login
avatarUrl
url
}
}
... on RenamedTitleEvent {
id
createdAt
currentTitle
previousTitle
actor {
login
avatarUrl
url
}
}
... on LockedEvent {
createdAt
actor {
login
avatarUrl
url
}
}
... on UnlockedEvent {
createdAt
actor {
login
avatarUrl
url
}
}
... on DeployedEvent {
createdAt
actor {
login
avatarUrl
url
}
ref {
name
prefix
}
deployment {
createdAt
state
creator {
avatarUrl
login
url
}
latestStatus {
description
environmentUrl
state
}
statuses(first: 30) {
edges {
node {
creator {
avatarUrl
login
url
}
logUrl
state
description
environmentUrl
}
}
}
}
}
... on HeadRefDeletedEvent {
createdAt
headRefName
actor {
avatarUrl
login
url
}
}
... on HeadRefRestoredEvent {
actor {
avatarUrl
login
url
}
createdAt
pullRequest {
number
headRefName
}
}
... on HeadRefForcePushedEvent {
createdAt
ref {
name
prefix
}
actor {
login
avatarUrl
url
}
afterCommit {
oid
url
}
beforeCommit {
oid
url
status {
state
contexts {
context
createdAt
description
state
targetUrl
creator {
avatarUrl
login
url
}
}
}
}
}
... on BaseRefForcePushedEvent {
createdAt
ref {
name
prefix
}
actor {
login
avatarUrl
url
}
afterCommit {
oid
url
}
beforeCommit {
oid
url
status {
state
contexts {
context
createdAt
description
state
targetUrl
creator {
avatarUrl
login
url
}
}
}
}
}
... on ReviewRequestedEvent {
id
createdAt
actor {
avatarUrl
login
url
}
subject {
avatarUrl
login
url
}
}
... on ReviewRequestRemovedEvent {
id
createdAt
actor {
avatarUrl
login
url
}
subject {
avatarUrl
login
url
}
}
... on ReviewDismissedEvent {
id
createdAt
messageHtml
previousReviewState
review {
id
submittedAt
authorAssociation
bodyHTML
state
viewerDidAuthor
author {
login
avatarUrl
url
}
comments(first: 30) {
edges {
node {
id
authorAssociation
bodyHTML
diffHunk
createdAt
url
originalPosition
path
position
author {
login
avatarUrl
url
}
reactionGroups {
viewerHasReacted
content
users {
totalCount
}
}
}
}
}
}
}
}
}
}
}
rateLimit {
cost
remaining
limit
}
}

File diff suppressed because it is too large Load Diff

View File

@ -4,17 +4,18 @@ import android.app.Application;
import android.support.annotation.NonNull;
import android.support.v7.preference.PreferenceManager;
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore;
import com.apollographql.apollo.ApolloClient;
import com.fastaccess.data.dao.model.Models;
import com.fastaccess.helper.DeviceNameGetter;
import com.fastaccess.helper.PrefGetter;
import com.fastaccess.helper.TypeFaceHelper;
import com.fastaccess.provider.colors.ColorsProvider;
import com.fastaccess.provider.emoji.EmojiManager;
import com.fastaccess.provider.fabric.FabricProvider;
import com.fastaccess.provider.rest.RestProvider;
import com.fastaccess.provider.tasks.notification.NotificationSchedulerJobTask;
import com.miguelbcr.io.rx_billing_service.RxBillingService;
import io.fabric.sdk.android.Fabric;
import io.requery.Persistable;
import io.requery.android.sqlite.DatabaseSource;
import io.requery.meta.EntityModel;
@ -33,6 +34,7 @@ import shortbread.Shortbread;
public class App extends Application {
private static App instance;
private ReactiveEntityStore<Persistable> dataStore;
private ApolloClient apolloClient;
@Override public void onCreate() {
super.onCreate();
@ -45,7 +47,7 @@ public class App extends Application {
}
private void init() {
initFabric();
FabricProvider.initFabric(this);
RxBillingService.register(this);
deleteDatabase("database.db");
getDataStore();//init requery before anything.
@ -58,16 +60,6 @@ public class App extends Application {
DeviceNameGetter.getInstance().loadDevice();
}
private void initFabric() {
Fabric fabric = new Fabric.Builder(this)
.kits(new Crashlytics.Builder()
.core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
.build())
.debuggable(BuildConfig.DEBUG)
.build();
Fabric.with(fabric);
}
private void setupPreference() {
PreferenceManager.setDefaultValues(this, R.xml.fasthub_settings, false);
PreferenceManager.setDefaultValues(this, R.xml.about_settings, false);
@ -89,4 +81,14 @@ public class App extends Application {
}
return dataStore;
}
public ApolloClient getApolloClient() {
if (apolloClient == null) {
apolloClient = ApolloClient.builder()
.serverUrl("https://" + (PrefGetter.isEnterprise() ? PrefGetter.getEnterpriseUrl() : "api.github.com") + "/graphql")
.okHttpClient(RestProvider.provideOkHttpClient())
.build();
}
return apolloClient;
}
}

View File

@ -30,7 +30,7 @@ public class AccessTokenModel implements Parcelable {
dest.writeString(this.tokenType);
}
protected AccessTokenModel(Parcel in) {
private AccessTokenModel(Parcel in) {
this.id = in.readLong();
this.token = in.readString();
this.hashedToken = in.readString();

View File

@ -22,7 +22,7 @@ import lombok.Setter;
dest.writeString(this.label);
}
protected AppLanguageModel(Parcel in) {
private AppLanguageModel(Parcel in) {
this.value = in.readString();
this.label = in.readString();
}

View File

@ -40,7 +40,7 @@ public class AuthModel implements Parcelable {
dest.writeString(this.otpCode);
}
protected AuthModel(Parcel in) {
private AuthModel(Parcel in) {
this.clientId = in.readString();
this.clientSecret = in.readString();
this.redirectUri = in.readString();

View File

@ -37,7 +37,7 @@ public class BranchesModel implements Parcelable {
dest.writeByte(this.isTag ? (byte) 1 : (byte) 0);
}
protected BranchesModel(Parcel in) {
private BranchesModel(Parcel in) {
this.name = in.readString();
this.commit = in.readParcelable(Commit.class.getClassLoader());
this.protectedBranch = in.readByte() != 0;

View File

@ -13,11 +13,11 @@ import lombok.Setter;
*/
@Getter @Setter public class CommentRequestModel implements Parcelable {
private String body;
@SerializedName("in_reply_to") private Long inReplyTo;
private String path;
private Integer position;
private Integer line;
public String body;
@SerializedName("in_reply_to") public Long inReplyTo;
public String path;
public Integer position;
public Integer line;
public CommentRequestModel() {}
@ -44,7 +44,7 @@ import lombok.Setter;
dest.writeValue(this.line);
}
protected CommentRequestModel(Parcel in) {
private CommentRequestModel(Parcel in) {
this.body = in.readString();
this.inReplyTo = (Long) in.readValue(Long.class.getClassLoader());
this.path = in.readString();

View File

@ -26,7 +26,7 @@ import lombok.Setter;
public CommitCountModel() {}
protected CommitCountModel(Parcel in) {
private CommitCountModel(Parcel in) {
this.all = new ArrayList<Integer>();
in.readList(this.all, Integer.class.getClassLoader());
this.owner = new ArrayList<Integer>();

View File

@ -23,7 +23,7 @@ import lombok.Setter;
public List<CommitLinesModel> linesModel;
public CommitFileModel commitFileModel;
public CommitFileChanges() {}
private CommitFileChanges() {}
public static Observable<CommitFileChanges> constructToObservable(@Nullable List<CommitFileModel> files) {
if (files == null || files.isEmpty()) return Observable.empty();
@ -56,7 +56,7 @@ import lombok.Setter;
dest.writeParcelable(this.commitFileModel, flags);
}
protected CommitFileChanges(Parcel in) {
private CommitFileChanges(Parcel in) {
this.linesModel = in.createTypedArrayList(CommitLinesModel.CREATOR);
this.commitFileModel = in.readParcelable(CommitFileModel.class.getClassLoader());
}

View File

@ -100,7 +100,7 @@ import static com.fastaccess.ui.widgets.DiffLineSpan.HUNK_TITLE;
dest.writeInt(this.position);
}
protected CommitLinesModel(Parcel in) {
private CommitLinesModel(Parcel in) {
this.text = in.readString();
this.color = in.readInt();
this.leftLineNo = in.readInt();

View File

@ -19,23 +19,23 @@ import lombok.Setter;
public class CreateGistModel implements Parcelable {
private HashMap<String, FilesListModel> files;
private String description;
@SerializedName("public") private boolean publicGist;
@SerializedName("public") private Boolean publicGist;
@Override public int describeContents() { return 0; }
@Override public void writeToParcel(Parcel dest, int flags) {
dest.writeSerializable(this.files);
dest.writeString(this.description);
dest.writeByte(this.publicGist ? (byte) 1 : (byte) 0);
dest.writeValue(this.publicGist);
}
@SuppressWarnings({"WeakerAccess", "unchecked"}) protected CreateGistModel(Parcel in) {
@SuppressWarnings("unchecked") private CreateGistModel(Parcel in) {
this.files = (HashMap<String, FilesListModel>) in.readSerializable();
this.description = in.readString();
this.publicGist = in.readByte() != 0;
this.publicGist = (Boolean) in.readValue(Boolean.class.getClassLoader());
}
public static final Parcelable.Creator<CreateGistModel> CREATOR = new Parcelable.Creator<CreateGistModel>() {
public static final Creator<CreateGistModel> CREATOR = new Creator<CreateGistModel>() {
@Override public CreateGistModel createFromParcel(Parcel source) {return new CreateGistModel(source);}
@Override public CreateGistModel[] newArray(int size) {return new CreateGistModel[size];}

View File

@ -14,11 +14,11 @@ import lombok.Setter;
@Getter @Setter public class EditReviewCommentModel implements Parcelable {
private int groupPosition;
private int commentPosition;
private String comment;
private ReviewCommentModel commentModel;
@SerializedName("in_reply_to") private long inReplyTo;
public int groupPosition;
public int commentPosition;
public String comment;
public ReviewCommentModel commentModel;
@SerializedName("in_reply_to") public long inReplyTo;
public EditReviewCommentModel() {}
@ -33,7 +33,7 @@ import lombok.Setter;
dest.writeLong(this.inReplyTo);
}
protected EditReviewCommentModel(Parcel in) {
private EditReviewCommentModel(Parcel in) {
this.groupPosition = in.readInt();
this.commentPosition = in.readInt();
this.comment = in.readString();

View File

@ -6,22 +6,24 @@ import android.os.Parcelable;
import java.io.Serializable;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Created by Kosh on 12 Nov 2016, 11:09 AM
*/
@Getter @Setter @NoArgsConstructor
@Getter @Setter
public class FilesListModel implements Parcelable, Serializable {
private String filename;
private String type;
private String rawUrl;
private long size;
private String content;
private boolean needFetching;
private String language;
public String filename;
public String type;
public String rawUrl;
public Long size;
public String content;
public Boolean needFetching;
public String language;
public FilesListModel() {
}
@Override public int describeContents() { return 0; }
@ -29,9 +31,9 @@ public class FilesListModel implements Parcelable, Serializable {
dest.writeString(this.filename);
dest.writeString(this.type);
dest.writeString(this.rawUrl);
dest.writeLong(this.size);
dest.writeValue(this.size);
dest.writeString(this.content);
dest.writeByte(this.needFetching ? (byte) 1 : (byte) 0);
dest.writeValue(this.needFetching);
dest.writeString(this.language);
}
@ -39,9 +41,9 @@ public class FilesListModel implements Parcelable, Serializable {
this.filename = in.readString();
this.type = in.readString();
this.rawUrl = in.readString();
this.size = in.readLong();
this.size = (Long) in.readValue(Long.class.getClassLoader());
this.content = in.readString();
this.needFetching = in.readByte() != 0;
this.needFetching = (Boolean) in.readValue(Boolean.class.getClassLoader());
this.language = in.readString();
}

View File

@ -146,7 +146,7 @@ public class FilterOptionsModel implements Parcelable {
dest.writeByte(this.isOrg ? (byte) 1 : (byte) 0);
}
protected FilterOptionsModel(Parcel in) {
private FilterOptionsModel(Parcel in) {
this.type = in.readString();
this.sort = in.readString();
this.sortDirection = in.readString();

View File

@ -152,8 +152,7 @@ import lombok.Setter;
}
@NonNull public static List<FragmentPagerAdapterModel> buildForGist(@NonNull Context context, @NonNull Gist gistsModel) {
return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.files), GistFilesListFragment.newInstance(gistsModel.getFiles())),
return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.files), GistFilesListFragment.newInstance(gistsModel.getFilesAsList(), false)),
new FragmentPagerAdapterModel(context.getString(R.string.comments), GistCommentsFragment.newInstance(gistsModel.getGistId())))
.collect(Collectors.toList());
}

View File

@ -33,7 +33,7 @@ import static com.annimon.stream.Collectors.toList;
private Notification notification;
private Date date;
public GroupedNotificationModel(Repo repo) {
private GroupedNotificationModel(Repo repo) {
this.type = HEADER;
this.repo = repo;
}

View File

@ -6,14 +6,11 @@ import android.os.Parcelable;
import java.util.Date;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
/**
* Created by Kosh on 07 May 2017, 5:08 PM
*/
@Getter @Setter public class GroupedReviewModel implements Parcelable {
public class GroupedReviewModel implements Parcelable {
private int position; //to group with!
private String diffText;
@ -22,9 +19,56 @@ import lombok.Setter;
private long id;
private List<ReviewCommentModel> comments;
public GroupedReviewModel() {}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
public String getDiffText() {
return diffText;
}
public void setDiffText(String diffText) {
this.diffText = diffText;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public List<ReviewCommentModel> getComments() {
return comments;
}
public void setComments(List<ReviewCommentModel> comments) {
this.comments = comments;
}
@Override public int describeContents() { return 0; }
@Override public void writeToParcel(Parcel dest, int flags) {
@ -36,7 +80,7 @@ import lombok.Setter;
dest.writeTypedList(this.comments);
}
protected GroupedReviewModel(Parcel in) {
private GroupedReviewModel(Parcel in) {
this.position = in.readInt();
this.diffText = in.readString();
long tmpDate = in.readLong();

View File

@ -25,7 +25,7 @@ import lombok.Setter;
public ImgurReponseModel() {}
protected ImgurReponseModel(Parcel in) {
private ImgurReponseModel(Parcel in) {
this.success = in.readByte() != 0;
this.status = in.readInt();
this.data = in.readParcelable(ImgurImage.class.getClassLoader());
@ -52,7 +52,7 @@ import lombok.Setter;
dest.writeString(this.link);
}
protected ImgurImage(Parcel in) {
private ImgurImage(Parcel in) {
this.title = in.readString();
this.description = in.readString();
this.link = in.readString();

View File

@ -55,7 +55,7 @@ public class IssueEventAdapterModel implements Parcelable {
dest.writeParcelable(this.issueModel, flags);
}
protected IssueEventAdapterModel(Parcel in) {
private IssueEventAdapterModel(Parcel in) {
this.type = in.readInt();
this.issueEvent = in.readParcelable(IssueEvent.class.getClassLoader());
this.issueModel = in.readParcelable(Issue.class.getClassLoader());

View File

@ -71,7 +71,7 @@ public class IssueRequestModel implements Parcelable {
dest.writeStringList(this.labels);
}
protected IssueRequestModel(Parcel in) {
private IssueRequestModel(Parcel in) {
int tmpState = in.readInt();
this.state = tmpState == -1 ? null : IssueState.values()[tmpState];
this.title = in.readString();

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

@ -24,7 +24,7 @@ import lombok.ToString;
public LanguageColorModel() {}
protected LanguageColorModel(Parcel in) {
private LanguageColorModel(Parcel in) {
this.color = in.readString();
this.url = in.readString();
}

View File

@ -20,7 +20,7 @@ import lombok.Setter;
@Override public void writeToParcel(Parcel dest, int flags) {dest.writeString(this.content);}
protected PostReactionModel(Parcel in) {this.content = in.readString();}
private PostReactionModel(Parcel in) {this.content = in.readString();}
public static final Parcelable.Creator<PostReactionModel> CREATOR = new Parcelable.Creator<PostReactionModel>() {
@Override public PostReactionModel createFromParcel(Parcel source) {return new PostReactionModel(source);}

View File

@ -55,7 +55,7 @@ public class PullRequestAdapterModel implements Parcelable {
dest.writeParcelable(this.pullRequest, flags);
}
protected PullRequestAdapterModel(Parcel in) {
private PullRequestAdapterModel(Parcel in) {
this.type = in.readInt();
this.issueEvent = in.readParcelable(IssueEvent.class.getClassLoader());
this.pullRequest = in.readParcelable(PullRequest.class.getClassLoader());

View File

@ -41,7 +41,7 @@ import lombok.Setter;
dest.writeLong(this.createdAt != null ? this.createdAt.getTime() : -1);
}
protected PullRequestStatusModel(Parcel in) {
private PullRequestStatusModel(Parcel in) {
int tmpState = in.readInt();
this.state = tmpState == -1 ? null : StatusStateType.values()[tmpState];
this.sha = in.readString();

View File

@ -2,12 +2,18 @@ package com.fastaccess.data.dao;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.fastaccess.data.dao.model.User;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import pr.PullRequestTimelineQuery;
/**
* Created by Kosh on 28 Mar 2017, 9:15 PM
@ -15,18 +21,19 @@ import lombok.Setter;
@Getter @Setter public class ReactionsModel implements Parcelable {
private long id;
private String url;
private int total_count;
@SerializedName("+1") private int plusOne;
@SerializedName("-1") private int minusOne;
private int laugh;
private int hooray;
private int confused;
private int heart;
private String content;
private User user;
private boolean isCallingApi;
public long id;
public String url;
public int total_count;
@SerializedName("+1") public int plusOne;
@SerializedName("-1") public int minusOne;
public int laugh;
public int hooray;
public int confused;
public int heart;
public String content;
public User user;
public boolean viewerHasReacted;
public boolean isCallingApi;
public ReactionsModel() {}
@ -81,4 +88,46 @@ import lombok.Setter;
@Override public ReactionsModel[] newArray(int size) {return new ReactionsModel[size];}
};
@NonNull public static List<ReactionsModel> getReactionGroup(@Nullable List<PullRequestTimelineQuery.ReactionGroup> reactions) {
List<ReactionsModel> models = new ArrayList<>();
if (reactions != null && !reactions.isEmpty()) {
for (PullRequestTimelineQuery.ReactionGroup reaction : reactions) {
ReactionsModel model = new ReactionsModel();
model.setContent(reaction.content().name());
model.setViewerHasReacted(reaction.viewerHasReacted());
model.setTotal_count(reaction.users().totalCount());
models.add(model);
}
}
return models;
}
@NonNull public static List<ReactionsModel> getReaction(@Nullable List<PullRequestTimelineQuery.ReactionGroup1> reactions) {
List<ReactionsModel> models = new ArrayList<>();
if (reactions != null && !reactions.isEmpty()) {
for (PullRequestTimelineQuery.ReactionGroup1 reaction : reactions) {
ReactionsModel model = new ReactionsModel();
model.setContent(reaction.content().name());
model.setViewerHasReacted(reaction.viewerHasReacted());
model.setTotal_count(reaction.users().totalCount());
models.add(model);
}
}
return models;
}
@NonNull public static List<ReactionsModel> getReaction2(@Nullable List<PullRequestTimelineQuery.ReactionGroup2> reactions) {
List<ReactionsModel> models = new ArrayList<>();
if (reactions != null && !reactions.isEmpty()) {
for (PullRequestTimelineQuery.ReactionGroup2 reaction : reactions) {
ReactionsModel model = new ReactionsModel();
model.setContent(reaction.content().name());
model.setViewerHasReacted(reaction.viewerHasReacted());
model.setTotal_count(reaction.users().totalCount());
models.add(model);
}
}
return models;
}
}

View File

@ -7,14 +7,11 @@ import com.fastaccess.data.dao.model.User;
import java.util.Date;
import lombok.Getter;
import lombok.Setter;
/**
* Created by Kosh on 04 May 2017, 7:10 PM
*/
@Getter @Setter public class ReviewCommentModel implements Parcelable {
public class ReviewCommentModel implements Parcelable {
private long id;
private String url;
@ -36,6 +33,147 @@ import lombok.Setter;
public ReviewCommentModel() {}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public long getPullRequestReviewId() {
return pullRequestReviewId;
}
public void setPullRequestReviewId(long pullRequestReviewId) {
this.pullRequestReviewId = pullRequestReviewId;
}
public String getDiffHunk() {
return diffHunk;
}
public void setDiffHunk(String diffHunk) {
this.diffHunk = diffHunk;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
public int getOriginalPosition() {
return originalPosition;
}
public void setOriginalPosition(int originalPosition) {
this.originalPosition = originalPosition;
}
public String getCommitId() {
return commitId;
}
public void setCommitId(String commitId) {
this.commitId = commitId;
}
public String getOriginalCommitId() {
return originalCommitId;
}
public void setOriginalCommitId(String originalCommitId) {
this.originalCommitId = originalCommitId;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getBodyHtml() {
return bodyHtml;
}
public void setBodyHtml(String bodyHtml) {
this.bodyHtml = bodyHtml;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public String getHtmlUrl() {
return htmlUrl;
}
public void setHtmlUrl(String htmlUrl) {
this.htmlUrl = htmlUrl;
}
public String getPullRequestUrl() {
return pullRequestUrl;
}
public void setPullRequestUrl(String pullRequestUrl) {
this.pullRequestUrl = pullRequestUrl;
}
public ReactionsModel getReactions() {
return reactions;
}
public void setReactions(ReactionsModel reactions) {
this.reactions = reactions;
}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ReviewCommentModel that = (ReviewCommentModel) o;
return id == that.id;
}
@Override public int hashCode() {
return (int) (id ^ (id >>> 32));
}
@Override public int describeContents() { return 0; }
@Override public void writeToParcel(Parcel dest, int flags) {
@ -85,4 +223,12 @@ import lombok.Setter;
@Override public ReviewCommentModel[] newArray(int size) {return new ReviewCommentModel[size];}
};
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}

View File

@ -4,59 +4,67 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.fastaccess.data.dao.model.User;
import com.fastaccess.data.dao.types.ReviewStateType;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
/**
* Created by Kosh on 10 Apr 2017, 4:26 PM
*/
@Getter @Setter public class ReviewModel implements Parcelable {
public class ReviewModel implements Parcelable {
private long id;
private User user;
@SerializedName("body_html") private String body;
private ReviewStateType state;
private String bodyHtml;
private String state;
private Date submittedAt;
private String commitId;
private String diffText;
private List<ReviewCommentModel> comments;
private ReactionsModel reactions;
private String bodyText;
public ReviewModel() {}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ReviewModel that = (ReviewModel) o;
return id == that.id;
}
@Override public int hashCode() {
return (int) (id ^ (id >>> 32));
}
@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.body);
dest.writeInt(this.state == null ? -1 : this.state.ordinal());
dest.writeString(this.bodyHtml);
dest.writeString(this.state);
dest.writeLong(this.submittedAt != null ? this.submittedAt.getTime() : -1);
dest.writeString(this.commitId);
dest.writeString(this.diffText);
dest.writeTypedList(this.comments);
dest.writeParcelable(this.reactions, flags);
dest.writeString(this.bodyText);
}
protected ReviewModel(Parcel in) {
this.id = in.readLong();
this.user = in.readParcelable(User.class.getClassLoader());
this.body = in.readString();
int tmpState = in.readInt();
this.state = tmpState == -1 ? null : ReviewStateType.values()[tmpState];
this.bodyHtml = in.readString();
this.state = in.readString();
long tmpSubmittedAt = in.readLong();
this.submittedAt = tmpSubmittedAt == -1 ? null : new Date(tmpSubmittedAt);
this.commitId = in.readString();
this.diffText = in.readString();
this.comments = in.createTypedArrayList(ReviewCommentModel.CREATOR);
this.reactions = in.readParcelable(ReactionsModel.class.getClassLoader());
this.bodyText = in.readString();
}
public static final Creator<ReviewModel> CREATOR = new Creator<ReviewModel>() {
@ -64,4 +72,84 @@ import lombok.Setter;
@Override public ReviewModel[] newArray(int size) {return new ReviewModel[size];}
};
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getBodyHtml() {
return bodyHtml;
}
public void setBodyHtml(String bodyHtml) {
this.bodyHtml = bodyHtml;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Date getSubmittedAt() {
return submittedAt;
}
public void setSubmittedAt(Date submittedAt) {
this.submittedAt = submittedAt;
}
public String getCommitId() {
return commitId;
}
public void setCommitId(String commitId) {
this.commitId = commitId;
}
public String getDiffText() {
return diffText;
}
public void setDiffText(String diffText) {
this.diffText = diffText;
}
public List<ReviewCommentModel> getComments() {
return comments;
}
public void setComments(List<ReviewCommentModel> comments) {
this.comments = comments;
}
public ReactionsModel getReactions() {
return reactions;
}
public void setReactions(ReactionsModel reactions) {
this.reactions = reactions;
}
public String getBodyText() {
return bodyText;
}
public void setBodyText(String bodyText) {
this.bodyText = bodyText;
}
}

View File

@ -27,7 +27,7 @@ import lombok.Setter;
dest.writeTypedList(this.comments);
}
protected ReviewRequestModel(Parcel in) {
private ReviewRequestModel(Parcel in) {
this.commitId = in.readString();
this.body = in.readString();
this.event = in.readString();

View File

@ -25,7 +25,7 @@ import lombok.Setter;
public SlackInvitePostModel() {}
protected SlackInvitePostModel(Parcel in) {
private SlackInvitePostModel(Parcel in) {
this.email = in.readString();
this.first_name = in.readString();
this.last_name = in.readString();

View File

@ -23,7 +23,7 @@ import lombok.Setter;
public SlackResponseModel() {}
protected SlackResponseModel(Parcel in) {
private SlackResponseModel(Parcel in) {
this.ok = in.readByte() != 0;
this.error = in.readString();
}

View File

@ -39,7 +39,7 @@ import lombok.Setter;
dest.writeLong(this.updatedAt != null ? this.updatedAt.getTime() : -1);
}
protected StatusesModel(Parcel in) {
private StatusesModel(Parcel in) {
this.url = in.readString();
this.id = in.readInt();
int tmpState = in.readInt();

View File

@ -2,249 +2,104 @@ 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.timeline.PullRequestCommitModel;
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;
@Getter @Setter public class TimelineModel implements Parcelable {
public static final int HEADER = 1;
public static final int EVENT = 2;
public static final int COMMENT = 3;
public static final int REVIEW = 4;
public static final int GROUP = 5;
public static final int COMMIT_COMMENTS = 6;
public static final int STATUS = 7;
private int type;
private Issue issue;
private IssueEventType event;
private Comment comment;
private IssueEvent event;
private PullRequest pullRequest;
private GenericEvent genericEvent;
private PullRequestStatusModel status;
private Issue issue;
private PullRequest pullRequest;
private ReviewModel review;
private GroupedReviewModel groupedReview;
private ReviewCommentModel reviewComment;
private Date sortedDate;
private GroupedReviewModel groupedReviewModel;
private PullRequestCommitModel commit;
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();
public TimelineModel() {}
public int getType() {
if (getEvent() != null) {
switch (getEvent()) {
case commented:
return COMMENT;
case reviewed:
case changes_requested:
return REVIEW;
case GROUPED:
return GROUP;
case commit_commented:
return COMMIT_COMMENTS;
default:
return EVENT;
}
} else {
if (issue != null || pullRequest != null) return HEADER;
else if (status != null) return STATUS;
return 0;
}
}
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) {
public static TimelineModel constructHeader(Issue issue) {
return new TimelineModel(issue);
}
@NonNull public static TimelineModel constructHeader(@NonNull PullRequest pullRequest) {
public static TimelineModel constructHeader(PullRequest pullRequest) {
return new TimelineModel(pullRequest);
}
@NonNull public static TimelineModel constructComment(@NonNull Comment comment) {
public static TimelineModel constructComment(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());
}
}).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));
}
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;
@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) {
@ -258,33 +113,104 @@ import static com.annimon.stream.Collectors.toList;
return comment != null ? (int) comment.getId() : 0;
}
public IssueEventType getEvent() {
return event;
}
public void setEvent(IssueEventType event) {
this.event = event;
}
public Comment getComment() {
return comment;
}
public void setComment(Comment comment) {
this.comment = comment;
}
public GenericEvent getGenericEvent() {
return genericEvent;
}
public void setGenericEvent(GenericEvent genericEvent) {
this.genericEvent = genericEvent;
}
public PullRequestStatusModel getStatus() {
return status;
}
public void setStatus(PullRequestStatusModel status) {
this.status = status;
}
public Issue getIssue() {
return issue;
}
public void setIssue(Issue issue) {
this.issue = issue;
}
public PullRequest getPullRequest() {
return pullRequest;
}
public void setPullRequest(PullRequest pullRequest) {
this.pullRequest = pullRequest;
}
public ReviewModel getReview() {
return review;
}
public void setReview(ReviewModel review) {
this.review = review;
}
public GroupedReviewModel getGroupedReviewModel() {
return groupedReviewModel;
}
public void setGroupedReviewModel(GroupedReviewModel groupedReviewModel) {
this.groupedReviewModel = groupedReviewModel;
}
public PullRequestCommitModel getCommit() {
return commit;
}
public void setCommit(PullRequestCommitModel commit) {
this.commit = commit;
}
@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.genericEvent, flags);
dest.writeParcelable(this.status, flags);
dest.writeParcelable(this.issue, flags);
dest.writeParcelable(this.pullRequest, flags);
dest.writeParcelable(this.review, flags);
dest.writeParcelable(this.groupedReview, flags);
dest.writeParcelable(this.reviewComment, flags);
dest.writeLong(this.sortedDate != null ? this.sortedDate.getTime() : -1);
dest.writeParcelable(this.groupedReviewModel, flags);
dest.writeParcelable(this.commit, 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.genericEvent = in.readParcelable(GenericEvent.class.getClassLoader());
this.status = in.readParcelable(PullRequestStatusModel.class.getClassLoader());
this.issue = in.readParcelable(Issue.class.getClassLoader());
this.pullRequest = in.readParcelable(PullRequest.class.getClassLoader());
this.review = in.readParcelable(ReviewModel.class.getClassLoader());
this.groupedReview = in.readParcelable(GroupedReviewModel.class.getClassLoader());
this.reviewComment = in.readParcelable(ReviewCommentModel.class.getClassLoader());
long tmpSortedDate = in.readLong();
this.sortedDate = tmpSortedDate == -1 ? null : new Date(tmpSortedDate);
this.groupedReviewModel = in.readParcelable(GroupedReviewModel.class.getClassLoader());
this.commit = in.readParcelable(PullRequestCommitModel.class.getClassLoader());
}
public static final Creator<TimelineModel> CREATOR = new Creator<TimelineModel>() {

View File

@ -32,7 +32,7 @@ import lombok.Setter;
dest.writeString(this.htmlUrl);
}
protected WikiModel(Parcel in) {
private WikiModel(Parcel in) {
this.pageName = in.readString();
this.title = in.readString();
this.summary = in.readString();

View File

@ -4,6 +4,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import com.annimon.stream.Collectors;
import com.annimon.stream.LongStream;
import com.annimon.stream.Stream;
import com.fastaccess.App;
@ -19,6 +20,7 @@ import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import io.reactivex.Observable;
import io.reactivex.Single;
@ -134,12 +136,13 @@ import static com.fastaccess.data.dao.model.Gist.OWNER_NAME;
return url != null ? url.hashCode() : 0;
}
@NonNull public List<FilesListModel> getFilesAsList() {
List<FilesListModel> models = new ArrayList<>();
@NonNull public ArrayList<FilesListModel> getFilesAsList() {
if (files != null) {
models.addAll(files.values());
return Stream.of(files)
.map(Map.Entry::getValue)
.collect(Collectors.toCollection(ArrayList::new));
}
return models;
return new ArrayList<>();
}
@NonNull public SpannableBuilder getDisplayTitle(boolean isFromProfile) {

View File

@ -92,6 +92,26 @@ import lombok.NoArgsConstructor;
})).subscribe(o -> {/*do nothing*/}, Throwable::printStackTrace);
}
public static Single<Boolean> saveAsSingle(@android.support.annotation.Nullable List<Notification> models) {
if (models == null || models.isEmpty()) {
return Single.just(true);
}
return RxHelper.getSingle(Single.fromPublisher(s -> {
try {
BlockingEntityStore<Persistable> dataStore = App.getInstance().getDataStore().toBlocking();
for (Notification entity : models) {
dataStore.delete(Notification.class).where(Notification.ID.eq(entity.getId())).get().value();
}
dataStore.insert(models);
s.onNext(true);
} catch (Exception e) {
e.printStackTrace();
s.onError(e);
}
s.onComplete();
}));
}
public static Single<List<Notification>> getUnreadNotifications() {
return App.getInstance()
.getDataStore()
@ -116,9 +136,9 @@ import lombok.NoArgsConstructor;
public static boolean hasUnreadNotifications() {
return App.getInstance()
.getDataStore()
.toBlocking()
.count(Notification.class)
.where(Notification.UNREAD.equal(true))
.limit(1)
.get()
.value() > 0;
}

View File

@ -121,13 +121,12 @@ import static com.fastaccess.data.dao.model.Repo.UPDATED_AT;
String starredUser;
String reposOwner;
public Single<Repo> save(Repo entity) {
return RxHelper.getSingle(App.getInstance().getDataStore()
.delete(Repo.class)
.where(Repo.ID.eq(entity.getId()))
.get()
.single()
.flatMap(observer -> App.getInstance().getDataStore().insert(entity)));
public Disposable save(Repo entity) {
return Single.create(e -> {
BlockingEntityStore<Persistable> dataSource = App.getInstance().getDataStore().toBlocking();
dataSource.delete(Repo.class).where(Repo.ID.eq(entity.getId())).get().value();
dataSource.insert(entity);
}).subscribe(o -> {/**/}, Throwable::printStackTrace);
}
public static Maybe<Repo> getRepo(@NonNull String name, @NonNull String login) {

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);
}
private 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);
}
private 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());
}
private 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() {}
private 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,98 @@
package com.fastaccess.data.dao.timeline;
import android.os.Parcel;
import android.os.Parcelable;
import com.fastaccess.data.dao.model.Comment;
import java.util.List;
/**
* Created by kosh on 15/08/2017.
*/
public class PullRequestCommitModel implements Parcelable {
private String login;
private String path;
private int position;
private String commitId;
private List<Comment> comments;
private int line;
public int getLine() {
return line;
}
public void setLine(int line) {
this.line = line;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
public String getCommitId() {
return commitId;
}
public void setCommitId(String commitId) {
this.commitId = commitId;
}
public List<Comment> getComments() {
return comments;
}
public void setComments(List<Comment> comments) {
this.comments = comments;
}
public PullRequestCommitModel() {}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
@Override public int describeContents() { return 0; }
@Override public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.login);
dest.writeString(this.path);
dest.writeInt(this.position);
dest.writeString(this.commitId);
dest.writeTypedList(this.comments);
dest.writeInt(this.line);
}
protected PullRequestCommitModel(Parcel in) {
this.login = in.readString();
this.path = in.readString();
this.position = in.readInt();
this.commitId = in.readString();
this.comments = in.createTypedArrayList(Comment.CREATOR);
this.line = in.readInt();
}
public static final Creator<PullRequestCommitModel> CREATOR = new Creator<PullRequestCommitModel>() {
@Override public PullRequestCommitModel createFromParcel(Parcel source) {return new PullRequestCommitModel(source);}
@Override public PullRequestCommitModel[] newArray(int size) {return new PullRequestCommitModel[size];}
};
}

View File

@ -0,0 +1,169 @@
package com.fastaccess.data.dao.timeline;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.fastaccess.data.dao.ReactionsModel;
import com.fastaccess.helper.Logger;
import com.fastaccess.helper.ParseDateFormat;
import java.util.ArrayList;
import java.util.List;
import pr.PullRequestTimelineQuery;
import pr.type.PullRequestReviewState;
/**
* Created by kosh on 20/08/2017.
*/
public class PullRequestReviewModel {
private PullRequestTimelineQuery.AsReviewDismissedEvent reviewDismissedEvent;
private PullRequestTimelineQuery.AsReviewRequestedEvent reviewRequestedEvent;
private PullRequestTimelineQuery.AsReviewRequestRemovedEvent reviewRequestRemovedEvent;
private PullRequestTimelineQuery.Node2 node;
private List<ReactionsModel> reaction;
private List<PullRequestReviewModel> comments;
private String id;
private String url;
private PullRequestTimelineQuery.Author2 author;
private String bodyHTML;
private String createdAt;
private PullRequestReviewState state;
@Nullable public static PullRequestReviewModel build(@NonNull PullRequestTimelineQuery.Node node) {
PullRequestReviewModel model = new PullRequestReviewModel();
if (node.asReviewRequestRemovedEvent() != null) {
model.reviewRequestRemovedEvent = node.asReviewRequestRemovedEvent();
} else if (node.asReviewDismissedEvent() != null) {
model.reviewDismissedEvent = node.asReviewDismissedEvent();
} else if (node.asReviewRequestedEvent() != null) {
model.reviewRequestedEvent = node.asReviewRequestedEvent();
} else {
PullRequestTimelineQuery.AsPullRequestReview pullRequestReview = node.asPullRequestReview();
if (pullRequestReview != null) {
model.state = pullRequestReview.state();
model.url = pullRequestReview.url().toString();
model.author = pullRequestReview.author();
model.bodyHTML = pullRequestReview.bodyHTML().toString();
model.createdAt = ParseDateFormat.getTimeAgo(pullRequestReview.createdAt().toString()).toString();
model.id = pullRequestReview.id();
model.url = pullRequestReview.url().toString();
List<PullRequestTimelineQuery.Edge2> edges = pullRequestReview.comments().edges();
if (edges != null && !edges.isEmpty()) {
List<PullRequestReviewModel> comments = new ArrayList<>();
for (PullRequestTimelineQuery.Edge2 edge : edges) {
PullRequestTimelineQuery.Node2 node2 = edge.node();
if (node2 != null) {
PullRequestReviewModel comment = new PullRequestReviewModel();
comment.node = node2;
comment.reaction = ReactionsModel.getReaction(node2.reactionGroups());
comments.add(comment);
}
}
Logger.e(comments.size());
model.comments = comments;
}
} else {
return null;
}
}
return model;
}
public PullRequestTimelineQuery.AsReviewDismissedEvent getReviewDismissedEvent() {
return reviewDismissedEvent;
}
public void setReviewDismissedEvent(PullRequestTimelineQuery.AsReviewDismissedEvent reviewDismissedEvent) {
this.reviewDismissedEvent = reviewDismissedEvent;
}
public PullRequestTimelineQuery.AsReviewRequestedEvent getReviewRequestedEvent() {
return reviewRequestedEvent;
}
public void setReviewRequestedEvent(PullRequestTimelineQuery.AsReviewRequestedEvent reviewRequestedEvent) {
this.reviewRequestedEvent = reviewRequestedEvent;
}
public PullRequestTimelineQuery.AsReviewRequestRemovedEvent getReviewRequestRemovedEvent() {
return reviewRequestRemovedEvent;
}
public void setReviewRequestRemovedEvent(PullRequestTimelineQuery.AsReviewRequestRemovedEvent reviewRequestRemovedEvent) {
this.reviewRequestRemovedEvent = reviewRequestRemovedEvent;
}
public PullRequestTimelineQuery.Node2 getNode() {
return node;
}
public void setNode(PullRequestTimelineQuery.Node2 node) {
this.node = node;
}
public List<ReactionsModel> getReaction() {
return reaction;
}
public void setReaction(List<ReactionsModel> reaction) {
this.reaction = reaction;
}
public List<PullRequestReviewModel> getComments() {
return comments;
}
public void setComments(List<PullRequestReviewModel> comments) {
this.comments = comments;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public PullRequestTimelineQuery.Author2 getAuthor() {
return author;
}
public void setAuthor(PullRequestTimelineQuery.Author2 author) {
this.author = author;
}
public String getBodyHTML() {
return bodyHTML;
}
public void setBodyHTML(String bodyHTML) {
this.bodyHTML = bodyHTML;
}
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
public PullRequestReviewState getState() {
return state;
}
public void setState(PullRequestReviewState state) {
this.state = state;
}
}

View File

@ -0,0 +1,85 @@
package com.fastaccess.data.dao.timeline;
import com.fastaccess.data.dao.ReactionsModel;
import com.fastaccess.data.dao.model.PullRequest;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import pr.PullRequestTimelineQuery;
/**
* Created by kosh on 02/08/2017.
*/
@Getter @Setter public class PullRequestTimelineModel {
public static final int HEADER = 1;
public static final int EVENT = 2;
public static final int COMMENT = 3;
public static final int STATUS = 4;
public static final int REVIEW = 5;
public static final int COMMIT_COMMENTS = 6;
public PullRequestTimelineQuery.Node node;
public PullRequest pullRequest;
public PullRequestTimelineQuery.Status status;
public List<ReactionsModel> reactions;
public boolean isMergeable;
public PullRequestCommitModel commitThread;
public PullRequestReviewModel reviewModel;
public PullRequestTimelineModel(PullRequest pullRequest) {
this.pullRequest = pullRequest;
}
public PullRequestTimelineModel(PullRequestTimelineQuery.Node node) {
this.node = node;
if (this.node.asCommitCommentThread() != null) {
} else {
if (node.asPullRequestReview() != null || node.asReviewDismissedEvent() != null
|| node.asReviewRequestedEvent() != null || node.asReviewRequestRemovedEvent() != null) {
reviewModel = PullRequestReviewModel.build(node);
}
}
}
public PullRequestTimelineModel(PullRequestTimelineQuery.Status status, boolean isMergeable) {
this.status = status;
this.isMergeable = isMergeable;
}
public int getType() {
if (pullRequest != null) return HEADER;
if (node != null) {
if (node.asAssignedEvent() != null || node.asClosedEvent() != null
|| node.asDemilestonedEvent() != null || node.asHeadRefDeletedEvent() != null
|| node.asLabeledEvent() != null || node.asLockedEvent() != null
|| node.asMergedEvent() != null || node.asMilestonedEvent() != null
|| node.asReferencedEvent() != null || node.asRenamedTitleEvent() != null
|| node.asReopenedEvent() != null || node.asUnassignedEvent() != null
|| node.asUnlabeledEvent() != null || node.asUnlockedEvent() != null
|| node.asCommit() != null || node.asHeadRefRestoredEvent() != null) {
return EVENT;
} else if (node.asIssueComment() != null) {
if (reactions == null) {
//noinspection ConstantConditions
setReactions(ReactionsModel.getReaction2(node.asIssueComment().reactionGroups()));
}
return COMMENT;
} else if (reviewModel != null) {
return REVIEW;
} else if (commitThread != null) {
return COMMIT_COMMENTS;
}
} else if (status != null) {
return STATUS;
}
return 0;
}
@Override public String toString() {
return String.valueOf(getType());
}
}

View File

@ -0,0 +1,51 @@
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 com.fastaccess.data.dao.model.Repo;
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;
private Repo repository;
public SourceModel() {}
@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);
dest.writeParcelable(this.repository, flags);
}
private 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());
this.repository = in.readParcelable(Repo.class.getClassLoader());
}
public static final Creator<SourceModel> CREATOR = new Creator<SourceModel>() {
@Override public SourceModel createFromParcel(Parcel source) {return new SourceModel(source);}
@Override public SourceModel[] newArray(int size) {return new SourceModel[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,13 @@ 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),
@SerializedName("commit-commented")commit_commented(R.drawable.ic_comment),
reviewed(R.drawable.ic_eye),
changes_requested(R.drawable.ic_eye),
added_to_project(R.drawable.ic_add),
GROUPED(R.drawable.ic_eye);
int iconResId;
@ -36,4 +46,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

@ -3,6 +3,7 @@ package com.fastaccess.data.dao.types;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import com.annimon.stream.Stream;
import com.fastaccess.R;
/**
@ -35,10 +36,9 @@ public enum ReactionTypes {
}
@Nullable public static ReactionTypes get(@IdRes int vId) {
for (ReactionTypes type : ReactionTypes.values()) {
if (type.vId == vId) return type;
}
return null;
return Stream.of(values())
.filter(value -> value.getvId() == vId)
.findFirst()
.orElse(null);
}
}

View File

@ -1,8 +1,11 @@
package com.fastaccess.data.dao.types;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import com.annimon.stream.Stream;
import com.fastaccess.R;
/**
@ -11,7 +14,7 @@ import com.fastaccess.R;
public enum ReviewStateType {
COMMENTED(R.string.reviewed, R.drawable.ic_eye),
CHANGES_REQUESTED(R.string.reviewed, R.drawable.ic_eye),
CHANGES_REQUESTED(R.string.request_changes, R.drawable.ic_clear),
REQUEST_CHANGES(R.string.reviewed, R.drawable.ic_eye),
DISMISSED(R.string.dismissed_review, R.drawable.ic_clear),
APPROVED(R.string.approved_these_changes, R.drawable.ic_done),
@ -32,4 +35,11 @@ public enum ReviewStateType {
@DrawableRes public int getDrawableRes() {
return drawableRes > 0 ? drawableRes : R.drawable.ic_eye;
}
@Nullable public static ReviewStateType getType(@NonNull String state) {
return Stream.of(values())
.filter(value -> value.name().toLowerCase().equalsIgnoreCase(state.toLowerCase()))
.findFirst()
.orElse(null);
}
}

View File

@ -1,7 +1,10 @@
package com.fastaccess.data.dao.types;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.annimon.stream.Stream;
import com.fastaccess.R;
/**
@ -23,4 +26,11 @@ public enum StatusStateType {
@DrawableRes public int getDrawableRes() {
return drawableRes;
}
@NonNull public static StatusStateType getState(@Nullable String status) {
return Stream.of(values())
.filter(value -> value.name().toLowerCase().equalsIgnoreCase(status))
.findFirst()
.orElse(pending);
}
}

View File

@ -6,7 +6,7 @@ import android.os.Parcelable
/**
* Created by Kosh on 13 Jun 2017, 8:06 PM
*/
data class WikiContentModel(val content: String? = null, val footer: String? = null,
data class WikiContentModel(val content: String? = null, private val footer: String? = null,
val sidebar: ArrayList<WikiSideBarModel>) : Parcelable {
companion object {
@JvmField val CREATOR: Parcelable.Creator<WikiContentModel> = object : Parcelable.Creator<WikiContentModel> {

View File

@ -32,7 +32,7 @@ public interface GistService {
@POST("gists") Observable<Gist> createGist(@Body CreateGistModel gistBody);
@POST("gists/{id}") Observable<Gist> editGist(@Body CreateGistModel gistBody, @NonNull @Path("id") String id);
@PATCH("gists/{id}") Observable<Gist> editGist(@Body CreateGistModel gistBody, @Path("id") String id);
@DELETE("gists/{id}") Observable<Response<Boolean>> deleteGist(@Path("id") String id);

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

@ -39,10 +39,13 @@ public interface OrganizationService {
@GET("teams/{id}/repos") Observable<Pageable<Repo>> getTeamRepos(@Path("id") long id, @Query("page") int page);
@GET("orgs/{username}/events")
Observable<Pageable<Event>> getReceivedEvents(@NonNull @Path("username") String userName, @Query("page") int page);
@GET("users/{username}/events/orgs/{org}")
Observable<Pageable<Event>> getReceivedEvents(@NonNull @Path("username") String userName,
@NonNull @Path("org") String org, @Query("page") int page);
@GET("orgs/{org}/repos")
Observable<Pageable<Repo>> getOrgRepos(@NonNull @Path("org") String org, @QueryMap(encoded = true) Map<String, String> filterParams, @Query("page") int page);
Observable<Pageable<Repo>> getOrgRepos(@NonNull @Path("org") String org,
@QueryMap(encoded = true) Map<String, String> filterParams,
@Query("page") int page);
}

View File

@ -138,6 +138,9 @@ public interface RepoService {
@NonNull @GET("repos/{owner}/{repo}/labels?per_page=100")
Observable<Pageable<LabelModel>> getLabels(@NonNull @Path("owner") String owner, @NonNull @Path("repo") String repo);
@NonNull @GET("repos/{owner}/{repo}/labels?per_page=100")
Observable<Pageable<LabelModel>> getLabels(@NonNull @Path("owner") String owner, @NonNull @Path("repo") String repo, @Query("page") int page);
@NonNull @POST("repos/{owner}/{repo}/labels")
Observable<LabelModel> addLabel(@NonNull @Path("owner") String owner, @NonNull @Path("repo") String repo, @Body LabelModel body);

View File

@ -42,7 +42,7 @@ public interface ReviewService {
Observable<Pageable<ReviewCommentModel>> getReviewComments(@Path("owner") String owner, @Path("repo") String repo,
@Path("number") long number, @Path("id") long reviewId);
@GET("repos/{owner}/{repo}/pulls/{number}/comments")
@GET("repos/{owner}/{repo}/pulls/{number}/comments?per_page=100")
@Headers("Accept: application/vnd.github.black-cat-preview+json, application/vnd.github.VERSION.html, "
+ "application/vnd.github.squirrel-girl-preview")
@NonNull
@ -68,5 +68,5 @@ public interface ReviewService {
@POST("repos/{owner}/{repo}/pulls/{number}/reviews")
@Headers("Accept: application/vnd.github.black-cat-preview")
Observable<Response<ReviewModel>> submitPrReview(@Path("owner") String owner, @Path("repo") String repo,
@Path("number") long number, @NonNull @Body ReviewRequestModel body);
@Path("number") long number, @NonNull @Body ReviewRequestModel body);
}

View File

@ -4,6 +4,8 @@ import android.app.NotificationManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
@ -73,14 +75,16 @@ public class AppHelper {
.append("**Android Version: ").append(String.valueOf(Build.VERSION.RELEASE)).append(" (SDK: ")
.append(String.valueOf(Build.VERSION.SDK_INT)).append(")**").append(" \n")
.append("**Device Information:**").append(" \n")
.append("- **" + (!model.equalsIgnoreCase(brand) ? "Manufacturer" : "Manufacturer&Brand") + ":** ").append(Build.MANUFACTURER)
.append("- **")
.append(!model.equalsIgnoreCase(brand) ? "Manufacturer" : "Manufacturer&Brand")
.append(":** ")
.append(Build.MANUFACTURER)
.append(" \n");
if (!(model.equalsIgnoreCase(brand) || "google".equals(Build.BRAND))) {
builder.append("- **Brand:** ").append(brand).append(" \n");
}
builder.append("- **Model:** ")
.append(model)
.append(" \n").append("---").append("\n");
builder.append("- **Model:** ").append(model).append(" \n")
.append("---").append("\n\n");
if (!Locale.getDefault().getLanguage().equals(new Locale("en").getLanguage())) {
builder.append("<--")
.append(App.getInstance().getString(R.string.english_please))
@ -157,7 +161,13 @@ public class AppHelper {
}
public static boolean isGoogleAvailable(@NonNull Context context) {
int status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
return status != ConnectionResult.SERVICE_DISABLED && status == ConnectionResult.SUCCESS;
ApplicationInfo applicationInfo = null;
try {
applicationInfo = context.getPackageManager().getApplicationInfo("com.google.android.gms", 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return applicationInfo != null && applicationInfo.enabled &&
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS;
}
}

View File

@ -5,9 +5,15 @@ import android.support.annotation.StringDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import static com.fastaccess.helper.BundleConstant.ExtraTYpe.EDIT_GIST_COMMENT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraTYpe.FOR_RESULT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraTYpe.NEW_GIST_COMMENT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraType.EDIT_COMMIT_COMMENT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraType.EDIT_GIST_COMMENT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraType.EDIT_ISSUE_COMMENT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraType.EDIT_REVIEW_COMMENT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraType.FOR_RESULT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraType.NEW_COMMIT_COMMENT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraType.NEW_GIST_COMMENT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraType.NEW_ISSUE_COMMENT_EXTRA;
import static com.fastaccess.helper.BundleConstant.ExtraType.NEW_REVIEW_COMMENT_EXTRA;
/**
* Created by Kosh on 12 Nov 2016, 3:55 PM
@ -35,12 +41,18 @@ public class BundleConstant {
@StringDef({
NEW_GIST_COMMENT_EXTRA,
FOR_RESULT_EXTRA,
EDIT_GIST_COMMENT_EXTRA,
FOR_RESULT_EXTRA
NEW_GIST_COMMENT_EXTRA,
EDIT_ISSUE_COMMENT_EXTRA,
NEW_ISSUE_COMMENT_EXTRA,
EDIT_COMMIT_COMMENT_EXTRA,
NEW_COMMIT_COMMENT_EXTRA,
NEW_REVIEW_COMMENT_EXTRA,
EDIT_REVIEW_COMMENT_EXTRA
})
@Retention(RetentionPolicy.SOURCE) public @interface ExtraTYpe {
@Retention(RetentionPolicy.SOURCE) public @interface ExtraType {
String FOR_RESULT_EXTRA = "for_result_extra";
String EDIT_GIST_COMMENT_EXTRA = "edit_comment_extra";
String NEW_GIST_COMMENT_EXTRA = "new_gist_comment_extra";

View File

@ -185,7 +185,7 @@ public class Bundler {
/**
* Get the underlying start.
*/
public Bundle get() {
private Bundle get() {
return bundle;
}

View File

@ -12,7 +12,7 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class CustomTabsHelper {
class CustomTabsHelper {
private static final String TAG = "CustomTabsHelper";
private static final String STABLE_PACKAGE = "com.android.chrome";
private static final String BETA_PACKAGE = "com.chrome.beta";

View File

@ -31,6 +31,18 @@ public class ParseDateFormat {
}
}
@NonNull public static CharSequence getTimeAgo(@Nullable String toParse) {
try {
Date parsedDate = getInstance().dateFormat.parse(toParse);
long now = System.currentTimeMillis();
return DateUtils.getRelativeTimeSpanString(parsedDate.getTime(), now, DateUtils.SECOND_IN_MILLIS);
} catch (Exception e) {
e.printStackTrace();
}
return "N/A";
}
@NonNull public static CharSequence getTimeAgo(@Nullable Date parsedDate) {
if (parsedDate != null) {
long now = System.currentTimeMillis();

View File

@ -3,6 +3,7 @@ package com.fastaccess.provider.emoji;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
/**
* This class represents an emoji.<br>
@ -12,203 +13,205 @@ import java.util.List;
* @author Vincent DURMONT [vdurmont@gmail.com]
*/
public class Emoji {
private final String description;
private final boolean supportsFitzpatrick;
private final List<String> aliases;
private final List<String> tags;
private final String unicode;
private final String htmlDec;
private final String htmlHex;
private final String description;
private final boolean supportsFitzpatrick;
private final List<String> aliases;
private final List<String> tags;
private String unicode;
private String htmlDec;
private String htmlHex;
/**
* Constructor for the Emoji.
*
* @param description The description of the emoji
* @param supportsFitzpatrick Whether the emoji supports Fitzpatrick modifiers
* @param aliases the aliases for this emoji
* @param tags the tags associated with this emoji
* @param bytes the bytes that represent the emoji
*/
protected Emoji(
String description,
boolean supportsFitzpatrick,
List<String> aliases,
List<String> tags,
byte... bytes
) {
this.description = description;
this.supportsFitzpatrick = supportsFitzpatrick;
this.aliases = Collections.unmodifiableList(aliases);
this.tags = Collections.unmodifiableList(tags);
/**
* Constructor for the Emoji.
*
* @param description
* The description of the emoji
* @param supportsFitzpatrick
* Whether the emoji supports Fitzpatrick modifiers
* @param aliases
* the aliases for this emoji
* @param tags
* the tags associated with this emoji
* @param bytes
* the bytes that represent the emoji
*/
protected Emoji(
String description,
boolean supportsFitzpatrick,
List<String> aliases,
List<String> tags,
byte... bytes
) {
this.description = description;
this.supportsFitzpatrick = supportsFitzpatrick;
this.aliases = Collections.unmodifiableList(aliases);
this.tags = Collections.unmodifiableList(tags);
int count = 0;
try {
this.unicode = new String(bytes, "UTF-8");
int stringLength = getUnicode().length();
String[] pointCodes = new String[stringLength];
String[] pointCodesHex = new String[stringLength];
for (int offset = 0; offset < stringLength; ) {
final int codePoint = getUnicode().codePointAt(offset);
pointCodes[count] = String.format("&#%d;", codePoint);
pointCodesHex[count++] = String.format("&#x%x;", codePoint);
offset += Character.charCount(codePoint);
}
this.htmlDec = stringJoin(pointCodes, count);
this.htmlHex = stringJoin(pointCodesHex, count);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
int count = 0;
try {
this.unicode = new String(bytes, "UTF-8");
int stringLength = getUnicode().length();
String[] pointCodes = new String[stringLength];
String[] pointCodesHex = new String[stringLength];
for (int offset = 0; offset < stringLength; ) {
final int codePoint = getUnicode().codePointAt(offset);
pointCodes[count] = String.format(Locale.getDefault(), "&#%d;", codePoint);
pointCodesHex[count++] = String.format("&#x%x;", codePoint);
offset += Character.charCount(codePoint);
}
this.htmlDec = stringJoin(pointCodes, count);
this.htmlHex = stringJoin(pointCodesHex, count);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
/**
* Method to replace String.join, since it was only introduced in java8
* @param array the array to be concatenated
* @return concatenated String
*/
private String stringJoin(String[] array, int count){
String joined = "";
for(int i = 0; i < count; i++)
joined += array[i];
return joined;
}
/**
* Returns the description of the emoji
*
* @return the description
*/
public String getDescription() {
return this.description;
}
/**
* Returns wether the emoji supports the Fitzpatrick modifiers or not
*
* @return true if the emoji supports the Fitzpatrick modifiers
*/
public boolean supportsFitzpatrick() {
return this.supportsFitzpatrick;
}
/**
* Returns the aliases of the emoji
*
* @return the aliases (unmodifiable)
*/
public List<String> getAliases() {
return this.aliases;
}
/**
* Returns the tags of the emoji
*
* @return the tags (unmodifiable)
*/
public List<String> getTags() {
return this.tags;
}
/**
* Returns the unicode representation of the emoji
*
* @return the unicode representation
*/
public String getUnicode() {
return this.unicode;
}
/**
* Returns the unicode representation of the emoji associated with the
* provided Fitzpatrick modifier.<br>
* If the modifier is null, then the result is similar to
* {@link Emoji#getUnicode()}
*
* @param fitzpatrick the fitzpatrick modifier or null
*
* @return the unicode representation
* @throws UnsupportedOperationException if the emoji doesn't support the
* Fitzpatrick modifiers
*/
public String getUnicode(Fitzpatrick fitzpatrick) {
if (!this.supportsFitzpatrick()) {
throw new UnsupportedOperationException(
"Cannot get the unicode with a fitzpatrick modifier, " +
"the emoji doesn't support fitzpatrick."
);
} else if (fitzpatrick == null) {
return this.getUnicode();
/**
* Method to replace String.join, since it was only introduced in java8
*
* @param array
* the array to be concatenated
* @return concatenated String
*/
private String stringJoin(String[] array, int count) {
String joined = "";
for (int i = 0; i < count; i++)
joined += array[i];
return joined;
}
return this.getUnicode() + fitzpatrick.unicode;
}
/**
* Returns the HTML decimal representation of the emoji
*
* @return the HTML decimal representation
*/
public String getHtmlDecimal() {
return this.htmlDec;
}
/**
* Returns the description of the emoji
*
* @return the description
*/
public String getDescription() {
return this.description;
}
/**
* @deprecated identical to {@link #getHtmlHexadecimal()} for
* backwards-compatibility. Use that instead.
*
* @return the HTML hexadecimal representation
*/
public String getHtmlHexidecimal() {
return this.getHtmlHexadecimal();
}
/**
* Returns wether the emoji supports the Fitzpatrick modifiers or not
*
* @return true if the emoji supports the Fitzpatrick modifiers
*/
public boolean supportsFitzpatrick() {
return this.supportsFitzpatrick;
}
/**
* Returns the HTML hexadecimal representation of the emoji
*
* @return the HTML hexadecimal representation
*/
public String getHtmlHexadecimal() {
return this.htmlHex;
}
/**
* Returns the aliases of the emoji
*
* @return the aliases (unmodifiable)
*/
public List<String> getAliases() {
return this.aliases;
}
@Override
public boolean equals(Object other) {
return !(other == null || !(other instanceof Emoji)) &&
((Emoji) other).getUnicode().equals(getUnicode());
}
/**
* Returns the tags of the emoji
*
* @return the tags (unmodifiable)
*/
public List<String> getTags() {
return this.tags;
}
@Override
public int hashCode() {
return unicode.hashCode();
}
/**
* Returns the unicode representation of the emoji
*
* @return the unicode representation
*/
public String getUnicode() {
return this.unicode;
}
/**
* Returns the String representation of the Emoji object.<br>
* <br>
* Example:<br>
* <code>Emoji {
* description='smiling face with open mouth and smiling eyes',
* supportsFitzpatrick=false,
* aliases=[smile],
* tags=[happy, joy, pleased],
* unicode='😄',
* htmlDec='&amp;#128516;',
* htmlHex='&amp;#x1f604;'
* }</code>
*
* @return the string representation
*/
@Override
public String toString() {
return "Emoji{" +
"description='" + description + '\'' +
", supportsFitzpatrick=" + supportsFitzpatrick +
", aliases=" + aliases +
", tags=" + tags +
", unicode='" + unicode + '\'' +
", htmlDec='" + htmlDec + '\'' +
", htmlHex='" + htmlHex + '\'' +
'}';
}
/**
* Returns the unicode representation of the emoji associated with the
* provided Fitzpatrick modifier.<br>
* If the modifier is null, then the result is similar to
* {@link Emoji#getUnicode()}
*
* @param fitzpatrick
* the fitzpatrick modifier or null
* @return the unicode representation
* @throws UnsupportedOperationException
* if the emoji doesn't support the Fitzpatrick modifiers
*/
public String getUnicode(Fitzpatrick fitzpatrick) {
if (!this.supportsFitzpatrick()) {
throw new UnsupportedOperationException(
"Cannot get the unicode with a fitzpatrick modifier, " +
"the emoji doesn't support fitzpatrick."
);
} else if (fitzpatrick == null) {
return this.getUnicode();
}
return this.getUnicode() + fitzpatrick.unicode;
}
/**
* Returns the HTML decimal representation of the emoji
*
* @return the HTML decimal representation
*/
public String getHtmlDecimal() {
return this.htmlDec;
}
/**
* @return the HTML hexadecimal representation
* @deprecated identical to {@link #getHtmlHexadecimal()} for backwards-compatibility. Use that instead.
*/
public String getHtmlHexidecimal() {
return this.getHtmlHexadecimal();
}
/**
* Returns the HTML hexadecimal representation of the emoji
*
* @return the HTML hexadecimal representation
*/
public String getHtmlHexadecimal() {
return this.htmlHex;
}
@Override
public boolean equals(Object other) {
return !(other == null || !(other instanceof Emoji)) &&
((Emoji) other).getUnicode().equals(getUnicode());
}
@Override
public int hashCode() {
return unicode.hashCode();
}
/**
* Returns the String representation of the Emoji object.<br>
* <br>
* Example:<br>
* <code>Emoji {
* description='smiling face with open mouth and smiling eyes',
* supportsFitzpatrick=false,
* aliases=[smile],
* tags=[happy, joy, pleased],
* unicode='😄',
* htmlDec='&amp;#128516;',
* htmlHex='&amp;#x1f604;'
* }</code>
*
* @return the string representation
*/
@Override
public String toString() {
return "Emoji{" +
"description='" + description + '\'' +
", supportsFitzpatrick=" + supportsFitzpatrick +
", aliases=" + aliases +
", tags=" + tags +
", unicode='" + unicode + '\'' +
", htmlDec='" + htmlDec + '\'' +
", htmlHex='" + htmlHex + '\'' +
'}';
}
}

View File

@ -16,10 +16,10 @@ import java.util.List;
*
* @author Vincent DURMONT [vdurmont@gmail.com]
*/
public class EmojiLoader {
class EmojiLoader {
private EmojiLoader() {}
public static List<Emoji> loadEmojis(InputStream stream) throws IOException {
static List<Emoji> loadEmojis(InputStream stream) throws IOException {
try {
JSONArray emojisJSON = new JSONArray(inputStreamToString(stream));
List<Emoji> emojis = new ArrayList<Emoji>(emojisJSON.length());
@ -48,7 +48,7 @@ public class EmojiLoader {
return sb.toString();
}
protected static Emoji buildEmojiFromJSON(JSONObject json) throws Exception {
private static Emoji buildEmojiFromJSON(JSONObject json) throws Exception {
if (!json.has("emoji")) {
return null;
}

View File

@ -87,7 +87,7 @@ public class EmojiManager {
return EMOJI_TRIE.getEmoji(unicode);
}
public static Collection<Emoji> getAll() {
public static List<Emoji> getAll() {
return ALL_EMOJIS;
}

View File

@ -52,32 +52,30 @@ public class EmojiParser {
*
* @return the string with the emojis replaced by their alias.
*/
public static String parseToAliases(
String input,
final FitzpatrickAction fitzpatrickAction
private static String parseToAliases(
String input,
final FitzpatrickAction fitzpatrickAction
) {
EmojiTransformer emojiTransformer = new EmojiTransformer() {
public String transform(UnicodeCandidate unicodeCandidate) {
switch (fitzpatrickAction) {
default:
case PARSE:
if (unicodeCandidate.hasFitzpatrick()) {
return ":" +
unicodeCandidate.getEmoji().getAliases().get(0) +
"|" +
unicodeCandidate.getFitzpatrickType() +
":";
}
case REMOVE:
EmojiTransformer emojiTransformer = unicodeCandidate -> {
switch (fitzpatrickAction) {
default:
case PARSE:
if (unicodeCandidate.hasFitzpatrick()) {
return ":" +
unicodeCandidate.getEmoji().getAliases().get(0) +
":";
case IGNORE:
return ":" +
unicodeCandidate.getEmoji().getAliases().get(0) +
":" +
unicodeCandidate.getFitzpatrickUnicode();
}
unicodeCandidate.getEmoji().getAliases().get(0) +
"|" +
unicodeCandidate.getFitzpatrickType() +
":";
}
case REMOVE:
return ":" +
unicodeCandidate.getEmoji().getAliases().get(0) +
":";
case IGNORE:
return ":" +
unicodeCandidate.getEmoji().getAliases().get(0) +
":" +
unicodeCandidate.getFitzpatrickUnicode();
}
};
@ -132,7 +130,7 @@ public class EmojiParser {
return result;
}
protected static List<AliasCandidate> getAliasCandidates(String input) {
private static List<AliasCandidate> getAliasCandidates(String input) {
List<AliasCandidate> candidates = new ArrayList<AliasCandidate>();
Matcher matcher = ALIAS_CANDIDATE_PATTERN.matcher(input);
@ -186,21 +184,19 @@ public class EmojiParser {
* @return the string with the emojis replaced by their html decimal
* representation.
*/
public static String parseToHtmlDecimal(
String input,
final FitzpatrickAction fitzpatrickAction
private static String parseToHtmlDecimal(
String input,
final FitzpatrickAction fitzpatrickAction
) {
EmojiTransformer emojiTransformer = new EmojiTransformer() {
public String transform(UnicodeCandidate unicodeCandidate) {
switch (fitzpatrickAction) {
default:
case PARSE:
case REMOVE:
return unicodeCandidate.getEmoji().getHtmlDecimal();
case IGNORE:
return unicodeCandidate.getEmoji().getHtmlDecimal() +
unicodeCandidate.getFitzpatrickUnicode();
}
EmojiTransformer emojiTransformer = unicodeCandidate -> {
switch (fitzpatrickAction) {
default:
case PARSE:
case REMOVE:
return unicodeCandidate.getEmoji().getHtmlDecimal();
case IGNORE:
return unicodeCandidate.getEmoji().getHtmlDecimal() +
unicodeCandidate.getFitzpatrickUnicode();
}
};
@ -241,21 +237,19 @@ public class EmojiParser {
* @return the string with the emojis replaced by their html hex
* representation.
*/
public static String parseToHtmlHexadecimal(
String input,
final FitzpatrickAction fitzpatrickAction
private static String parseToHtmlHexadecimal(
String input,
final FitzpatrickAction fitzpatrickAction
) {
EmojiTransformer emojiTransformer = new EmojiTransformer() {
public String transform(UnicodeCandidate unicodeCandidate) {
switch (fitzpatrickAction) {
default:
case PARSE:
case REMOVE:
return unicodeCandidate.getEmoji().getHtmlHexadecimal();
case IGNORE:
return unicodeCandidate.getEmoji().getHtmlHexadecimal() +
unicodeCandidate.getFitzpatrickUnicode();
}
EmojiTransformer emojiTransformer = unicodeCandidate -> {
switch (fitzpatrickAction) {
default:
case PARSE:
case REMOVE:
return unicodeCandidate.getEmoji().getHtmlHexadecimal();
case IGNORE:
return unicodeCandidate.getEmoji().getHtmlHexadecimal() +
unicodeCandidate.getFitzpatrickUnicode();
}
};
@ -270,11 +264,7 @@ public class EmojiParser {
* @return the string without any emoji
*/
public static String removeAllEmojis(String str) {
EmojiTransformer emojiTransformer = new EmojiTransformer() {
public String transform(UnicodeCandidate unicodeCandidate) {
return "";
}
};
EmojiTransformer emojiTransformer = unicodeCandidate -> "";
return parseFromUnicode(str, emojiTransformer);
}
@ -292,14 +282,12 @@ public class EmojiParser {
String str,
final Collection<Emoji> emojisToRemove
) {
EmojiTransformer emojiTransformer = new EmojiTransformer() {
public String transform(UnicodeCandidate unicodeCandidate) {
if (!emojisToRemove.contains(unicodeCandidate.getEmoji())) {
return unicodeCandidate.getEmoji().getUnicode() +
unicodeCandidate.getFitzpatrickUnicode();
}
return "";
EmojiTransformer emojiTransformer = unicodeCandidate -> {
if (!emojisToRemove.contains(unicodeCandidate.getEmoji())) {
return unicodeCandidate.getEmoji().getUnicode() +
unicodeCandidate.getFitzpatrickUnicode();
}
return "";
};
return parseFromUnicode(str, emojiTransformer);
@ -317,14 +305,12 @@ public class EmojiParser {
String str,
final Collection<Emoji> emojisToKeep
) {
EmojiTransformer emojiTransformer = new EmojiTransformer() {
public String transform(UnicodeCandidate unicodeCandidate) {
if (emojisToKeep.contains(unicodeCandidate.getEmoji())) {
return unicodeCandidate.getEmoji().getUnicode() +
unicodeCandidate.getFitzpatrickUnicode();
}
return "";
EmojiTransformer emojiTransformer = unicodeCandidate -> {
if (emojisToKeep.contains(unicodeCandidate.getEmoji())) {
return unicodeCandidate.getEmoji().getUnicode() +
unicodeCandidate.getFitzpatrickUnicode();
}
return "";
};
return parseFromUnicode(str, emojiTransformer);
@ -340,9 +326,9 @@ public class EmojiParser {
*
* @return input string with all emojis transformed
*/
public static String parseFromUnicode(
String input,
EmojiTransformer transformer
private static String parseFromUnicode(
String input,
EmojiTransformer transformer
) {
int prev = 0;
StringBuilder sb = new StringBuilder();
@ -378,7 +364,7 @@ public class EmojiParser {
* @param input String to find all unicode emojis in
* @return List of UnicodeCandidates for each unicode emote in text
*/
protected static List<UnicodeCandidate> getUnicodeCandidates(String input) {
private static List<UnicodeCandidate> getUnicodeCandidates(String input) {
char[] inputCharArray = input.toCharArray();
List<UnicodeCandidate> candidates = new ArrayList<UnicodeCandidate>();
for (int i = 0; i < input.length(); i++) {
@ -417,7 +403,7 @@ public class EmojiParser {
* @return the end index of the unicode emoji starting at startPos. -1 if not
* found
*/
protected static int getEmojiEndPos(char[] text, int startPos) {
private static int getEmojiEndPos(char[] text, int startPos) {
int best = -1;
for (int j = startPos + 1; j <= text.length; j++) {
EmojiTrie.Matches status = EmojiManager.isEmoji(Arrays.copyOfRange(
@ -482,7 +468,7 @@ public class EmojiParser {
}
protected static class AliasCandidate {
static class AliasCandidate {
public final String fullString;
public final String alias;
public final Fitzpatrick fitzpatrick;

View File

@ -0,0 +1,15 @@
package com.fastaccess.provider.fabric;
import android.content.Context;
import android.support.annotation.NonNull;
/**
* Created by kosh on 14/08/2017.
*/
public class FabricProvider {
public static void initFabric(@NonNull Context context) {}//DO NOTHING IN DEBUG
public static void logPurchase(@NonNull String productKey) {}//DO NOTHING IN DEBUG
}

View File

@ -0,0 +1,31 @@
package com.fastaccess.provider.markdown
import java.util.*
/**
* Created by kosh on 11/08/2017.
*/
class CachedComments private constructor() {
val map = WeakHashMap<String, CharSequence>()
fun put(repo: String?, login: String?, number: Any?, comment: CharSequence) {
map.put("$repo/$login/$number", comment)
}
fun get(repo: String?, login: String?, number: Any?): CharSequence? {
return map["$repo/$login/$number"]
}
fun clear() {
map.clear()
}
private object Holder {
val INSTANCE = CachedComments()
}
companion object {
val instance: CachedComments by lazy { Holder.INSTANCE }
}
}

View File

@ -3,18 +3,31 @@ package com.fastaccess.provider.markdown;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Html;
import android.view.ViewTreeObserver;
import android.webkit.MimeTypeMap;
import android.widget.EditText;
import android.widget.TextView;
import com.annimon.stream.IntStream;
import com.fastaccess.helper.InputHelper;
import com.fastaccess.helper.Logger;
import com.fastaccess.provider.markdown.extension.emoji.EmojiExtension;
import com.fastaccess.provider.markdown.extension.mention.MentionExtension;
import com.fastaccess.provider.timeline.HtmlHelper;
import org.commonmark.Extension;
import org.commonmark.ext.autolink.AutolinkExtension;
import org.commonmark.ext.front.matter.YamlFrontMatterExtension;
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.ext.ins.InsExtension;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import java.util.Arrays;
import java.util.List;
/**
* Created by Kosh on 24 Nov 2016, 7:43 PM
*/
@ -35,12 +48,42 @@ public class MarkDownProvider {
public static void setMdText(@NonNull TextView textView, String markdown) {
if (!InputHelper.isEmpty(markdown)) {
Parser parser = Parser.builder().build();
Node node = parser.parse(markdown);
HtmlHelper.htmlIntoTextView(textView, HtmlRenderer.builder().build().render(node));
int width = textView.getMeasuredWidth();
if (width > 0) {
render(textView, markdown, width);
} else {
textView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override public void onGlobalLayout() {
textView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
render(textView, markdown, textView.getMeasuredWidth());
}
});
}
}
}
protected static void render(@NonNull TextView textView, String markdown, int width) {
List<Extension> extensions = Arrays.asList(
StrikethroughExtension.create(),
AutolinkExtension.create(),
TablesExtension.create(),
InsExtension.create(),
EmojiExtension.create(),
MentionExtension.create(),
YamlFrontMatterExtension.create());
Parser parser = Parser.builder()
.extensions(extensions)
.build();
Node node = parser.parse(markdown);
String rendered = HtmlRenderer
.builder()
.extensions(extensions)
.build()
.render(node);
Logger.e(rendered);
HtmlHelper.htmlIntoTextView(textView, rendered, (width - (textView.getPaddingStart() + textView.getPaddingEnd())));
}
public static void stripMdText(@NonNull TextView textView, String markdown) {
if (!InputHelper.isEmpty(markdown)) {
Parser parser = Parser.builder().build();
@ -49,7 +92,7 @@ public class MarkDownProvider {
}
}
private static String stripHtml(String html) {
public static String stripHtml(String html) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY).toString();
} else {
@ -125,7 +168,7 @@ public class MarkDownProvider {
int selectionStart = editText.getSelectionStart();
int selectionEnd = editText.getSelectionEnd();
String substring = source.substring(selectionStart, selectionEnd);
String result = "__" + substring + "__ ";
String result = "**" + substring + "** ";
editText.getText().replace(selectionStart, selectionEnd, result);
editText.setSelection(result.length() + selectionStart - 3);
@ -151,6 +194,17 @@ public class MarkDownProvider {
}
}
public static void addInlinleCode(@NonNull EditText editText) {
String source = editText.getText().toString();
int selectionStart = editText.getSelectionStart();
int selectionEnd = editText.getSelectionEnd();
String substring = source.substring(selectionStart, selectionEnd);
String result = "`" + substring + "` ";
editText.getText().replace(selectionStart, selectionEnd, result);
editText.setSelection(result.length() + selectionStart - 2);
}
public static void addStrikeThrough(@NonNull EditText editText) {
String source = editText.getText().toString();
int selectionStart = editText.getSelectionStart();

View File

@ -0,0 +1,21 @@
package com.fastaccess.provider.markdown.extension.emoji;
import org.commonmark.node.CustomNode;
import org.commonmark.node.Delimited;
/**
* Created by kosh on 20/08/2017.
*/
public class Emoji extends CustomNode implements Delimited {
private static final String DELIMITER = ":";
@Override public String getOpeningDelimiter() {
return DELIMITER;
}
@Override public String getClosingDelimiter() {
return DELIMITER;
}
}

View File

@ -0,0 +1,28 @@
package com.fastaccess.provider.markdown.extension.emoji;
import com.fastaccess.provider.markdown.extension.emoji.internal.EmojiDelimiterProcessor;
import com.fastaccess.provider.markdown.extension.emoji.internal.EmojiNodeRenderer;
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
/**
* Created by kosh on 20/08/2017.
*/
public class EmojiExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension {
private EmojiExtension() {}
public static Extension create() {
return new EmojiExtension();
}
@Override public void extend(Parser.Builder parserBuilder) {
parserBuilder.customDelimiterProcessor(new EmojiDelimiterProcessor());
}
@Override public void extend(HtmlRenderer.Builder rendererBuilder) {
rendererBuilder.nodeRendererFactory(EmojiNodeRenderer::new);
}
}

View File

@ -0,0 +1,42 @@
package com.fastaccess.provider.markdown.extension.emoji.internal;
import com.fastaccess.provider.markdown.extension.emoji.Emoji;
import org.commonmark.node.Node;
import org.commonmark.node.Text;
import org.commonmark.parser.delimiter.DelimiterProcessor;
import org.commonmark.parser.delimiter.DelimiterRun;
public class EmojiDelimiterProcessor implements DelimiterProcessor {
@Override public char getOpeningCharacter() {
return ':';
}
@Override public char getClosingCharacter() {
return ':';
}
@Override public int getMinLength() {
return 1;
}
@Override public int getDelimiterUse(DelimiterRun opener, DelimiterRun closer) {
if (opener.length() >= 1 && closer.length() >= 1) {
return 1;
} else {
return 0;
}
}
@Override public void process(Text opener, Text closer, int delimiterCount) {
Node emoji = new Emoji();
Node tmp = opener.getNext();
while (tmp != null && tmp != closer) {
Node next = tmp.getNext();
emoji.appendChild(tmp);
tmp = next;
}
opener.insertAfter(emoji);
}
}

View File

@ -0,0 +1,41 @@
package com.fastaccess.provider.markdown.extension.emoji.internal;
import org.commonmark.node.Node;
import org.commonmark.renderer.NodeRenderer;
import org.commonmark.renderer.html.HtmlNodeRendererContext;
import org.commonmark.renderer.html.HtmlWriter;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
public class EmojiNodeRenderer implements NodeRenderer {
private final HtmlNodeRendererContext context;
private final HtmlWriter html;
public EmojiNodeRenderer(HtmlNodeRendererContext context) {
this.context = context;
this.html = context.getWriter();
}
@Override public Set<Class<? extends Node>> getNodeTypes() {
return Collections.singleton(com.fastaccess.provider.markdown.extension.emoji.Emoji.class);
}
@Override public void render(Node node) {
Map<String, String> attributes = context.extendAttributes(node, "emoji", Collections.emptyMap());
html.tag("emoji", attributes);
renderChildren(node);
html.tag("/emoji");
}
private void renderChildren(Node parent) {
Node node = parent.getFirstChild();
while (node != null) {
Node next = node.getNext();
context.render(node);
node = next;
}
}
}

View File

@ -0,0 +1,21 @@
package com.fastaccess.provider.markdown.extension.mention;
import org.commonmark.node.CustomNode;
import org.commonmark.node.Delimited;
/**
* Created by kosh on 20/08/2017.
*/
public class Mention extends CustomNode implements Delimited {
private static final String DELIMITER = "@";
@Override public String getOpeningDelimiter() {
return DELIMITER;
}
@Override public String getClosingDelimiter() {
return " ";
}
}

View File

@ -0,0 +1,28 @@
package com.fastaccess.provider.markdown.extension.mention;
import com.fastaccess.provider.markdown.extension.mention.internal.MentionDelimiterProcessor;
import com.fastaccess.provider.markdown.extension.mention.internal.MentionNodeRenderer;
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
/**
* Created by kosh on 20/08/2017.
*/
public class MentionExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension {
private MentionExtension() {}
public static Extension create() {
return new MentionExtension();
}
@Override public void extend(Parser.Builder parserBuilder) {
parserBuilder.customDelimiterProcessor(new MentionDelimiterProcessor());
}
@Override public void extend(HtmlRenderer.Builder rendererBuilder) {
rendererBuilder.nodeRendererFactory(MentionNodeRenderer::new);
}
}

View File

@ -0,0 +1,42 @@
package com.fastaccess.provider.markdown.extension.mention.internal;
import com.fastaccess.provider.markdown.extension.mention.Mention;
import org.commonmark.node.Node;
import org.commonmark.node.Text;
import org.commonmark.parser.delimiter.DelimiterProcessor;
import org.commonmark.parser.delimiter.DelimiterRun;
public class MentionDelimiterProcessor implements DelimiterProcessor {
@Override public char getOpeningCharacter() {
return '@';
}
@Override public char getClosingCharacter() {
return ' ';
}
@Override public int getMinLength() {
return 1;
}
@Override public int getDelimiterUse(DelimiterRun opener, DelimiterRun closer) {
if (opener.length() >= 1 && closer.length() >= 1) {
return 1;
} else {
return 0;
}
}
@Override public void process(Text opener, Text closer, int delimiterCount) {
Node mention = new Mention();
Node tmp = opener.getNext();
while (tmp != null && tmp != closer) {
Node next = tmp.getNext();
mention.appendChild(tmp);
tmp = next;
}
opener.insertAfter(mention);
}
}

View File

@ -0,0 +1,43 @@
package com.fastaccess.provider.markdown.extension.mention.internal;
import com.fastaccess.provider.markdown.extension.mention.Mention;
import org.commonmark.node.Node;
import org.commonmark.renderer.NodeRenderer;
import org.commonmark.renderer.html.HtmlNodeRendererContext;
import org.commonmark.renderer.html.HtmlWriter;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
public class MentionNodeRenderer implements NodeRenderer {
private final HtmlNodeRendererContext context;
private final HtmlWriter html;
public MentionNodeRenderer(HtmlNodeRendererContext context) {
this.context = context;
this.html = context.getWriter();
}
@Override public Set<Class<? extends Node>> getNodeTypes() {
return Collections.singleton(Mention.class);
}
@Override public void render(Node node) {
Map<String, String> attributes = context.extendAttributes(node, "mention", Collections.emptyMap());
html.tag("mention", attributes);
renderChildren(node);
html.tag("/mention");
}
private void renderChildren(Node parent) {
Node node = parent.getFirstChild();
while (node != null) {
Node next = node.getNext();
context.render(node);
node = next;
}
}
}

View File

@ -58,10 +58,11 @@ public class RestProvider {
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.excludeFieldsWithModifiers(Modifier.FINAL, Modifier.TRANSIENT, Modifier.STATIC)
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.disableHtmlEscaping()
.setPrettyPrinting()
.create();
private static OkHttpClient provideOkHttpClient() {
public static OkHttpClient provideOkHttpClient() {
if (okHttpClient == null) {
OkHttpClient.Builder client = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {

View File

@ -7,14 +7,24 @@ import com.fastaccess.helper.InputHelper;
import java.io.IOException;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class PaginationInterceptor implements Interceptor {
@Override public Response intercept(@NonNull Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
Request request = chain.request();
Response response = chain.proceed(request);
Headers headers = chain.request().headers();
if (headers != null) {
if ((headers.values("Accept").contains("application/vnd.github.html") ||
headers.values("Accept").contains("application/vnd.github.VERSION.raw"))) {
return response;//return them as they are.
}
}
if (response.isSuccessful()) {
if (response.peekBody(1).string().equals("[")) {
String json = "{";

View File

@ -29,10 +29,11 @@ public class OnLoadMore<P> extends InfiniteScroll {
return parameter;
}
@Override public void onLoadMore(int page, int totalItemsCount) {
@Override public boolean onLoadMore(int page, int totalItemsCount) {
if (presenter != null) {
presenter.setPreviousTotal(totalItemsCount);
presenter.onCallApi(page + 1, parameter);
return presenter.onCallApi(page + 1, parameter);
}
return false;
}
}

View File

@ -31,6 +31,7 @@ import com.fastaccess.ui.modules.repos.issues.create.CreateIssueActivity;
import com.fastaccess.ui.modules.repos.issues.issue.details.IssuePagerActivity;
import com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.PullRequestPagerActivity;
import com.fastaccess.ui.modules.repos.wiki.WikiActivity;
import com.fastaccess.ui.modules.search.SearchActivity;
import com.fastaccess.ui.modules.trending.TrendingActivity;
import com.fastaccess.ui.modules.user.UserPagerActivity;
@ -338,6 +339,9 @@ public class SchemeParser {
} else if (segments != null && !segments.isEmpty() && segments.size() > 1 && segments.get(0).equalsIgnoreCase("orgs")) {
if ("invitation".equalsIgnoreCase(uri.getLastPathSegment())) {
return null;
} else if ("search".equalsIgnoreCase(uri.getLastPathSegment())) {
String query = uri.getQueryParameter("q");
return SearchActivity.getIntent(context, query);
} else {
return UserPagerActivity.createIntent(context, segments.get(1), true);
}

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

@ -57,7 +57,7 @@ object ThemeEngine {
setTaskDescription(activity)
}
@StyleRes fun getTheme(themeMode: Int, themeColor: Int): Int {
@StyleRes private fun getTheme(themeMode: Int, themeColor: Int): Int {
Logger.e(themeMode, themeColor)
// I wish if I could simplify this :'( too many cases for the love of god.
when (themeMode) {
@ -156,7 +156,7 @@ object ThemeEngine {
return R.style.ThemeLight
}
@StyleRes fun getDialogTheme(themeMode: Int, themeColor: Int): Int {
@StyleRes private fun getDialogTheme(themeMode: Int, themeColor: Int): Int {
when (themeMode) {
PrefGetter.LIGHT -> when (themeColor) {
PrefGetter.RED -> return R.style.DialogThemeLight_Red

View File

@ -62,7 +62,7 @@ public class CommentsHelper {
}
}
public static String getEmojiByUnicode(int unicode) {
private static String getEmojiByUnicode(int unicode) {
return new String(Character.toChars(unicode));
}

View File

@ -1,21 +1,20 @@
package com.fastaccess.provider.timeline;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.view.HapticFeedbackConstants;
import android.view.WindowManager;
import android.view.ViewTreeObserver;
import android.widget.PopupMenu;
import android.widget.TextView;
import com.fastaccess.App;
import com.fastaccess.R;
import com.fastaccess.helper.PrefGetter;
import com.fastaccess.helper.ViewHelper;
@ -23,6 +22,7 @@ import com.fastaccess.provider.scheme.SchemeParser;
import com.fastaccess.provider.timeline.handler.BetterLinkMovementExtended;
import com.fastaccess.provider.timeline.handler.DrawableHandler;
import com.fastaccess.provider.timeline.handler.EmojiHandler;
import com.fastaccess.provider.timeline.handler.HrHandler;
import com.fastaccess.provider.timeline.handler.ItalicHandler;
import com.fastaccess.provider.timeline.handler.LinkHandler;
import com.fastaccess.provider.timeline.handler.ListsHandler;
@ -45,9 +45,18 @@ import net.nightwhistler.htmlspanner.handlers.BoldHandler;
public class HtmlHelper {
public static void htmlIntoTextView(@NonNull TextView textView, @NonNull String html) {
public static void htmlIntoTextView(@NonNull TextView textView, @NonNull String html, int width) {
registerClickEvent(textView);
textView.setText(initHtml(textView).fromHtml(format(html).toString()));
if (textView.getMeasuredWidth() > 0) {
textView.setText(initHtml(textView, getActualWidth(textView)).fromHtml(format(html).toString()));
} else {
textView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override public void onGlobalLayout() {
textView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
textView.setText(initHtml(textView, getActualWidth(textView)).fromHtml(format(html).toString()));
}
});
}
}
private static void registerClickEvent(@NonNull TextView textView) {
@ -82,7 +91,11 @@ public class HtmlHelper {
});
}
private static HtmlSpanner initHtml(@NonNull TextView textView) {
private static int getActualWidth(TextView textView) {
return textView.getMeasuredWidth() - (convertDpToPx(textView.getContext(), 16));
}
private static HtmlSpanner initHtml(@NonNull TextView textView, int width) {
@PrefGetter.ThemeType int theme = PrefGetter.getThemeType();
@ColorInt int windowBackground = getWindowBackground(theme);
Drawable checked = ContextCompat.getDrawable(textView.getContext(), R.drawable.ic_checkbox_small);
@ -108,13 +121,12 @@ public class HtmlHelper {
mySpanner.registerHandler("sub", new SubScriptHandler());
mySpanner.registerHandler("sup", new SuperScriptHandler());
mySpanner.registerHandler("a", new LinkHandler());
mySpanner.registerHandler("hr", new HrHandler(windowBackground, width, false));
mySpanner.registerHandler("emoji", new EmojiHandler());
mySpanner.registerHandler("mention", new LinkHandler());
TableHandler tableHandler = new TableHandler();
tableHandler.setTextColor(ViewHelper.generateTextColor(windowBackground));
WindowManager windowManager = (WindowManager) App.getInstance().getSystemService(Context.WINDOW_SERVICE);
Point point = new Point();
windowManager.getDefaultDisplay().getRealSize(point);
tableHandler.setTableWidth((int) (point.x / 1.2));
tableHandler.setTextSize(18.0F);
tableHandler.setTableWidth(width);
mySpanner.registerHandler("table", tableHandler);
return mySpanner;
}
@ -144,10 +156,6 @@ public class HtmlHelper {
private static final String SIGNATURE_END = "</div>";
private static final String EMAIL_START = "<div class=\"email-fragment\">";
private static final String EMAIL_END = "</div>";
private static final String HIDDEN_REPLY_START = "<div class=\"email-hidden-reply\" style=\" display:none\">";
private static final String HIDDEN_REPLY_END = "</div>";
@ -206,4 +214,9 @@ public class HtmlHelper {
length = input.length();
}
}
private static int convertDpToPx(Context context, float dp) {
return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5f);
}
}

View File

@ -0,0 +1,135 @@
package com.fastaccess.provider.timeline
import com.fastaccess.data.dao.*
import com.fastaccess.data.dao.model.Comment
import com.fastaccess.data.dao.timeline.GenericEvent
import com.fastaccess.data.dao.timeline.PullRequestCommitModel
import com.fastaccess.data.dao.types.IssueEventType
import com.fastaccess.helper.InputHelper
import com.fastaccess.helper.Logger
import com.fastaccess.provider.rest.RestProvider
import com.google.gson.Gson
import com.google.gson.JsonObject
import io.reactivex.Observable
/**
* Created by kosh on 26/07/2017.
*/
object TimelineConverter {
fun convert(jsonObjects: List<JsonObject>?): Observable<TimelineModel> {
if (jsonObjects == null) return Observable.empty<TimelineModel>()
val gson = RestProvider.gson
return Observable.fromIterable(jsonObjects)
.map { jsonObject ->
val event = jsonObject.get("event").asString
val timeline = TimelineModel()
if (!InputHelper.isEmpty(event)) {
val type = IssueEventType.getType(event)
timeline.event = type
if (type != null) {
if (type == IssueEventType.commented) {
timeline.comment = getComment(jsonObject, gson)
} else {
timeline.genericEvent = getGenericEvent(jsonObject, gson)
}
}
} else {
timeline.genericEvent = getGenericEvent(jsonObject, gson)
}
timeline
}
.filter { filterEvents(it.event) }
}
fun convert(jsonObjects: List<JsonObject>?, comments: Pageable<ReviewCommentModel>?): List<TimelineModel> {
val list = arrayListOf<TimelineModel>()
if (jsonObjects == null) return list
val gson = RestProvider.gson
Logger.e(comments?.items?.size)
jsonObjects.onEach { jsonObject ->
val event = jsonObject.get("event").asString
val timeline = TimelineModel()
if (!InputHelper.isEmpty(event)) {
val type = IssueEventType.getType(event)
timeline.event = type
if (type != null) {
if (type == IssueEventType.commented) {
timeline.comment = getComment(jsonObject, gson)
list.add(timeline)
} else if (type == IssueEventType.commit_commented) {
val commit = getCommit(jsonObject, gson)
if (commit != null) {
val comment = commit.comments?.firstOrNull()
comment?.let {
commit.path = it.path
commit.position = it.position
commit.line = it.line
commit.login = it.user?.login
}
timeline.commit = commit
list.add(timeline)
}
} else if (type == IssueEventType.reviewed || type == IssueEventType
.changes_requested) {
val review = getReview(jsonObject, gson)
if (review != null) {
timeline.review = review
list.add(timeline)
val reviewsList = arrayListOf<TimelineModel>()
comments?.items?.filter { it.pullRequestReviewId == review.id }
?.onEach {
val grouped = GroupedReviewModel()
grouped.diffText = it.diffHunk
grouped.path = it.path
grouped.position = it.position
grouped.comments = arrayListOf(it)
grouped.id = it.id
val groupTimeline = TimelineModel()
groupTimeline.event = IssueEventType.GROUPED
groupTimeline.groupedReviewModel = grouped
reviewsList.add(groupTimeline)
}
comments?.items?.filter { it.pullRequestReviewId != review.id }?.onEach {
reviewsList.onEach { reviews ->
if (it.path == reviews.groupedReviewModel.path && it.position == reviews.groupedReviewModel.position) {
reviews.groupedReviewModel.comments.add(it)
}
}
}
list.addAll(reviewsList)
}
} else {
timeline.genericEvent = getGenericEvent(jsonObject, gson)
list.add(timeline)
}
}
} else {
timeline.genericEvent = getGenericEvent(jsonObject, gson)
list.add(timeline)
}
}
return list.filter({filterEvents(it.event)})
}
private fun getCommit(jsonObject: JsonObject, gson: Gson): PullRequestCommitModel? {
return gson.fromJson(jsonObject, PullRequestCommitModel::class.java)
}
private fun getGenericEvent(jsonObject: JsonObject, gson: Gson): GenericEvent {
return gson.fromJson(jsonObject, GenericEvent::class.java)
}
private fun getComment(jsonObject: JsonObject, gson: Gson): Comment {
return gson.fromJson(jsonObject, Comment::class.java)
}
private fun filterEvents(type: IssueEventType?): Boolean {
return type != null && type != IssueEventType.subscribed && type != IssueEventType.unsubscribed && type != IssueEventType.mentioned
}
private fun getReview(jsonObject: JsonObject, gson: Gson): ReviewModel? {
return gson.fromJson(jsonObject, ReviewModel::class.java)
}
}

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,39 +27,45 @@ 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);
String thisString = context.getString(R.string.this_value);
String in = context.getString(R.string.in_value);
if (event == IssueEventType.labeled || event == IssueEventType.unlabeled) {
if (issueEventModel.getAssignee() != null && issueEventModel.getAssigner() != null) {
spannableBuilder.bold(issueEventModel.getAssigner().getLogin());
} else if (issueEventModel.getActor() != null) {
spannableBuilder.bold(issueEventModel.getActor().getLogin());
}
spannableBuilder.bold(issueEventModel.getActor().getLogin());
spannableBuilder.append(" ").append(event.name().replaceAll("_", " "));
LabelModel labelModel = issueEventModel.getLabel();
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 +82,6 @@ public class TimelineProvider {
.append(in)
.append(" ")
.url(substring(issueEventModel.getCommitId()));
}
} else if (event == IssueEventType.assigned || event == IssueEventType.unassigned) {
spannableBuilder
@ -99,15 +104,17 @@ public class TimelineProvider {
} else if (event == IssueEventType.head_ref_deleted || event == IssueEventType.head_ref_restored) {
spannableBuilder.append(" ").append(event.name().replaceAll("_", " "),
new BackgroundColorSpan(HtmlHelper.getWindowBackground(PrefGetter.getThemeType())));
} else if (event == IssueEventType.milestoned || event == IssueEventType.demilestoned) {
spannableBuilder.append(" ")
.append(event == IssueEventType.milestoned ? "added this to the" : "removed this from the")
.append(" ")
.bold(issueEventModel.getMilestone().getTitle())
.append(" ")
.append("milestone");
} else {
spannableBuilder.append(" ").append(event.name().replaceAll("_", " "));
}
if (event == IssueEventType.milestoned || event == IssueEventType.demilestoned) {
spannableBuilder.append(" ")
.append(event == IssueEventType.milestoned ? to : from)
.append(" ")
.bold(issueEventModel.getMilestone().getTitle());
} else if (event == IssueEventType.renamed) {
if (event == IssueEventType.renamed) {
spannableBuilder
.append(" ")
.append(from)
@ -123,18 +130,40 @@ 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()));
} else if (sourceModel.getRepository() != null) {
title.url(sourceModel.getRepository().getName());
}
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

@ -5,10 +5,10 @@ import android.text.style.ImageSpan;
import android.widget.TextView;
import com.fastaccess.helper.InputHelper;
import com.fastaccess.helper.Logger;
import com.fastaccess.provider.timeline.handler.drawable.DrawableGetter;
import net.nightwhistler.htmlspanner.TagNodeHandler;
import net.nightwhistler.htmlspanner.spans.CenterSpan;
import org.htmlcleaner.TagNode;
@ -30,15 +30,14 @@ import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
@Override public void handleTagNode(TagNode node, SpannableStringBuilder builder, int start, int end) {
String src = node.getAttributeByName("src");
boolean isGif = "gif".equalsIgnoreCase(node.getAttributeByName("alt"));
Logger.e(isGif);
if (!InputHelper.isEmpty(src)) {
builder.append("");
if (isNull()) return;
CenterSpan centerSpan = new CenterSpan();
DrawableGetter imageGetter = new DrawableGetter(textView);
builder.setSpan(centerSpan, start, builder.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan(new ImageSpan(imageGetter.getDrawable(src)), start, builder.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
appendNewLine(builder);
}
}
}

View File

@ -2,7 +2,7 @@ package com.fastaccess.provider.timeline.handler;
import android.text.SpannableStringBuilder;
import com.fastaccess.helper.InputHelper;
import com.fastaccess.helper.Logger;
import com.fastaccess.provider.emoji.Emoji;
import com.fastaccess.provider.emoji.EmojiManager;
@ -18,11 +18,17 @@ public class EmojiHandler extends TagNodeHandler {
@Override public void handleTagNode(TagNode node, SpannableStringBuilder builder, int start, int end) {
String emoji = node.getAttributeByName("alias");
if (!InputHelper.isEmpty(emoji)) {
if (emoji != null) {
Emoji unicode = EmojiManager.getForAlias(emoji);
if (unicode != null && unicode.getUnicode() != null) {
builder.replace(start, end, unicode.getUnicode());
}
} else if (node.getText() != null) {
Logger.e(node.getText());
Emoji unicode = EmojiManager.getForAlias(node.getText().toString());
if (unicode != null && unicode.getUnicode() != null) {
builder.replace(start, end, unicode.getUnicode());
}
}
}

View File

@ -0,0 +1,33 @@
package com.fastaccess.provider.timeline.handler;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import net.nightwhistler.htmlspanner.TagNodeHandler;
import net.nightwhistler.htmlspanner.spans.CenterSpan;
import org.htmlcleaner.TagNode;
import lombok.AllArgsConstructor;
/**
* Created by kosh on 30/07/2017.
*/
@AllArgsConstructor public class HrHandler extends TagNodeHandler {
private final int color;
private final int width;
private final boolean isHeader;
@Override public void handleTagNode(TagNode tagNode, SpannableStringBuilder spannableStringBuilder, int i, int i1) {
spannableStringBuilder.append("\n");
SpannableStringBuilder builder = new SpannableStringBuilder("$");
HrSpan hrSpan = new HrSpan(color, width);
builder.setSpan(hrSpan, 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan(new CenterSpan(), 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.append("\n");
spannableStringBuilder.append(builder);
}
}

View File

@ -0,0 +1,43 @@
package com.fastaccess.provider.timeline.handler;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.text.style.LineHeightSpan;
import android.text.style.ReplacementSpan;
public class HrSpan extends ReplacementSpan implements LineHeightSpan {
private final int width;
private final int color;
HrSpan(int color, int width) {
this.color = color;
this.width = width;
Drawable drawable = new ColorDrawable(color);
}
@Override public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
return (int) paint.measureText(text, start, end);
}
@Override public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top,
int y, int bottom, @NonNull Paint paint) {
final int currentColor = paint.getColor();
paint.setColor(color);
paint.setStyle(Paint.Style.FILL);
int height = 10;
canvas.drawRect(new Rect(0, bottom - height, (int) x + width, bottom), paint);
paint.setColor(currentColor);
}
@Override public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) {
fm.top /= 3;
fm.ascent /= 3;
fm.bottom /= 3;
fm.descent /= 3;
}
}

View File

@ -18,6 +18,10 @@ public class LinkHandler extends TagNodeHandler {
@Override public void handleTagNode(TagNode node, SpannableStringBuilder spannableStringBuilder, int start, int end) {
String href = node.getAttributeByName("href");
spannableStringBuilder.setSpan(new LinkSpan(href, linkColor), start, end, 33);
if (href != null) {
spannableStringBuilder.setSpan(new LinkSpan(href, linkColor), start, end, 33);
} else if (node.getText() != null) {
spannableStringBuilder.setSpan(new LinkSpan("https://github.com/" + node.getText().toString(), linkColor), start, end, 33);
}
}
}

View File

@ -42,7 +42,7 @@ import lombok.NoArgsConstructor;
return node.getParent() == null ? null : node.getParent().getName();
}
@Override public void beforeChildren(TagNode node, SpannableStringBuilder builder) {
@Override public void beforeChildren(TagNode node, SpannableStringBuilder builder) {
TodoItems todoItem = null;
if (node.getChildTags() != null && node.getChildTags().length > 0) {
for (TagNode tagNode : node.getChildTags()) {

View File

@ -19,6 +19,7 @@ import android.text.style.AlignmentSpan;
import android.text.style.ImageSpan;
import net.nightwhistler.htmlspanner.TagNodeHandler;
import net.nightwhistler.htmlspanner.spans.CenterSpan;
import org.htmlcleaner.TagNode;
@ -35,12 +36,12 @@ import java.util.List;
*/
public class TableHandler extends TagNodeHandler {
private int tableWidth = 400;
private int tableWidth = 500;
private Typeface typeFace = Typeface.DEFAULT;
private float textSize = 16f;
private float textSize = 30f;
private int textColor = Color.BLACK;
private static final int PADDING = 5;
private static final int PADDING = 20;
/**
* Sets how wide the table should be.
@ -144,7 +145,6 @@ public class TableHandler extends TagNodeHandler {
int rowHeight = 0;
for (Spanned cell : row) {
StaticLayout layout = new StaticLayout(cell, textPaint, columnWidth
- 2 * PADDING, Alignment.ALIGN_NORMAL, 1f, 0f, true);
@ -165,19 +165,14 @@ public class TableHandler extends TagNodeHandler {
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
builder.setSpan(new ImageSpan(drawable), start + i, builder.length(), 33);
}
/*
We add an empty last row to work around a rendering issue where
the last row would appear detached.
*/
builder.append("\uFFFC");
Drawable drawable = new TableRowDrawable(new ArrayList<Spanned>(), table.isDrawBorder());
Drawable drawable = new TableRowDrawable(new ArrayList<>(), table.isDrawBorder());
drawable.setBounds(0, 0, tableWidth, 1);
builder.setSpan(new ImageSpan(drawable), builder.length() - 1, builder.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan((AlignmentSpan) () -> Alignment.ALIGN_CENTER, start, builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan(new CenterSpan(), start, builder.length(), 33);
builder.append("\n");
}

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;
@ -22,7 +20,7 @@ import java.util.Set;
public class DrawableGetter implements Html.ImageGetter, Drawable.Callback {
private WeakReference<TextView> container;
private final Set<GifBitmapTarget> cachedTargets;
private final Set<GlideDrawableTarget> cachedTargets;
public DrawableGetter(TextView tv) {
tv.setTag(R.id.drawable_callback, this);
@ -30,27 +28,14 @@ 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();
final GifBitmapTarget target = new GifBitmapTarget(urlDrawable, container);
final GlideDrawableTarget target = new GlideDrawableTarget(urlDrawable, container);
load.into(target);
cachedTargets.add(target);
}
@ -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 (GlideDrawableTarget target : drawableGetter.cachedTargets) {
Glide.clear(target);
}
}
}
}

View File

@ -11,11 +11,11 @@ import com.fastaccess.R;
import java.lang.ref.WeakReference;
class GifBitmapTarget extends SimpleTarget<GlideDrawable> {
class GlideDrawableTarget extends SimpleTarget<GlideDrawable> {
private final UrlDrawable urlDrawable;
private final WeakReference<TextView> container;
GifBitmapTarget(UrlDrawable urlDrawable, WeakReference<TextView> container) {
GlideDrawableTarget(UrlDrawable urlDrawable, WeakReference<TextView> container) {
this.urlDrawable = urlDrawable;
this.container = container;
}
@ -34,7 +34,7 @@ class GifBitmapTarget extends SimpleTarget<GlideDrawable> {
width = (float) resource.getIntrinsicWidth() * multiplier;
height = (float) resource.getIntrinsicHeight() * multiplier;
}
Rect rect = new Rect(0, 0, Math.round(width), Math.round(height));
Rect rect = new Rect(0, 0, (int) Math.round(width / 1.3), (int) Math.round(height / 1.3));
resource.setBounds(rect);
urlDrawable.setBounds(rect);
urlDrawable.setDrawable(resource);

Some files were not shown because too many files have changed in this diff Show More