From 4493917b39e3804b350d865393648525597f6c56 Mon Sep 17 00:00:00 2001 From: M M Arif Date: Thu, 14 Nov 2024 18:42:50 +0000 Subject: [PATCH] Pinned issues, d and bash lang support, pin and unpin an issue (#1401) Closes #1395 Closes #1398 Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1401 Co-authored-by: M M Arif Co-committed-by: M M Arif --- .woodpecker/build.yml | 1 + .woodpecker/check.yml | 9 + app/build.gradle | 9 +- .../org/mian/gitnex/actions/IssueActions.java | 91 +++++++++ .../mian/gitnex/actions/LabelsActions.java | 2 +- .../mian/gitnex/adapters/IssuesAdapter.java | 21 ++- .../mian/gitnex/core/MainGrammarLocator.java | 8 + .../BottomSheetSingleIssueFragment.java | 31 ++++ .../mian/gitnex/fragments/IssuesFragment.java | 64 ++++++- .../java/org/mian/gitnex/helpers/AppUtil.java | 1 + .../codeeditor/languages/BashLanguage.java | 154 ++++++++++++++++ .../codeeditor/languages/DLanguage.java | 162 +++++++++++++++++ .../codeeditor/languages/Language.java | 4 +- .../gitnex/helpers/contexts/IssueContext.java | 11 ++ app/src/main/res/drawable/ic_pin.xml | 19 +- app/src/main/res/drawable/ic_unpin.xml | 34 ++++ .../res/layout/bottom_sheet_single_issue.xml | 27 +++ app/src/main/res/layout/fragment_issues.xml | 52 ++++-- app/src/main/res/layout/list_issues.xml | 1 + .../main/res/layout/list_issues_pinned.xml | 172 ++++++++++++++++++ app/src/main/res/values/gitea_version.xml | 2 +- app/src/main/res/values/strings.xml | 7 + build.gradle | 2 +- gradle.properties | 1 - gradle/wrapper/gradle-wrapper.properties | 2 +- 25 files changed, 853 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/BashLanguage.java create mode 100644 app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/DLanguage.java create mode 100644 app/src/main/res/drawable/ic_unpin.xml create mode 100644 app/src/main/res/layout/list_issues_pinned.xml diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index a612ae0f..9fc936ba 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -4,6 +4,7 @@ steps: commands: - ./gradlew assembleFreeRelease when: + event: [ push ] path: [ app/**, build.gradle ] sign: diff --git a/.woodpecker/check.yml b/.woodpecker/check.yml index d3611a30..377bb663 100644 --- a/.woodpecker/check.yml +++ b/.woodpecker/check.yml @@ -5,13 +5,22 @@ steps: pattern: "*.java" regex: " \\\\* \\\\@author [\\\\S\\\\s]+" must_contain: true + when: + event: [ push ] + path: [ app/**, build.gradle ] style: image: alvrme/alpine-android:android-32-jdk17 commands: - ./gradlew :app:spotlessCheck + when: + event: [ push ] + path: [ app/**, build.gradle ] test: image: alvrme/alpine-android:android-32-jdk17 commands: - ./gradlew test + when: + event: [ push ] + path: [ app/**, build.gradle ] diff --git a/app/build.gradle b/app/build.gradle index 3ddc01be..40a08b36 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,7 @@ android { } buildFeatures { viewBinding true + buildConfig true } buildTypes { release { @@ -58,12 +59,12 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.google.android.material:material:1.12.0' - implementation 'androidx.compose.material3:material3:1.3.0' - implementation 'androidx.compose.material3:material3-window-size-class:1.3.0' + implementation 'androidx.compose.material3:material3:1.3.1' + implementation 'androidx.compose.material3:material3-window-size-class:1.3.1' implementation 'androidx.viewpager2:viewpager2:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.constraintlayout:constraintlayout:2.2.0' implementation "androidx.legacy:legacy-support-v4:1.0.0" - implementation "androidx.lifecycle:lifecycle-viewmodel:2.7.0" + implementation "androidx.lifecycle:lifecycle-viewmodel:2.8.7" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.2.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' diff --git a/app/src/main/java/org/mian/gitnex/actions/IssueActions.java b/app/src/main/java/org/mian/gitnex/actions/IssueActions.java index 2f5de64e..c00a1375 100644 --- a/app/src/main/java/org/mian/gitnex/actions/IssueActions.java +++ b/app/src/main/java/org/mian/gitnex/actions/IssueActions.java @@ -254,6 +254,97 @@ public class IssueActions { }); } + public static void pinIssue(final Context ctx, IssueContext issue) { + + Call call; + + call = + RetrofitClient.getApiInterface(ctx) + .pinIssue( + issue.getRepository().getOwner(), + issue.getRepository().getName(), + (long) issue.getIssueIndex()); + + call.enqueue( + new Callback<>() { + + @Override + public void onResponse( + @NonNull Call call, @NonNull retrofit2.Response response) { + + if (response.isSuccessful()) { + + if (response.code() == 204) { + + Toasty.success(ctx, ctx.getString(R.string.issue_pinned)); + IssuesFragment.resumeIssues = true; + issue.setPinned(true); + } + } else if (response.code() == 401) { + + AlertDialogs.authorizationTokenRevokedDialog(ctx); + } else { + + Toasty.error(ctx, ctx.getString(R.string.pinning_failed)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + + Toasty.error( + ctx, + ctx.getResources().getString(R.string.genericServerResponseError)); + } + }); + } + + public static void unpinIssue(final Context ctx, IssueContext issue) { + + Call call; + + call = + RetrofitClient.getApiInterface(ctx) + .unpinIssue( + issue.getRepository().getOwner(), + issue.getRepository().getName(), + (long) issue.getIssueIndex()); + + call.enqueue( + new Callback<>() { + + @Override + public void onResponse( + @NonNull Call call, @NonNull retrofit2.Response response) { + + if (response.isSuccessful()) { + + if (response.code() == 204) { + + Toasty.success(ctx, ctx.getString(R.string.issue_unpinned)); + IssuesFragment.resumeIssues = true; + issue.setPinned(false); + issue.getIssue().setPinOrder(0L); + } + } else if (response.code() == 401) { + + AlertDialogs.authorizationTokenRevokedDialog(ctx); + } else { + + Toasty.error(ctx, ctx.getString(R.string.unpinning_failed)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + + Toasty.error( + ctx, + ctx.getResources().getString(R.string.genericServerResponseError)); + } + }); + } + public static ActionResult reply( Context context, String comment, IssueContext issue) { diff --git a/app/src/main/java/org/mian/gitnex/actions/LabelsActions.java b/app/src/main/java/org/mian/gitnex/actions/LabelsActions.java index e19fef05..9f50aa7f 100644 --- a/app/src/main/java/org/mian/gitnex/actions/LabelsActions.java +++ b/app/src/main/java/org/mian/gitnex/actions/LabelsActions.java @@ -45,7 +45,7 @@ public class LabelsActions { assert issueLabelsList != null; - if (issueLabelsList.size() > 0) { + if (!issueLabelsList.isEmpty()) { for (Label label : issueLabelsList) { diff --git a/app/src/main/java/org/mian/gitnex/adapters/IssuesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/IssuesAdapter.java index f46e2d56..0a57cf87 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/IssuesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/IssuesAdapter.java @@ -22,6 +22,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.amulyakhare.textdrawable.TextDrawable; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.google.android.material.card.MaterialCardView; import com.vdurmont.emoji.EmojiParser; import java.util.List; import java.util.Locale; @@ -49,18 +50,24 @@ public class IssuesAdapter extends RecyclerView.Adapter private List issuesList; private Runnable loadMoreListener; private boolean isLoading = false, isMoreDataAvailable = true; + private final String type; - public IssuesAdapter(Context ctx, List issuesListMain) { + public IssuesAdapter(Context ctx, List issuesListMain, String type) { this.context = ctx; this.issuesList = issuesListMain; tinyDb = TinyDB.getInstance(context); + this.type = type; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(context); - return new IssuesHolder(inflater.inflate(R.layout.list_issues, parent, false)); + if (type.equalsIgnoreCase("pinned")) { + return new IssuesHolder(inflater.inflate(R.layout.list_issues_pinned, parent, false)); + } else { + return new IssuesHolder(inflater.inflate(R.layout.list_issues, parent, false)); + } } @Override @@ -118,6 +125,7 @@ public class IssuesAdapter extends RecyclerView.Adapter private final HorizontalScrollView labelsScrollViewDots; private final LinearLayout frameLabelsDots; private final ImageView commentIcon; + private final MaterialCardView cardView; private Issue issueObject; IssuesHolder(View itemView) { @@ -132,6 +140,7 @@ public class IssuesAdapter extends RecyclerView.Adapter labelsScrollViewDots = itemView.findViewById(R.id.labelsScrollViewDots); frameLabelsDots = itemView.findViewById(R.id.frameLabelsDots); commentIcon = itemView.findViewById(R.id.comment_icon); + cardView = itemView.findViewById(R.id.card_view); new Handler() .postDelayed( @@ -291,6 +300,14 @@ public class IssuesAdapter extends RecyclerView.Adapter context.getResources().getColor(R.color.releasePre, null)); } + if (issue.getState().equalsIgnoreCase("open") && type.equalsIgnoreCase("pinned")) { + cardView.setStrokeColor(context.getResources().getColor(R.color.darkGreen, null)); + } + if (issue.getState().equalsIgnoreCase("closed") && type.equalsIgnoreCase("pinned")) { + cardView.setStrokeColor( + context.getResources().getColor(R.color.iconIssuePrClosedColor, null)); + } + this.issueCreatedTime.setText(TimeHelper.formatTime(issue.getCreatedAt(), locale)); this.issueCreatedTime.setOnClickListener( new ClickListener( diff --git a/app/src/main/java/org/mian/gitnex/core/MainGrammarLocator.java b/app/src/main/java/org/mian/gitnex/core/MainGrammarLocator.java index ae051dc8..b65dcf8f 100644 --- a/app/src/main/java/org/mian/gitnex/core/MainGrammarLocator.java +++ b/app/src/main/java/org/mian/gitnex/core/MainGrammarLocator.java @@ -39,6 +39,14 @@ public class MainGrammarLocator { case "csx": return "csharp"; + case "bash": + case "sh": + case "bsh": + return "sh"; + + case "d": + return "d"; + case "groovy": case "gradle": case "gvy": diff --git a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetSingleIssueFragment.java b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetSingleIssueFragment.java index b54d6c6c..c02e3db3 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetSingleIssueFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetSingleIssueFragment.java @@ -34,6 +34,7 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment { private final IssueContext issue; private final String issueCreator; private BottomSheetListener bmListener; + private boolean issuePinStatus = false; public BottomSheetSingleIssueFragment(IssueContext issue, String username) { this.issue = issue; @@ -288,6 +289,36 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment { binding.editLabels.setVisibility(View.GONE); } + binding.pinIssue.setOnClickListener( + pinIssue -> { + IssueActions.pinIssue(ctx, issue); + dismiss(); + }); + + binding.unpinIssue.setOnClickListener( + unpinIssue -> { + IssueActions.unpinIssue(ctx, issue); + dismiss(); + }); + + if (issue.getIssue().getPinOrder() > 0) { + binding.pinIssue.setVisibility(View.GONE); + binding.unpinIssue.setVisibility(View.VISIBLE); + } else { + binding.pinIssue.setVisibility(View.VISIBLE); + binding.unpinIssue.setVisibility(View.GONE); + + if (issue.isPinned()) { + binding.pinIssue.setVisibility(View.GONE); + binding.unpinIssue.setVisibility(View.VISIBLE); + } + } + + if (!isRepoAdmin) { + binding.pinIssue.setVisibility(View.GONE); + binding.unpinIssue.setVisibility(View.GONE); + } + return binding.getRoot(); } diff --git a/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java index d249dc7a..3e6e4f45 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java @@ -43,7 +43,9 @@ public class IssuesFragment extends Fragment { private Context context; private Menu menu; private List issuesList; + private List pinnedIssuesList; private IssuesAdapter adapter; + private IssuesAdapter adapterPinned; private int pageSize = Constants.issuesPageInit; private int resultLimit; private RepositoryContext repository; @@ -71,6 +73,7 @@ public class IssuesFragment extends Fragment { resultLimit = Constants.getCurrentResultLimit(context); issuesList = new ArrayList<>(); + pinnedIssuesList = new ArrayList<>(); fragmentIssuesBinding.pullToRefresh.setOnRefreshListener( () -> @@ -91,7 +94,7 @@ public class IssuesFragment extends Fragment { }, 200)); - adapter = new IssuesAdapter(context, issuesList); + adapter = new IssuesAdapter(context, issuesList, ""); adapter.setLoadMoreListener( () -> fragmentIssuesBinding.recyclerView.post( @@ -114,6 +117,12 @@ public class IssuesFragment extends Fragment { fragmentIssuesBinding.recyclerView.setLayoutManager(new LinearLayoutManager(context)); fragmentIssuesBinding.recyclerView.setAdapter(adapter); + adapterPinned = new IssuesAdapter(context, pinnedIssuesList, "pinned"); + LinearLayoutManager horizontalLayoutManager = + new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false); + fragmentIssuesBinding.rvPinnedIssues.setLayoutManager(horizontalLayoutManager); + fragmentIssuesBinding.rvPinnedIssues.setAdapter(adapterPinned); + ((RepoDetailActivity) requireActivity()) .setFragmentRefreshListener( issueState -> { @@ -125,7 +134,7 @@ public class IssuesFragment extends Fragment { issuesList.clear(); - adapter = new IssuesAdapter(context, issuesList); + adapter = new IssuesAdapter(context, issuesList, ""); adapter.setLoadMoreListener( () -> fragmentIssuesBinding.recyclerView.post( @@ -169,7 +178,7 @@ public class IssuesFragment extends Fragment { filterIssueByMilestone -> { issuesList.clear(); - adapter = new IssuesAdapter(context, issuesList); + adapter = new IssuesAdapter(context, issuesList, ""); adapter.setLoadMoreListener( () -> fragmentIssuesBinding.recyclerView.post( @@ -217,6 +226,8 @@ public class IssuesFragment extends Fragment { repository.getIssueMilestoneFilterName(), null); + getPinnedIssues(repository.getOwner(), repository.getName()); + if (archived) { fragmentIssuesBinding.createNewIssue.setVisibility(View.GONE); } @@ -251,10 +262,53 @@ public class IssuesFragment extends Fragment { repository.getIssueState().toString(), repository.getIssueMilestoneFilterName(), null); + getPinnedIssues(repository.getOwner(), repository.getName()); resumeIssues = false; } } + private void getPinnedIssues(String repoOwner, String repoName) { + + Call> call = + RetrofitClient.getApiInterface(context).repoListPinnedIssues(repoOwner, repoName); + + call.enqueue( + new Callback<>() { + + @Override + public void onResponse( + @NonNull Call> call, + @NonNull Response> response) { + + if (response.code() == 200) { + assert response.body() != null; + if (!response.body().isEmpty()) { + fragmentIssuesBinding.pinnedIssuesFrame.setVisibility(View.VISIBLE); + pinnedIssuesList.clear(); + pinnedIssuesList.addAll(response.body()); + adapterPinned.notifyDataChanged(); + fragmentIssuesBinding.noDataIssues.setVisibility(View.GONE); + } else { + pinnedIssuesList.clear(); + adapterPinned.notifyDataChanged(); + fragmentIssuesBinding.noDataIssues.setVisibility(View.VISIBLE); + } + fragmentIssuesBinding.progressBar.setVisibility(View.GONE); + } else if (response.code() == 404) { + fragmentIssuesBinding.noDataIssues.setVisibility(View.VISIBLE); + fragmentIssuesBinding.progressBar.setVisibility(View.GONE); + } else { + Toasty.error(context, getString(R.string.genericError)); + } + } + + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + Toasty.error(context, getString(R.string.genericServerResponseError)); + } + }); + } + private void loadInitial( String repoOwner, String repoName, @@ -296,10 +350,12 @@ public class IssuesFragment extends Fragment { issuesList.clear(); issuesList.addAll(response.body()); adapter.notifyDataChanged(); + adapterPinned.notifyDataChanged(); fragmentIssuesBinding.noDataIssues.setVisibility(View.GONE); } else { issuesList.clear(); adapter.notifyDataChanged(); + adapterPinned.notifyDataChanged(); fragmentIssuesBinding.noDataIssues.setVisibility(View.VISIBLE); } fragmentIssuesBinding.progressBar.setVisibility(View.GONE); @@ -366,8 +422,10 @@ public class IssuesFragment extends Fragment { fragmentIssuesBinding.getRoot(), getString(R.string.noMoreData)); adapter.setMoreDataAvailable(false); + adapterPinned.setMoreDataAvailable(false); } adapter.notifyDataChanged(); + adapterPinned.notifyDataChanged(); fragmentIssuesBinding.progressBar.setVisibility(View.GONE); } else { Toasty.error(context, getString(R.string.genericError)); 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 8d3f10b2..95060805 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java +++ b/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java @@ -94,6 +94,7 @@ public class AppUtil { "c", "cc", "cpp", + "d", "h", "cxx", "cyc", diff --git a/app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/BashLanguage.java b/app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/BashLanguage.java new file mode 100644 index 00000000..2274302d --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/BashLanguage.java @@ -0,0 +1,154 @@ +package org.mian.gitnex.helpers.codeeditor.languages; + +import com.amrdeveloper.codeview.Code; +import com.amrdeveloper.codeview.Keyword; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * @author M M Arif + */ +public class BashLanguage extends Language { + + private static final Pattern PATTERN_BUILTINS = Pattern.compile("[,:;[->]{}()]"); + private static final Pattern PATTERN_SINGLE_LINE_COMMENT = Pattern.compile("//[^\\n]*"); + private static final Pattern PATTERN_MULTI_LINE_COMMENT = + Pattern.compile("/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/"); + private static final Pattern PATTERN_ATTRIBUTE = Pattern.compile("\\.[a-zA-Z0-9_]+"); + private static final Pattern PATTERN_OPERATION = + Pattern.compile( + ":|==|>|<|!=|>=|<=|->|=|>|<|%|-|-=|%=|\\+|\\-|\\-=|\\+=|\\^|\\&|\\|::|\\?|\\*"); + private static final Pattern PATTERN_GENERIC = Pattern.compile("<[a-zA-Z0-9,<>]+>"); + private static final Pattern PATTERN_TODO_COMMENT = + Pattern.compile("//\\s?(TODO|todo)\\s[^\n]*"); + private static final Pattern PATTERN_NUMBERS = Pattern.compile("\\b(\\d*[.]?\\d+)\\b"); + private static final Pattern PATTERN_CHAR = Pattern.compile("['](.*?)[']"); + private static final Pattern PATTERN_STRING = Pattern.compile("[\"](.*?)[\"]"); + private static final Pattern PATTERN_HEX = Pattern.compile("0x[0-9a-fA-F]+"); + + public static String getCommentStart() { + return "//"; + } + + public static String getCommentEnd() { + return ""; + } + + @Override + public Pattern getPattern(LanguageElement element) { + switch (element) { + case KEYWORD: + return Pattern.compile("\\b(" + String.join("|", getKeywords()) + ")\\b"); + case BUILTIN: + return PATTERN_BUILTINS; + case NUMBER: + return PATTERN_NUMBERS; + case CHAR: + return PATTERN_CHAR; + case STRING: + return PATTERN_STRING; + case HEX: + return PATTERN_HEX; + case SINGLE_LINE_COMMENT: + return PATTERN_SINGLE_LINE_COMMENT; + case MULTI_LINE_COMMENT: + return PATTERN_MULTI_LINE_COMMENT; + case ATTRIBUTE: + return PATTERN_ATTRIBUTE; + case OPERATION: + return PATTERN_OPERATION; + case TODO_COMMENT: + return PATTERN_TODO_COMMENT; + case GENERIC: + return PATTERN_GENERIC; + case ANNOTATION: + default: + return null; + } + } + + @Override + public String[] getKeywords() { + return new String[] { + "BASH_VERSION", + "BASH", + "PWD", + "OSTYPE", + "HOME", + "LANG", + "HOSTNAME", + "PATH", + "COLUMNS", + "USER", + "then", + "set", + "env", + "printenv", + "for", + "register", + "typedef", + "class", + "return", + "union", + "const", + "goto", + "short", + "unsigned", + "continue", + "if", + "fi", + "signed", + "virtual", + "default", + "inline", + "sizeof", + "delete", + "int", + "static", + "do", + "long", + "while", + "echo", + "alias", + "ps", + "ax", + "grep", + "do", + "done", + "exit", + "read" + }; + } + + @Override + public List getCodeList() { + List codeList = new ArrayList<>(); + String[] keywords = getKeywords(); + for (String keyword : keywords) { + codeList.add(new Keyword(keyword)); + } + return codeList; + } + + @Override + public String getName() { + return "sh"; + } + + @Override + public Set getIndentationStarts() { + Set characterSet = new HashSet<>(); + characterSet.add('{'); + return characterSet; + } + + @Override + public Set getIndentationEnds() { + Set characterSet = new HashSet<>(); + characterSet.add('}'); + return characterSet; + } +} diff --git a/app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/DLanguage.java b/app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/DLanguage.java new file mode 100644 index 00000000..7fa82d3b --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/DLanguage.java @@ -0,0 +1,162 @@ +package org.mian.gitnex.helpers.codeeditor.languages; + +import com.amrdeveloper.codeview.Code; +import com.amrdeveloper.codeview.Keyword; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * @author M M Arif + */ +public class DLanguage extends Language { + + private static final Pattern PATTERN_BUILTINS = Pattern.compile("[,:;[->]{}()]"); + private static final Pattern PATTERN_SINGLE_LINE_COMMENT = Pattern.compile("//[^\\n]*"); + private static final Pattern PATTERN_MULTI_LINE_COMMENT = + Pattern.compile("/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/"); + private static final Pattern PATTERN_ATTRIBUTE = Pattern.compile("\\.[a-zA-Z0-9_]+"); + private static final Pattern PATTERN_OPERATION = + Pattern.compile( + ":|==|>|<|!=|>=|<=|->|=|>|<|%|-|-=|%=|\\+|\\-|\\-=|\\+=|\\^|\\&|\\|::|\\?|\\*"); + private static final Pattern PATTERN_GENERIC = Pattern.compile("<[a-zA-Z0-9,<>]+>"); + private static final Pattern PATTERN_TODO_COMMENT = + Pattern.compile("//\\s?(TODO|todo)\\s[^\n]*"); + private static final Pattern PATTERN_NUMBERS = Pattern.compile("\\b(\\d*[.]?\\d+)\\b"); + private static final Pattern PATTERN_CHAR = Pattern.compile("['](.*?)[']"); + private static final Pattern PATTERN_STRING = Pattern.compile("[\"](.*?)[\"]"); + private static final Pattern PATTERN_HEX = Pattern.compile("0x[0-9a-fA-F]+"); + + public static String getCommentStart() { + return "//"; + } + + public static String getCommentEnd() { + return ""; + } + + @Override + public Pattern getPattern(LanguageElement element) { + switch (element) { + case KEYWORD: + return Pattern.compile("\\b(" + String.join("|", getKeywords()) + ")\\b"); + case BUILTIN: + return PATTERN_BUILTINS; + case NUMBER: + return PATTERN_NUMBERS; + case CHAR: + return PATTERN_CHAR; + case STRING: + return PATTERN_STRING; + case HEX: + return PATTERN_HEX; + case SINGLE_LINE_COMMENT: + return PATTERN_SINGLE_LINE_COMMENT; + case MULTI_LINE_COMMENT: + return PATTERN_MULTI_LINE_COMMENT; + case ATTRIBUTE: + return PATTERN_ATTRIBUTE; + case OPERATION: + return PATTERN_OPERATION; + case TODO_COMMENT: + return PATTERN_TODO_COMMENT; + case GENERIC: + return PATTERN_GENERIC; + case ANNOTATION: + default: + return null; + } + } + + @Override + public String[] getKeywords() { + return new String[] { + "new", + "switch", + "auto", + "else", + "operator", + "template", + "break", + "enum", + "private", + "this", + "case", + "extern", + "protected", + "throw", + "catch", + "float", + "public", + "try", + "char", + "for", + "register", + "typedef", + "class", + "friend", + "return", + "union", + "const", + "goto", + "short", + "unsigned", + "continue", + "if", + "signed", + "virtual", + "default", + "inline", + "sizeof", + "void", + "delete", + "int", + "static", + "volatile", + "do", + "long", + "struct", + "while", + "property", + "Log", + "alias", + "import", + "module", + "Singleton", + "string", + "bool", + "deprecated" + }; + } + + @Override + public List getCodeList() { + List codeList = new ArrayList<>(); + String[] keywords = getKeywords(); + for (String keyword : keywords) { + codeList.add(new Keyword(keyword)); + } + return codeList; + } + + @Override + public String getName() { + return "d"; + } + + @Override + public Set getIndentationStarts() { + Set characterSet = new HashSet<>(); + characterSet.add('{'); + return characterSet; + } + + @Override + public Set getIndentationEnds() { + Set characterSet = new HashSet<>(); + characterSet.add('}'); + return characterSet; + } +} diff --git a/app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/Language.java b/app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/Language.java index 4be2f00e..3888fbd0 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/Language.java +++ b/app/src/main/java/org/mian/gitnex/helpers/codeeditor/languages/Language.java @@ -35,7 +35,9 @@ public abstract class Language { new JsonLanguage(), new CppLanguage(), new CLanguage(), - new LispLanguage() + new LispLanguage(), + new DLanguage(), + new BashLanguage() }; for (Language l : languagesArray) { languages.put(l.getName().toUpperCase(), l); diff --git a/app/src/main/java/org/mian/gitnex/helpers/contexts/IssueContext.java b/app/src/main/java/org/mian/gitnex/helpers/contexts/IssueContext.java index 418f28bf..a8bed5a5 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/contexts/IssueContext.java +++ b/app/src/main/java/org/mian/gitnex/helpers/contexts/IssueContext.java @@ -19,6 +19,7 @@ public class IssueContext implements Serializable { private Issue issue; private PullRequest pullRequest; private boolean isSubscribed; + private boolean isPinned; private int issueIndex = 0; private String issueType; @@ -133,6 +134,16 @@ public class IssueContext implements Serializable { isSubscribed = subscribed; } + public boolean isPinned() { + + return isPinned; + } + + public void setPinned(boolean pinned) { + + isPinned = pinned; + } + public String getIssueType() { return issueType; diff --git a/app/src/main/res/drawable/ic_pin.xml b/app/src/main/res/drawable/ic_pin.xml index 07b1453a..3e9004a2 100644 --- a/app/src/main/res/drawable/ic_pin.xml +++ b/app/src/main/res/drawable/ic_pin.xml @@ -1,17 +1,24 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + + + + + diff --git a/app/src/main/res/layout/bottom_sheet_single_issue.xml b/app/src/main/res/layout/bottom_sheet_single_issue.xml index 4a87b262..f1e8fbe4 100644 --- a/app/src/main/res/layout/bottom_sheet_single_issue.xml +++ b/app/src/main/res/layout/bottom_sheet_single_issue.xml @@ -163,6 +163,33 @@ app:drawableTopCompat="@drawable/ic_tag" app:layout_alignSelf="flex_start" /> + + + + - + android:layout_height="wrap_content"> - + android:layout_height="wrap_content" + android:paddingStart="@dimen/dimen8dp" + android:paddingEnd="@dimen/dimen8dp" + android:paddingTop="@dimen/dimen8dp" + android:visibility="gone" + android:background="?attr/primaryBackgroundColor"> + android:layout_height="match_parent" /> - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/gitea_version.xml b/app/src/main/res/values/gitea_version.xml index 87e7d7ed..d6ffee88 100644 --- a/app/src/main/res/values/gitea_version.xml +++ b/app/src/main/res/values/gitea_version.xml @@ -2,6 +2,6 @@ 1.21 - 1.24 + 10.0.0 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0f60a63a..902264b3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -556,6 +556,8 @@ Title Description Bio + Pin + Unpin Explore users @@ -643,6 +645,11 @@ You have already Unsubscribed Un-Subscription failed + Issue pinned successfully + Issue unpinned successfully + Pinning issue failed + Unpinning issue failed + Close Milestone Open Milestone Milestone status updated successfully diff --git a/build.gradle b/build.gradle index 0257fcbd..0174c32a 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.1.2' + classpath 'com.android.tools.build:gradle:8.7.2' } } diff --git a/gradle.properties b/gradle.properties index 1547a776..ab35f618 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,6 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -android.defaults.buildfeatures.buildconfig=true android.enableJetifier=true android.nonFinalResIds=false android.nonTransitiveRClass=false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8ba31e99..8fcf7d8e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Jul 11 23:34:25 PKT 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists