diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index ffa16517..40c66e59 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -19,9 +19,18 @@ + diff --git a/README.md b/README.md index a03a0cb7..f46bdafa 100644 --- a/README.md +++ b/README.md @@ -89,5 +89,6 @@ Thanks to all the open source libraries, contributors and donators. - Ge0rg/memorizingTrustManager - Dimezis/blurView - Mikaelhg/urlbuilder +- emoji-java [Follow me on Fediverse - mastodon.social/@mmarif](https://mastodon.social/@mmarif) diff --git a/app/build.gradle b/app/build.gradle index a570c5f0..59b2c58b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,8 +38,10 @@ android { abortOnError false } compileOptions { - targetCompatibility = "8" - sourceCompatibility = "8" + coreLibraryDesugaringEnabled true + + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } defaultConfig{ vectorDrawables.useSupportLibrary = true @@ -60,7 +62,7 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.3.0-alpha02' implementation 'com.google.android.material:material:1.3.0-alpha03' - implementation 'androidx.constraintlayout:constraintlayout:2.0.2' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" testImplementation 'junit:junit:4.13.1' @@ -108,5 +110,7 @@ dependencies { implementation "androidx.work:work-runtime:$work_version" implementation "com.eightbitlab:blurview:1.6.4" implementation "io.mikael:urlbuilder:2.0.9" + implementation 'com.vdurmont:emoji-java:5.1.1' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.0' } diff --git a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java index f7bb36ec..41c80252 100644 --- a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java @@ -59,6 +59,7 @@ import org.mian.gitnex.models.Labels; import org.mian.gitnex.models.UpdateIssueAssignees; import org.mian.gitnex.models.WatchInfo; import org.mian.gitnex.viewmodels.IssueCommentsViewModel; +import org.mian.gitnex.views.ReactionList; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -208,6 +209,10 @@ public class IssueDetailActivity extends BaseActivity implements LabelsListAdapt switch(text) { + case "onResume": + onResume(); + break; + case "showLabels": showLabels(); break; @@ -519,7 +524,13 @@ public class IssueDetailActivity extends BaseActivity implements LabelsListAdapt viewBinding.divider.setVisibility(View.VISIBLE); } - adapter = new IssueCommentsAdapter(ctx, issueCommentsMain, getSupportFragmentManager(), this::onResume); + Bundle bundle = new Bundle(); + bundle.putString("repoOwner", repoOwner); + bundle.putString("repoName", repoName); + bundle.putInt("issueNumber", issueIndex); + + adapter = new IssueCommentsAdapter(ctx, bundle, issueCommentsMain, getSupportFragmentManager(), this::onResume); + viewBinding.recyclerView.setAdapter(adapter); }); @@ -718,6 +729,15 @@ public class IssueDetailActivity extends BaseActivity implements LabelsListAdapt .setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getCreated_at()), ctx)); } + Bundle bundle = new Bundle(); + bundle.putString("repoOwner", repoOwner); + bundle.putString("repoName", repoName); + bundle.putInt("issueId", singleIssue.getNumber()); + + ReactionList reactionList = new ReactionList(ctx, bundle); + viewBinding.commentReactionBadges.removeAllViews(); + viewBinding.commentReactionBadges.addView(reactionList); + if(singleIssue.getMilestone() != null) { viewBinding.issueMilestone.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/org/mian/gitnex/adapters/FilesDiffAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/FilesDiffAdapter.java index 2b7811c8..86bec71c 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/FilesDiffAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/FilesDiffAdapter.java @@ -15,8 +15,9 @@ import android.widget.TextView; import androidx.fragment.app.FragmentManager; import org.mian.gitnex.R; import org.mian.gitnex.fragments.BottomSheetReplyFragment; -import org.mian.gitnex.helpers.DiffTextView; +import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.models.FileDiffView; +import org.mian.gitnex.views.DiffTextView; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentSkipListMap; @@ -48,11 +49,11 @@ public class FilesDiffAdapter extends BaseAdapter { selectedViews = new ConcurrentSkipListMap<>(); - COLOR_ADDED = getColorFromAttribute(R.attr.diffAddedColor); - COLOR_REMOVED = getColorFromAttribute(R.attr.diffRemovedColor); - COLOR_NORMAL = getColorFromAttribute(R.attr.primaryBackgroundColor); - COLOR_SELECTED = getColorFromAttribute(R.attr.diffSelectedColor); - COLOR_FONT = getColorFromAttribute(R.attr.inputTextColor); + COLOR_ADDED = AppUtil.getColorFromAttribute(context, R.attr.diffAddedColor); + COLOR_REMOVED = AppUtil.getColorFromAttribute(context, R.attr.diffRemovedColor); + COLOR_NORMAL = AppUtil.getColorFromAttribute(context, R.attr.primaryBackgroundColor); + COLOR_SELECTED = AppUtil.getColorFromAttribute(context, R.attr.diffSelectedColor); + COLOR_FONT = AppUtil.getColorFromAttribute(context, R.attr.inputTextColor); } @@ -256,13 +257,4 @@ public class FilesDiffAdapter extends BaseAdapter { } - private int getColorFromAttribute(int resid) { - - TypedValue typedValue = new TypedValue(); - context.getTheme().resolveAttribute(resid, typedValue, true); - - return typedValue.data; - - } - } diff --git a/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java index a60230c6..8d78c7db 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java @@ -10,6 +10,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.fragment.app.FragmentManager; @@ -29,6 +30,8 @@ import org.mian.gitnex.helpers.TimeHelper; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.models.IssueComments; +import org.mian.gitnex.views.ReactionList; +import org.mian.gitnex.views.ReactionSpinner; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -42,17 +45,22 @@ import retrofit2.Callback; public class IssueCommentsAdapter extends RecyclerView.Adapter { private final Context ctx; + private final TinyDB tinyDB; + private final Bundle bundle; private final List issuesComments; private final FragmentManager fragmentManager; private final BottomSheetReplyFragment.OnInteractedListener onInteractedListener; - public IssueCommentsAdapter(Context ctx, List issuesCommentsMain, FragmentManager fragmentManager, BottomSheetReplyFragment.OnInteractedListener onInteractedListener) { + public IssueCommentsAdapter(Context ctx, Bundle bundle, List issuesCommentsMain, FragmentManager fragmentManager, BottomSheetReplyFragment.OnInteractedListener onInteractedListener) { this.ctx = ctx; + this.bundle = bundle; this.issuesComments = issuesCommentsMain; this.fragmentManager = fragmentManager; this.onInteractedListener = onInteractedListener; + tinyDB = TinyDB.getInstance(ctx); + } class IssueCommentViewHolder extends RecyclerView.ViewHolder { @@ -63,6 +71,7 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter { final Context ctx = v.getContext(); - final TinyDB tinyDb = TinyDB.getInstance(ctx); - final String loginUid = tinyDb.getString("loginUid"); + final String loginUid = tinyDB.getString("loginUid"); @SuppressLint("InflateParams") View vw = LayoutInflater.from(ctx).inflate(R.layout.bottom_sheet_issue_comments, null); @@ -102,6 +111,24 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter { + + tinyDB.putBoolean("commentEdited", true); + + onInteractedListener.onInteracted(); + dialog.dismiss(); + + }); + + linearLayout.addView(reactionSpinner); + commentMenuEdit.setOnClickListener(v1 -> { Bundle bundle = new Bundle(); @@ -125,7 +152,7 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter { + + tinyDB.putBoolean("singleIssueUpdate", true); + + bmListener.onButtonClicked("onResume"); + dismiss(); + + }); + + linearLayout.addView(reactionSpinner); + if(tinyDB.getString("issueType").equalsIgnoreCase("Pull")) { editIssue.setText(R.string.editPrText); diff --git a/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java b/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java index 3558d735..fdfefe85 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java +++ b/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java @@ -9,7 +9,9 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.util.Base64; import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.View; +import androidx.annotation.ColorInt; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -131,6 +133,16 @@ public class AppUtil { } + @ColorInt + public static int getColorFromAttribute(Context context, int resid) { + + TypedValue typedValue = new TypedValue(); + context.getTheme().resolveAttribute(resid, typedValue, true); + + return typedValue.data; + + } + public static String customDateFormat(String customDate) { String[] parts = customDate.split("-"); diff --git a/app/src/main/java/org/mian/gitnex/helpers/ChangeLog.java b/app/src/main/java/org/mian/gitnex/helpers/ChangeLog.java index 6d0f4d9e..bfe5a4cf 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ChangeLog.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ChangeLog.java @@ -1,10 +1,5 @@ package org.mian.gitnex.helpers; -import java.io.IOException; -import java.util.Objects; -import org.mian.gitnex.R; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; import android.app.Activity; import android.content.pm.PackageManager; import android.content.res.Resources; @@ -12,6 +7,11 @@ import android.content.res.XmlResourceParser; import android.text.Html; import android.util.Log; import androidx.appcompat.app.AlertDialog; +import org.mian.gitnex.R; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import java.io.IOException; +import java.util.Objects; /** * Author M M Arif @@ -90,13 +90,14 @@ public class ChangeLog { String changelogMessage = getChangelog(resId, res); - androidx.appcompat.app.AlertDialog.Builder builder = new AlertDialog.Builder(changelogActivity); + AlertDialog.Builder builder = new AlertDialog.Builder(changelogActivity); + builder.setTitle(R.string.changelogTitle); builder.setMessage(Html.fromHtml("" + changelogMessage + "")); builder.setNeutralButton(R.string.close, null); builder.setCancelable(false); - builder.create(); - builder.show(); + + builder.create().show(); } diff --git a/app/src/main/java/org/mian/gitnex/interfaces/ApiInterface.java b/app/src/main/java/org/mian/gitnex/interfaces/ApiInterface.java index d07f58f7..e7edecb3 100644 --- a/app/src/main/java/org/mian/gitnex/interfaces/ApiInterface.java +++ b/app/src/main/java/org/mian/gitnex/interfaces/ApiInterface.java @@ -1,13 +1,16 @@ package org.mian.gitnex.interfaces; import com.google.gson.JsonElement; +import org.mian.gitnex.models.APISettings; import org.mian.gitnex.models.AddEmail; +import org.mian.gitnex.models.AttachmentSettings; import org.mian.gitnex.models.Branches; import org.mian.gitnex.models.Collaborators; import org.mian.gitnex.models.Commits; import org.mian.gitnex.models.CreateIssue; import org.mian.gitnex.models.CreateLabel; import org.mian.gitnex.models.CreatePullRequest; +import org.mian.gitnex.models.CreateStatusOption; import org.mian.gitnex.models.DeleteFile; import org.mian.gitnex.models.EditFile; import org.mian.gitnex.models.Emails; @@ -15,8 +18,10 @@ import org.mian.gitnex.models.ExploreRepositories; import org.mian.gitnex.models.Files; import org.mian.gitnex.models.GiteaVersion; import org.mian.gitnex.models.IssueComments; +import org.mian.gitnex.models.IssueReaction; import org.mian.gitnex.models.Issues; import org.mian.gitnex.models.Labels; +import org.mian.gitnex.models.MarkdownOption; import org.mian.gitnex.models.MergePullRequest; import org.mian.gitnex.models.Milestones; import org.mian.gitnex.models.NewFile; @@ -28,10 +33,14 @@ import org.mian.gitnex.models.OrganizationRepository; import org.mian.gitnex.models.Permission; import org.mian.gitnex.models.PullRequests; import org.mian.gitnex.models.Releases; +import org.mian.gitnex.models.RepositorySettings; import org.mian.gitnex.models.RepositoryTransfer; +import org.mian.gitnex.models.Status; import org.mian.gitnex.models.Teams; +import org.mian.gitnex.models.UISettings; import org.mian.gitnex.models.UpdateIssueAssignees; import org.mian.gitnex.models.UpdateIssueState; +import org.mian.gitnex.models.UserHeatmap; import org.mian.gitnex.models.UserInfo; import org.mian.gitnex.models.UserOrganizations; import org.mian.gitnex.models.UserRepositories; @@ -70,8 +79,26 @@ public interface ApiInterface { @GET("version") // gitea version API Call getGiteaVersionWithToken(@Header("Authorization") String token); - @GET("user") // username, full name, email - Call getUserInfo(@Header("Authorization") String token); + @POST("markdown") + Call renderMarkdown(@Header("Authorization") String token, @Body MarkdownOption markdownOption); + + @POST("markdown/raw") + Call renderRawMarkdown(@Header("Authorization") String token, @Body String body); + + @GET("signing-key.gpg") // Get default signing-key.gpg + Call getSigningKey(@Header("Authorization") String token); + + @GET("settings/api") // Get instance's global settings for api + Call getAPISettings(@Header("Authorization") String token); + + @GET("settings/attachment") // Get instance's global settings for attachments + Call getAttachmentSettings(@Header("Authorization") String token); + + @GET("settings/repository") // Get instance's global settings for repositories + Call getRepositorySettings(@Header("Authorization") String token); + + @GET("settings/ui") // Get instance's global settings for ui + Call getUISettings(@Header("Authorization") String token); @GET("users/{username}/tokens") // get user token Call> getUserTokens(@Header("Authorization") String authorization, @Path("username") String loginUid); @@ -112,6 +139,9 @@ public interface ApiInterface { @PUT("repos/{owner}/{repo}/notifications") // Mark notification threads as read, pinned or unread on a specific repo Call markRepoNotificationThreadsAsRead(@Header("Authorization") String token, @Path("owner") String owner, @Path("repo") String repo, @Query("all") Boolean all, @Query("status-types") String[] statusTypes, @Query("to-status") String toStatus, @Query("last_read_at") String last_read_at); + @GET("user") // username, full name, email + Call getUserInfo(@Header("Authorization") String token); + @GET("user/orgs") // get user organizations Call> getUserOrgs(@Header("Authorization") String token); @@ -130,6 +160,24 @@ public interface ApiInterface { @POST("user/repos") // create new repository Call createNewUserRepository(@Header("Authorization") String token, @Body OrganizationRepository jsonStr); + @GET("user/followers") // get user followers + Call> getFollowers(@Header("Authorization") String token); + + @GET("user/following") // get following + Call> getFollowing(@Header("Authorization") String token); + + @POST("user/emails") // add new email + Call addNewEmail(@Header("Authorization") String token, @Body AddEmail jsonStr); + + @GET("user/emails") // get user emails + Call> getUserEmails(@Header("Authorization") String token); + + @GET("user/starred") // get user starred repositories + Call> getUserStarredRepos(@Header("Authorization") String token, @Query("page") int page, @Query("limit") int limit); + + @GET("users/{username}/heatmap") // Get a user's heatmap + Call> getUserHeatmap(@Header("Authorization") String token, @Path("username") String username); + @GET("repos/{owner}/{repo}") // get repo information Call getUserRepository(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName); @@ -193,9 +241,6 @@ public interface ApiInterface { @PATCH("repos/{owner}/{repo}/labels/{index}") // update / patch a label Call patchLabel(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("index") int labelIndex, @Body CreateLabel jsonStr); - @GET("user/starred") // get user starred repositories - Call> getUserStarredRepos(@Header("Authorization") String token, @Query("page") int page, @Query("limit") int limit); - @GET("orgs/{orgName}/repos") // get repositories by org Call> getReposByOrg(@Header("Authorization") String token, @Path("orgName") String orgName, @Query("page") int page, @Query("limit") int limit); @@ -226,17 +271,23 @@ public interface ApiInterface { @PATCH("repos/{owner}/{repo}/issues/comments/{commentId}") // edit a comment Call patchIssueComment(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("commentId") int commentId, @Body IssueComments jsonStr); - @GET("user/followers") // get user followers - Call> getFollowers(@Header("Authorization") String token); + @GET("repos/{owner}/{repo}/issues/comments/{commentId}/reactions") // get comment reactions + Call> getIssueCommentReactions(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("commentId") int commentId); - @GET("user/following") // get following - Call> getFollowing(@Header("Authorization") String token); + @POST("repos/{owner}/{repo}/issues/comments/{commentId}/reactions") // add reaction to a comment + Call setIssueCommentReaction(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("commentId") int commentId, @Body IssueReaction jsonStr); - @POST("user/emails") // add new email - Call addNewEmail(@Header("Authorization") String token, @Body AddEmail jsonStr); + @HTTP(method = "DELETE", path = "repos/{owner}/{repo}/issues/comments/{commentId}/reactions", hasBody = true) // delete a reaction of a comment + Call removeIssueCommentReaction(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("commentId") int commentId, @Body IssueReaction jsonStr); - @GET("user/emails") // get user emails - Call> getUserEmails(@Header("Authorization") String token); + @GET("repos/{owner}/{repo}/issues/{index}/reactions") // get issue reactions + Call> getIssueReactions(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("index") int issueIndex); + + @POST("repos/{owner}/{repo}/issues/{index}/reactions") // add reaction to an issue + Call setIssueReaction(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("index") int issueIndex, @Body IssueReaction jsonStr); + + @HTTP(method = "DELETE", path = "repos/{owner}/{repo}/issues/{index}/reactions", hasBody = true) // delete a reaction of an issue + Call removeIssueReaction(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("index") int issueIndex, @Body IssueReaction jsonStr); @GET("repos/{owner}/{repo}/issues/{index}/labels") // get issue labels Call> getIssueLabels(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("index") int issueIndex); @@ -363,4 +414,12 @@ public interface ApiInterface { @GET("repos/{owner}/{repo}/forks") // get all repo forks Call> getRepositoryForks(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Query("page") int page, @Query("limit") int limit); + + @POST("repos/{owner}/{repo}/statuses/{sha}") // Create a commit status + Call createCommitStatus(@Header("Authorization") String token, @Path("owner") String owner, @Path("repo") String repo, @Path("sha") String sha, @Body CreateStatusOption createStatusOption); + + @GET("repos/{owner}/{repo}/statuses/{sha}") // Get a commit's statuses + Call> getCommitStatuses(@Header("Authorization") String token, @Path("owner") String owner, @Path("repo") String repo, @Query("sort") String sort, @Query("state") String state, @Query("page") int page, @Query("limit") int limit); + + } diff --git a/app/src/main/java/org/mian/gitnex/models/APISettings.java b/app/src/main/java/org/mian/gitnex/models/APISettings.java new file mode 100644 index 00000000..ff633aea --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/models/APISettings.java @@ -0,0 +1,34 @@ +package org.mian.gitnex.models; + +/** + * Author opyale + */ + +public class APISettings { + + private int default_git_trees_per_page; + private int default_max_blob_size; + private int default_paging_num; + private int max_response_items; + + public int getDefault_git_trees_per_page() { + + return default_git_trees_per_page; + } + + public int getDefault_max_blob_size() { + + return default_max_blob_size; + } + + public int getDefault_paging_num() { + + return default_paging_num; + } + + public int getMax_response_items() { + + return max_response_items; + } + +} diff --git a/app/src/main/java/org/mian/gitnex/models/AttachmentSettings.java b/app/src/main/java/org/mian/gitnex/models/AttachmentSettings.java new file mode 100644 index 00000000..0f2dcef0 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/models/AttachmentSettings.java @@ -0,0 +1,34 @@ +package org.mian.gitnex.models; + +/** + * Author opyale + */ + +public class AttachmentSettings { + + private String allowed_types; + private boolean enabled; + private float max_files; + private float max_size; + + public String getAllowed_types() { + + return allowed_types; + } + + public boolean isEnabled() { + + return enabled; + } + + public float getMax_files() { + + return max_files; + } + + public float getMax_size() { + + return max_size; + } + +} diff --git a/app/src/main/java/org/mian/gitnex/models/CreateStatusOption.java b/app/src/main/java/org/mian/gitnex/models/CreateStatusOption.java new file mode 100644 index 00000000..74cdc8d8 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/models/CreateStatusOption.java @@ -0,0 +1,36 @@ +package org.mian.gitnex.models; + +/** + * Author opyale + */ + +public class CreateStatusOption { + + private String context; + private String description; + private String statusState; + private String target_url; + + public CreateStatusOption(String context, String description, String statusState, String target_url) { + this.context = context; + this.description = description; + this.statusState = statusState; + this.target_url = target_url; + } + + public String getContext() { + return context; + } + + public String getDescription() { + return description; + } + + public String getStatusState() { + return statusState; + } + + public String getTarget_url() { + return target_url; + } +} diff --git a/app/src/main/java/org/mian/gitnex/models/IssueReaction.java b/app/src/main/java/org/mian/gitnex/models/IssueReaction.java new file mode 100644 index 00000000..74fcc838 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/models/IssueReaction.java @@ -0,0 +1,71 @@ +package org.mian.gitnex.models; + +import java.util.Date; + +/** + * Author 6543 + */ + +public class IssueReaction { + + private String content; + private userObject user; + private Date created_at; + + public IssueReaction(String content) { + this.content = content; + } + + public static class userObject { + + private int id; + private String login; + private String full_name; + private String email; + private String avatar_url; + private String language; + private String username; + + public int getId() { + return id; + } + + public String getLogin() { + return login; + } + + public String getFull_name() { + return full_name; + } + + public String getEmail() { + return email; + } + + public String getAvatar_url() { + return avatar_url; + } + + public String getLanguage() { + return language; + } + + public String getUsername() { + return username; + } + + } + + public String getContent() { + return content; + } + + public userObject getUser() { + return user; + } + + public Date getCreated_at() { + return created_at; + } + +} diff --git a/app/src/main/java/org/mian/gitnex/models/MarkdownOption.java b/app/src/main/java/org/mian/gitnex/models/MarkdownOption.java new file mode 100644 index 00000000..444dea44 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/models/MarkdownOption.java @@ -0,0 +1,42 @@ +package org.mian.gitnex.models; + +/** + * Author opyale + */ + +public class MarkdownOption { + + private String Context; + private String Mode; + private String Text; + private boolean Wiki; + + public MarkdownOption(String context, String mode, String text, boolean wiki) { + + Context = context; + Mode = mode; + Text = text; + Wiki = wiki; + } + + public String getContext() { + + return Context; + } + + public String getMode() { + + return Mode; + } + + public String getText() { + + return Text; + } + + public boolean isWiki() { + + return Wiki; + } + +} diff --git a/app/src/main/java/org/mian/gitnex/models/MultiSelectModel.java b/app/src/main/java/org/mian/gitnex/models/MultiSelectModel.java deleted file mode 100644 index 710a8b35..00000000 --- a/app/src/main/java/org/mian/gitnex/models/MultiSelectModel.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.mian.gitnex.models; - -/** - * Author com.github.abumoallim, modified by M M Arif - */ - -public class MultiSelectModel { - - private Integer id; - private String name; - private Boolean isSelected; - - public MultiSelectModel(Integer id, String name) { - this.id = id; - this.name = name; - } - - public int getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Boolean getSelected() { - return isSelected; - } - - public void setSelected(Boolean selected) { - isSelected = selected; - } - -} diff --git a/app/src/main/java/org/mian/gitnex/models/RepositorySettings.java b/app/src/main/java/org/mian/gitnex/models/RepositorySettings.java new file mode 100644 index 00000000..0fdf189f --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/models/RepositorySettings.java @@ -0,0 +1,22 @@ +package org.mian.gitnex.models; + +/** + * Author opyale + */ + +public class RepositorySettings { + + private boolean http_git_disabled; + private boolean mirrors_disabled; + + public boolean isHttp_git_disabled() { + + return http_git_disabled; + } + + public boolean isMirrors_disabled() { + + return mirrors_disabled; + } + +} diff --git a/app/src/main/java/org/mian/gitnex/models/Status.java b/app/src/main/java/org/mian/gitnex/models/Status.java new file mode 100644 index 00000000..a0012d11 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/models/Status.java @@ -0,0 +1,56 @@ +package org.mian.gitnex.models; + +import java.util.Date; + +/** + * Author opyale + */ + +public class Status { + + private String context; + private Date created_at; + private UserInfo creator; + private String description; + private int id; + private String status; + private String target_url; + private Date updated_at; + private String url; + + public String getContext() { + return context; + } + + public Date getCreated_at() { + return created_at; + } + + public UserInfo getCreator() { + return creator; + } + + public String getDescription() { + return description; + } + + public int getId() { + return id; + } + + public String getStatus() { + return status; + } + + public String getTarget_url() { + return target_url; + } + + public Date getUpdated_at() { + return updated_at; + } + + public String getUrl() { + return url; + } +} diff --git a/app/src/main/java/org/mian/gitnex/models/UISettings.java b/app/src/main/java/org/mian/gitnex/models/UISettings.java new file mode 100644 index 00000000..67b6e7ba --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/models/UISettings.java @@ -0,0 +1,16 @@ +package org.mian.gitnex.models; + +/** + * Author opyale + */ + +public class UISettings { + + private String[] allowed_reactions; + + public String[] getAllowed_reactions() { + + return allowed_reactions; + } + +} diff --git a/app/src/main/java/org/mian/gitnex/models/UserHeatmap.java b/app/src/main/java/org/mian/gitnex/models/UserHeatmap.java new file mode 100644 index 00000000..f0a62c3b --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/models/UserHeatmap.java @@ -0,0 +1,12 @@ +package org.mian.gitnex.models; + +/** + * Author opyale + */ + +public class UserHeatmap { + + private long contributions; + private long timestamp; + +} diff --git a/app/src/main/java/org/mian/gitnex/helpers/DiffTextView.java b/app/src/main/java/org/mian/gitnex/views/DiffTextView.java similarity index 97% rename from app/src/main/java/org/mian/gitnex/helpers/DiffTextView.java rename to app/src/main/java/org/mian/gitnex/views/DiffTextView.java index dcb81e7a..2e9503c3 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/DiffTextView.java +++ b/app/src/main/java/org/mian/gitnex/views/DiffTextView.java @@ -1,4 +1,4 @@ -package org.mian.gitnex.helpers; +package org.mian.gitnex.views; import android.content.Context; import android.util.AttributeSet; diff --git a/app/src/main/java/org/mian/gitnex/views/ReactionList.java b/app/src/main/java/org/mian/gitnex/views/ReactionList.java new file mode 100644 index 00000000..3e13dfcd --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/views/ReactionList.java @@ -0,0 +1,140 @@ +package org.mian.gitnex.views; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Bundle; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.LinearLayout; +import android.widget.TextView; +import androidx.cardview.widget.CardView; +import com.vdurmont.emoji.Emoji; +import com.vdurmont.emoji.EmojiManager; +import org.mian.gitnex.R; +import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.models.IssueReaction; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import retrofit2.Response; + +/** + * @author opyale + */ + +@SuppressLint("ViewConstructor") +public class ReactionList extends HorizontalScrollView { + + private enum ReactionType { COMMENT, ISSUE } + + @SuppressLint("SetTextI18n") + public ReactionList(Context context, Bundle bundle) { + + super(context); + + LinearLayout root = new LinearLayout(context); + + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + root.setOrientation(LinearLayout.HORIZONTAL); + root.setGravity(Gravity.START); + root.setLayoutParams(layoutParams); + + addView(root); + setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + TinyDB tinyDB = TinyDB.getInstance(context); + + String loginUid = tinyDB.getString("loginUid"); + String repoOwner = bundle.getString("repoOwner"); + String repoName = bundle.getString("repoName"); + + int id; + ReactionType reactionType; + + if(bundle.containsKey("commentId")) { + id = bundle.getInt("commentId"); + reactionType = ReactionType.COMMENT; + } else { + id = bundle.getInt("issueId"); + reactionType = ReactionType.ISSUE; + } + + new Thread(() -> { + + try { + + Response> response = null; + + switch(reactionType) { + + case ISSUE: + response = RetrofitClient + .getApiInterface(context) + .getIssueReactions(Authorization.get(context), repoOwner, repoName, id) + .execute(); + break; + + case COMMENT: + response = RetrofitClient + .getApiInterface(context) + .getIssueCommentReactions(Authorization.get(context), repoOwner, repoName, id) + .execute(); + break; + + } + + Map> sortedReactions = new HashMap<>(); + + if(response.isSuccessful() && response.body() != null) { + + for(IssueReaction issueReaction : response.body()) { + + if(sortedReactions.containsKey(issueReaction.getContent())) { + + sortedReactions.get(issueReaction.getContent()).add(issueReaction); + } else { + List issueReactions = new ArrayList<>(); + issueReactions.add(issueReaction); + + sortedReactions.put(issueReaction.getContent(), issueReactions); + } + } + } + + for(String content : sortedReactions.keySet()) { + + List issueReactions = sortedReactions.get(content); + + @SuppressLint("InflateParams") CardView reactionBadge = (CardView) LayoutInflater.from(context) + .inflate(R.layout.layout_reaction_badge, this, false); + + for(IssueReaction issueReaction : issueReactions) { + + if(issueReaction.getUser().getLogin().equals(loginUid)) { + reactionBadge.setCardBackgroundColor(AppUtil.getColorFromAttribute(context, R.attr.inputSelectedColor)); + break; + } + } + + Emoji emoji = EmojiManager.getForAlias(content); + + ((TextView) reactionBadge.findViewById(R.id.symbol)).setText(((emoji == null) ? content : emoji.getUnicode()) + " " + issueReactions.size()); + root.post(() -> root.addView(reactionBadge)); + + } + + } catch (IOException ignored) {} + + }).start(); + + } + +} diff --git a/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java b/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java new file mode 100644 index 00000000..32da4d05 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java @@ -0,0 +1,238 @@ +package org.mian.gitnex.views; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Bundle; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.LinearLayout; +import android.widget.TextView; +import androidx.cardview.widget.CardView; +import com.vdurmont.emoji.Emoji; +import com.vdurmont.emoji.EmojiManager; +import org.mian.gitnex.R; +import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.models.IssueReaction; +import org.mian.gitnex.models.UISettings; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import retrofit2.Response; + +/** + * @author opyale + */ + +@SuppressLint("ViewConstructor") +public class ReactionSpinner extends HorizontalScrollView { + + private enum ReactionType { COMMENT, ISSUE } + private enum ReactionAction { REMOVE, ADD } + + private OnInteractedListener onInteractedListener; + + public ReactionSpinner(Context context, Bundle bundle) { + + super(context); + + LinearLayout root = new LinearLayout(context); + + int dens = AppUtil.getPixelsFromDensity(context, 10); + + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + root.setOrientation(LinearLayout.HORIZONTAL); + root.setPadding(dens, 0, dens, 0); + root.setGravity(Gravity.START); + root.setLayoutParams(layoutParams); + + TinyDB tinyDB = TinyDB.getInstance(context); + + String loginUid = tinyDB.getString("loginUid"); + String repoOwner = bundle.getString("repoOwner"); + String repoName = bundle.getString("repoName"); + + int id; + ReactionType reactionType; + + if(bundle.containsKey("commentId")) { + id = bundle.getInt("commentId"); + reactionType = ReactionType.COMMENT; + } else { + id = bundle.getInt("issueId"); + reactionType = ReactionType.ISSUE; + } + + new Thread(() -> { + + try { + + List allReactions = getReactions(repoOwner, repoName, reactionType, id); + + for(String allowedReaction : getAllowedReactions()) { + + @SuppressLint("InflateParams") CardView reactionButton = (CardView) LayoutInflater.from(context) + .inflate(R.layout.layout_reaction_button, root, false); + + IssueReaction myReaction = null; + + for(IssueReaction issueReaction : allReactions) { + + if(issueReaction.getContent().equals(allowedReaction) && issueReaction.getUser().getLogin().equals(loginUid)) { + myReaction = issueReaction; + break; + } + } + + ReactionAction reactionAction; + + if(myReaction != null) { + + reactionButton.setCardBackgroundColor(AppUtil.getColorFromAttribute(context, R.attr.inputSelectedColor)); + reactionAction = ReactionAction.REMOVE; + } else { + reactionAction = ReactionAction.ADD; + } + + reactionButton.setOnClickListener(v -> new Thread(() -> { + + try { + if(react(repoOwner, repoName, reactionType, reactionAction, new IssueReaction(allowedReaction), id)) { + v.post(() -> onInteractedListener.onInteracted()); + } + } catch(IOException ignored) {} + + }).start()); + + Emoji emoji = EmojiManager.getForAlias(allowedReaction); + + ((TextView) reactionButton.findViewById(R.id.symbol)).setText((emoji == null) ? allowedReaction : emoji.getUnicode()); + root.post(() -> root.addView(reactionButton)); + + } + + } catch(IOException ignored) {} + + }).start(); + + setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + addView(root); + + } + + private boolean react(String repoOwner, String repoName, ReactionType reactionType, ReactionAction reactionAction, IssueReaction issueReaction, int id) throws IOException { + + Response response = null; + + switch(reactionType) { + + case ISSUE: + switch(reactionAction) { + + case ADD: + response = RetrofitClient + .getApiInterface(getContext()) + .setIssueReaction(Authorization.get(getContext()), repoOwner, repoName, id, issueReaction) + .execute(); + break; + + + case REMOVE: + response = RetrofitClient + .getApiInterface(getContext()) + .removeIssueReaction(Authorization.get(getContext()), repoOwner, repoName, id, issueReaction) + .execute(); + break; + + } + break; + + case COMMENT: + switch(reactionAction) { + + case ADD: + response = RetrofitClient + .getApiInterface(getContext()) + .setIssueCommentReaction(Authorization.get(getContext()), repoOwner, repoName, id, issueReaction) + .execute(); + break; + + + case REMOVE: + response = RetrofitClient + .getApiInterface(getContext()) + .removeIssueCommentReaction(Authorization.get(getContext()), repoOwner, repoName, id, issueReaction) + .execute(); + break; + + } + break; + + } + + return response.isSuccessful(); + + } + + private List getReactions(String repoOwner, String repoName, ReactionType reactionType, int id) throws IOException { + + Response> response = null; + + switch(reactionType) { + + case ISSUE: + response = RetrofitClient + .getApiInterface(getContext()) + .getIssueReactions(Authorization.get(getContext()), repoOwner, repoName, id) + .execute(); + break; + + case COMMENT: + response = RetrofitClient + .getApiInterface(getContext()) + .getIssueCommentReactions(Authorization.get(getContext()), repoOwner, repoName, id) + .execute(); + break; + + } + + if(response.isSuccessful() && response.body() != null) + return response.body(); + else + return Collections.emptyList(); + + } + + private List getAllowedReactions() throws IOException { + + List allowedReactions = new ArrayList<>(); + + Response response = RetrofitClient + .getApiInterface(getContext()) + .getUISettings(Authorization.get(getContext())) + .execute(); + + if(response.isSuccessful() && response.body() != null) { + allowedReactions.addAll(Arrays.asList(response.body().getAllowed_reactions())); + } else { + allowedReactions.addAll(Arrays.asList("+1", "-1", "laugh", "hooray", "confused", "heart", "rocket", "eyes")); + } + + return allowedReactions; + + } + + public void setOnInteractedListener(OnInteractedListener onInteractedListener) { + this.onInteractedListener = onInteractedListener; + } + + public interface OnInteractedListener { void onInteracted(); } + +} diff --git a/app/src/main/res/layout/activity_issue_detail.xml b/app/src/main/res/layout/activity_issue_detail.xml index 0d642551..650d8b18 100644 --- a/app/src/main/res/layout/activity_issue_detail.xml +++ b/app/src/main/res/layout/activity_issue_detail.xml @@ -225,6 +225,14 @@ + + + android:paddingBottom="12dp"> + android:layout_height="wrap_content" + android:orientation="vertical"> + + + + + + + + + diff --git a/app/src/main/res/layout/layout_reaction_button.xml b/app/src/main/res/layout/layout_reaction_button.xml new file mode 100644 index 00000000..3fe143a9 --- /dev/null +++ b/app/src/main/res/layout/layout_reaction_button.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/app/src/main/res/layout/list_issue_comments.xml b/app/src/main/res/layout/list_issue_comments.xml index c27ba22a..07525a85 100644 --- a/app/src/main/res/layout/list_issue_comments.xml +++ b/app/src/main/res/layout/list_issue_comments.xml @@ -71,4 +71,11 @@ android:textIsSelectable="true" android:textSize="14sp" /> + + diff --git a/app/src/main/res/values-v23/themes.xml b/app/src/main/res/values-v23/themes.xml index a669193b..bd16df27 100644 --- a/app/src/main/res/values-v23/themes.xml +++ b/app/src/main/res/values-v23/themes.xml @@ -17,6 +17,7 @@ @color/lightThemeTextColor @color/lightThemeBackground @color/lightThemeInputBackground + @color/lightThemInputSelected @color/lightThemeInputTextColor @style/AppThemeLightCheckBoxStyle @color/darkGreen @@ -57,6 +58,7 @@ @color/retroThemeTextColor @color/retroThemeBackground @color/retroThemeInputBackground + @color/retroThemeInputSelected @color/retroThemeInputTextColor @style/AppThemeRetroCheckBoxStyle @color/retroThemeColorPrimary diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 268fa6c6..9817fbe9 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -6,6 +6,7 @@ + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 04a2448f..b024bc12 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -12,6 +12,7 @@ #009486 #ffffff #1d1d1d + #3A3A3A #1d1d1d #f2711c @color/btnBackground @@ -35,7 +36,8 @@ #e0e0e0 #646565 #f9f9f9 - #f2f2f2 + #EFEFEF + #C9CCCC #212121 #dbdbdb @@ -45,6 +47,7 @@ #212f3c #fcfcfc #f2f2f2 + #D3D3D3 #6200EE #dbdbdb #6200EE diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 44401d8b..0ae51044 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -17,6 +17,7 @@ @color/colorWhite @color/colorPrimary @color/inputBackground + @color/inputSelected @color/colorWhite @style/AppThemeCheckBoxStyle @color/darkGreen @@ -56,6 +57,7 @@ @color/lightThemeTextColor @color/lightThemeBackground @color/lightThemeInputBackground + @color/lightThemInputSelected @color/lightThemeInputTextColor @style/AppThemeLightCheckBoxStyle @color/darkGreen @@ -95,6 +97,7 @@ @color/retroThemeTextColor @color/retroThemeBackground @color/retroThemeInputBackground + @color/retroThemeInputSelected @color/retroThemeInputTextColor @style/AppThemeRetroCheckBoxStyle @color/retroThemeColorPrimary