From 7caecadb32e13482a3f4cba515b8ecf84c3f5ba8 Mon Sep 17 00:00:00 2001 From: M M Arif Date: Tue, 14 Apr 2020 20:55:04 +0000 Subject: [PATCH] Refactor issues (#380) new strings for tabs Merge branch 'refactor-issues' of gitea.com:gitnex/GitNex into refactor-issues Fix conflicts and files refactors Merge branch 'master' into refactor-issues Merge branch 'master' into refactor-issues refactored closed issues in new tab Added gitea ver check, minor ui fixes Merge branch 'master' into refactor-issues # Conflicts: # app/src/main/java/org/mian/gitnex/adapters/IssuesAdapter.java Refactored open issues Added parent fragment Co-authored-by: 6543 <6543@noreply.gitea.io> Reviewed-on: https://gitea.com/gitnex/GitNex/pulls/380 --- app/build.gradle | 2 +- .../gitnex/activities/CommitsActivity.java | 16 +- .../gitnex/activities/RepoDetailActivity.java | 32 +- .../gitnex/adapters/ClosedIssuesAdapter.java | 275 ------------- .../CommitsAdapter.java} | 25 +- .../mian/gitnex/adapters/IssuesAdapter.java | 384 ++++++++---------- .../gitnex/adapters/MyReposListAdapter.java | 2 +- .../gitnex/adapters/ReposListAdapter.java | 2 +- .../adapters/RepositoriesByOrgAdapter.java | 2 +- .../adapters/StarredReposListAdapter.java | 2 +- .../fragments/IssuesClosedFragment.java | 276 +++++++------ .../gitnex/fragments/IssuesMainFragment.java | 155 +++++++ .../gitnex/fragments/IssuesOpenFragment.java | 379 +++++++++-------- .../gitnex/fragments/ProfileFragment.java | 2 +- .../gitnex/helpers/StaticGlobalVariables.java | 18 + .../main/res/layout/activity_create_issue.xml | 20 +- app/src/main/res/layout/activity_main.xml | 2 +- .../main/res/layout/activity_repo_detail.xml | 8 +- app/src/main/res/layout/badge_issue.xml | 2 +- .../main/res/layout/fragment_issues_main.xml | 48 +++ app/src/main/res/layout/fragment_settings.xml | 8 +- app/src/main/res/layout/list_issues.xml | 2 +- ...appearance.xml => settings_appearance.xml} | 0 ...ngs_fileview.xml => settings_fileview.xml} | 0 ...s_languages.xml => settings_languages.xml} | 0 ...ngs_security.xml => settings_security.xml} | 0 app/src/main/res/values/strings.xml | 7 +- 27 files changed, 811 insertions(+), 858 deletions(-) delete mode 100644 app/src/main/java/org/mian/gitnex/adapters/ClosedIssuesAdapter.java rename app/src/main/java/org/mian/gitnex/{items/CommitsItems.java => adapters/CommitsAdapter.java} (82%) create mode 100644 app/src/main/java/org/mian/gitnex/fragments/IssuesMainFragment.java create mode 100644 app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java create mode 100644 app/src/main/res/layout/fragment_issues_main.xml rename app/src/main/res/layout/{layout_settings_appearance.xml => settings_appearance.xml} (100%) rename app/src/main/res/layout/{layout_settings_fileview.xml => settings_fileview.xml} (100%) rename app/src/main/res/layout/{layout_settings_languages.xml => settings_languages.xml} (100%) rename app/src/main/res/layout/{layout_settings_security.xml => settings_security.xml} (100%) diff --git a/app/build.gradle b/app/build.gradle index 08555654..8e002783 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,7 +29,7 @@ android { configurations { cleanedAnnotations - compile.exclude group: 'org.jetbrains' , module:'annotations' + compile.exclude group: 'org.jetbrains', module: 'annotations' } dependencies { diff --git a/app/src/main/java/org/mian/gitnex/activities/CommitsActivity.java b/app/src/main/java/org/mian/gitnex/activities/CommitsActivity.java index 60df3de6..e3d20068 100644 --- a/app/src/main/java/org/mian/gitnex/activities/CommitsActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/CommitsActivity.java @@ -27,7 +27,7 @@ import com.mikepenz.fastadapter_extensions.items.ProgressItem; import com.mikepenz.fastadapter_extensions.scroll.EndlessRecyclerOnScrollListener; import org.mian.gitnex.R; import org.mian.gitnex.clients.RetrofitClient; -import org.mian.gitnex.items.CommitsItems; +import org.mian.gitnex.adapters.CommitsAdapter; import org.mian.gitnex.models.Commits; import org.mian.gitnex.util.TinyDB; import java.util.ArrayList; @@ -42,7 +42,7 @@ import static com.mikepenz.fastadapter.adapters.ItemAdapter.items; * Author M M Arif */ -public class CommitsActivity extends BaseActivity implements ItemFilterListener { +public class CommitsActivity extends BaseActivity implements ItemFilterListener { private View.OnClickListener onClickListener; private TextView noData; @@ -52,8 +52,8 @@ public class CommitsActivity extends BaseActivity implements ItemFilterListener< private int resultLimit = 50; private boolean loadNextFlag = false; - private List items = new ArrayList<>(); - private FastItemAdapter fastItemAdapter; + private List items = new ArrayList<>(); + private FastItemAdapter fastItemAdapter; private ItemAdapter footerAdapter; private EndlessRecyclerOnScrollListener endlessRecyclerOnScrollListener; @@ -103,7 +103,7 @@ public class CommitsActivity extends BaseActivity implements ItemFilterListener< //noinspection unchecked fastItemAdapter.addAdapter(1, footerAdapter); - fastItemAdapter.getItemFilter().withFilterPredicate((IItemAdapter.Predicate) (item, constraint) -> item.getCommitTitle().toLowerCase().contains(Objects.requireNonNull(constraint).toString().toLowerCase())); + fastItemAdapter.getItemFilter().withFilterPredicate((IItemAdapter.Predicate) (item, constraint) -> item.getCommitTitle().toLowerCase().contains(Objects.requireNonNull(constraint).toString().toLowerCase())); fastItemAdapter.getItemFilter().withItemFilterListener(this); @@ -164,7 +164,7 @@ public class CommitsActivity extends BaseActivity implements ItemFilterListener< for (int i = 0; i < response.body().size(); i++) { - items.add(new CommitsItems(getApplicationContext()).withNewItems(response.body().get(i).getCommit().getMessage(), response.body().get(i).getHtml_url(), + items.add(new CommitsAdapter(getApplicationContext()).withNewItems(response.body().get(i).getCommit().getMessage(), response.body().get(i).getHtml_url(), response.body().get(i).getCommit().getCommitter().getName(), response.body().get(i).getCommit().getCommitter().getDate())); } @@ -229,7 +229,7 @@ public class CommitsActivity extends BaseActivity implements ItemFilterListener< for (int i = 0; i < response.body().size(); i++) { - fastItemAdapter.add(fastItemAdapter.getAdapterItemCount(), new CommitsItems(getApplicationContext()).withNewItems(response.body().get(i).getCommit().getMessage(), + fastItemAdapter.add(fastItemAdapter.getAdapterItemCount(), new CommitsAdapter(getApplicationContext()).withNewItems(response.body().get(i).getCommit().getMessage(), response.body().get(i).getHtml_url(), response.body().get(i).getCommit().getCommitter().getName(), response.body().get(i).getCommit().getCommitter().getDate())); @@ -304,7 +304,7 @@ public class CommitsActivity extends BaseActivity implements ItemFilterListener< } @Override - public void itemsFiltered(@Nullable CharSequence constraint, @Nullable List results) { + public void itemsFiltered(@Nullable CharSequence constraint, @Nullable List results) { endlessRecyclerOnScrollListener.disable(); } diff --git a/app/src/main/java/org/mian/gitnex/activities/RepoDetailActivity.java b/app/src/main/java/org/mian/gitnex/activities/RepoDetailActivity.java index abcc9754..e1d8b100 100644 --- a/app/src/main/java/org/mian/gitnex/activities/RepoDetailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/RepoDetailActivity.java @@ -27,10 +27,9 @@ import org.mian.gitnex.R; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.fragments.BottomSheetRepoFragment; import org.mian.gitnex.fragments.BranchesFragment; -import org.mian.gitnex.fragments.IssuesClosedFragment; import org.mian.gitnex.fragments.CollaboratorsFragment; import org.mian.gitnex.fragments.FilesFragment; -import org.mian.gitnex.fragments.IssuesOpenFragment; +import org.mian.gitnex.fragments.IssuesMainFragment; import org.mian.gitnex.fragments.LabelsFragment; import org.mian.gitnex.fragments.MilestonesFragment; import org.mian.gitnex.fragments.PullRequestsFragment; @@ -126,7 +125,7 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetRepoF } // only show Collaborators if you have permission to - final View collaboratorTab = vg.getChildAt(9); + final View collaboratorTab = vg.getChildAt(8); if (tinyDb.getBoolean("isRepoAdmin")) { collaboratorTab.setVisibility(View.VISIBLE); } @@ -166,8 +165,8 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetRepoF // pull count if (textViewBadgePull.getText() != "") { // only show if API returned a number - Objects.requireNonNull(tabLayout.getTabAt(4)).setCustomView(tabHeader4); - TabLayout.Tab tabOpenPulls = tabLayout.getTabAt(4); + Objects.requireNonNull(tabLayout.getTabAt(3)).setCustomView(tabHeader4); + TabLayout.Tab tabOpenPulls = tabLayout.getTabAt(3); assert tabOpenPulls != null; TextView openPullTabView = Objects.requireNonNull(tabOpenPulls.getCustomView()).findViewById(R.id.counterBadgePullText); openPullTabView.setTextColor(textColor); @@ -176,8 +175,8 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetRepoF // release count if (VersionCheck.compareVersion("1.11.5", tinyDb.getString("giteaVersion")) < 1) { if(textViewBadgeRelease.getText() != "") { // only show if API returned a number - Objects.requireNonNull(tabLayout.getTabAt(6)).setCustomView(tabHeader6); - TabLayout.Tab tabOpenRelease = tabLayout.getTabAt(6); + Objects.requireNonNull(tabLayout.getTabAt(5)).setCustomView(tabHeader6); + TabLayout.Tab tabOpenRelease = tabLayout.getTabAt(5); assert tabOpenRelease != null; TextView openReleaseTabView = Objects.requireNonNull(tabOpenRelease.getCustomView()).findViewById(R.id.counterBadgeReleaseText); openReleaseTabView.setTextColor(textColor); @@ -305,23 +304,20 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetRepoF case 1: // files return FilesFragment.newInstance(repoOwner, repoName); case 2: // issues - fragment = new IssuesOpenFragment(); + fragment = new IssuesMainFragment(); break; - case 3: // closed issues - fragment = new IssuesClosedFragment(); - break; - case 4: // pull requests + case 3: // pull requests fragment = new PullRequestsFragment(); break; - case 5: // branches + case 4: // branches return BranchesFragment.newInstance(repoOwner, repoName); - case 6: // releases + case 5: // releases return ReleasesFragment.newInstance(repoOwner, repoName); - case 7: // milestones + case 6: // milestones return MilestonesFragment.newInstance(repoOwner, repoName); - case 8: // labels + case 7: // labels return LabelsFragment.newInstance(repoOwner, repoName); - case 9: // collaborators + case 8: // collaborators return CollaboratorsFragment.newInstance(repoOwner, repoName); } assert fragment != null; @@ -330,7 +326,7 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetRepoF @Override public int getCount() { - return 10; + return 9; } } diff --git a/app/src/main/java/org/mian/gitnex/adapters/ClosedIssuesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ClosedIssuesAdapter.java deleted file mode 100644 index 7a9ea6ac..00000000 --- a/app/src/main/java/org/mian/gitnex/adapters/ClosedIssuesAdapter.java +++ /dev/null @@ -1,275 +0,0 @@ -package org.mian.gitnex.adapters; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Filter; -import android.widget.Filterable; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.TextView; -import org.mian.gitnex.R; -import org.mian.gitnex.activities.IssueDetailActivity; -import org.mian.gitnex.clients.PicassoService; -import org.mian.gitnex.helpers.ClickListener; -import org.mian.gitnex.helpers.RoundedTransformation; -import org.mian.gitnex.helpers.TimeHelper; -import org.mian.gitnex.models.Issues; -import org.mian.gitnex.util.TinyDB; -import org.ocpsoft.prettytime.PrettyTime; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -/** - * Author M M Arif - */ - -public class ClosedIssuesAdapter extends RecyclerView.Adapter implements Filterable { - - private Context context; - private final int TYPE_LOAD = 0; - private List issuesList; - private List issuesListFull; - private ClosedIssuesAdapter.OnLoadMoreListener loadMoreListener; - private boolean isLoading = false, isMoreDataAvailable = true; - - public ClosedIssuesAdapter(Context context, List issuesListMain) { - - this.context = context; - this.issuesList = issuesListMain; - issuesListFull = new ArrayList<>(issuesList); - - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - - LayoutInflater inflater = LayoutInflater.from(context); - - if(viewType == TYPE_LOAD){ - return new ClosedIssuesAdapter.IssuesHolder(inflater.inflate(R.layout.list_issues, parent,false)); - } - else { - return new ClosedIssuesAdapter.LoadHolder(inflater.inflate(R.layout.row_load,parent,false)); - } - - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { - - if(position >= getItemCount()-1 && isMoreDataAvailable && !isLoading && loadMoreListener!=null) { - - isLoading = true; - loadMoreListener.onLoadMore(); - - } - - if(getItemViewType(position) == TYPE_LOAD) { - - ((ClosedIssuesAdapter.IssuesHolder)holder).bindData(issuesList.get(position)); - - } - - } - - @Override - public int getItemViewType(int position) { - - if(issuesList.get(position).getTitle() != null) { - return TYPE_LOAD; - } - else { - return 1; - } - - } - - @Override - public int getItemCount() { - - return issuesList.size(); - - } - - class IssuesHolder extends RecyclerView.ViewHolder { - - private TextView issueNumber; - private ImageView issueAssigneeAvatar; - private TextView issueTitle; - private TextView issueCreatedTime; - private TextView issueCommentsCount; - private RelativeLayout relativeLayoutFrame; - - IssuesHolder(View itemView) { - - super(itemView); - - issueNumber = itemView.findViewById(R.id.issueNumber); - issueAssigneeAvatar = itemView.findViewById(R.id.assigneeAvatar); - issueTitle = itemView.findViewById(R.id.issueTitle); - issueCommentsCount = itemView.findViewById(R.id.issueCommentsCount); - LinearLayout frameCommentsCount = itemView.findViewById(R.id.frameCommentsCount); - issueCreatedTime = itemView.findViewById(R.id.issueCreatedTime); - relativeLayoutFrame = itemView.findViewById(R.id.relativeLayoutFrame); - - issueTitle.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - - Context context = v.getContext(); - //Log.i("issueNumber", issueNumber.getText().toString()); - - Intent intent = new Intent(context, IssueDetailActivity.class); - intent.putExtra("issueNumber", issueNumber.getText()); - - TinyDB tinyDb = new TinyDB(context); - tinyDb.putString("issueNumber", issueNumber.getText().toString()); - tinyDb.putString("issueType", "issue"); - context.startActivity(intent); - - } - }); - frameCommentsCount.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - - Context context = v.getContext(); - //Log.i("issueNumber", issueNumber.getText().toString()); - - Intent intent = new Intent(context, IssueDetailActivity.class); - intent.putExtra("issueNumber", issueNumber.getText()); - - TinyDB tinyDb = new TinyDB(context); - tinyDb.putString("issueNumber", issueNumber.getText().toString()); - tinyDb.putString("issueType", "issue"); - context.startActivity(intent); - - } - }); - - } - - @SuppressLint("SetTextI18n") - void bindData(Issues issuesModel){ - - final TinyDB tinyDb = new TinyDB(context); - final String locale = tinyDb.getString("locale"); - final String timeFormat = tinyDb.getString("dateFormat"); - - /*if(issuesModel.getPull_request() != null) { - if (issuesModel.getPull_request().isMerged()) { - relativeLayoutFrame.setVisibility(View.GONE); - relativeLayoutFrame.setLayoutParams(new RecyclerView.LayoutParams(0, 0)); - } - }*/ - - if (!issuesModel.getUser().getFull_name().equals("")) { - issueAssigneeAvatar.setOnClickListener(new ClickListener(context.getResources().getString(R.string.issueCreator) + issuesModel.getUser().getFull_name(), context)); - } else { - issueAssigneeAvatar.setOnClickListener(new ClickListener(context.getResources().getString(R.string.issueCreator) + issuesModel.getUser().getLogin(), context)); - } - - if (issuesModel.getUser().getAvatar_url() != null) { - PicassoService.getInstance(context).get().load(issuesModel.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar); - } else { - PicassoService.getInstance(context).get().load(issuesModel.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar); - } - - String issueNumber_ = "" + context.getResources().getString(R.string.hash) + issuesModel.getNumber() + ""; - issueTitle.setText(Html.fromHtml(issueNumber_ + " " + issuesModel.getTitle())); - - issueNumber.setText(String.valueOf(issuesModel.getNumber())); - issueCommentsCount.setText(String.valueOf(issuesModel.getComments())); - - issueCreatedTime.setText(TimeHelper.formatTime(issuesModel.getClosed_at(), new Locale(locale), timeFormat, context)); - - if(timeFormat.equals("pretty")) { - issueCreatedTime.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(issuesModel.getClosed_at()), context)); - } - - } - - } - - static class LoadHolder extends RecyclerView.ViewHolder { - - LoadHolder(View itemView) { - super(itemView); - } - - } - - public void setMoreDataAvailable(boolean moreDataAvailable) { - - isMoreDataAvailable = moreDataAvailable; - - } - - public void notifyDataChanged() { - - notifyDataSetChanged(); - isLoading = false; - - } - - public interface OnLoadMoreListener { - - void onLoadMore(); - - } - - public void setLoadMoreListener(ClosedIssuesAdapter.OnLoadMoreListener loadMoreListener) { - - this.loadMoreListener = loadMoreListener; - - } - - @Override - public Filter getFilter() { - return issuesFilter; - } - - private Filter issuesFilter = new Filter() { - @Override - protected FilterResults performFiltering(CharSequence constraint) { - List filteredList = new ArrayList<>(); - - if (constraint == null || constraint.length() == 0) { - filteredList.addAll(issuesList); - } else { - String filterPattern = constraint.toString().toLowerCase().trim(); - - for (Issues item : issuesList) { - if (item.getTitle().toLowerCase().contains(filterPattern) || item.getBody().toLowerCase().contains(filterPattern)) { - filteredList.add(item); - } - } - } - - FilterResults results = new FilterResults(); - results.values = filteredList; - - return results; - } - - @Override - protected void publishResults(CharSequence constraint, FilterResults results) { - issuesList.clear(); - issuesList.addAll((List) results.values); - notifyDataSetChanged(); - } - }; - -} diff --git a/app/src/main/java/org/mian/gitnex/items/CommitsItems.java b/app/src/main/java/org/mian/gitnex/adapters/CommitsAdapter.java similarity index 82% rename from app/src/main/java/org/mian/gitnex/items/CommitsItems.java rename to app/src/main/java/org/mian/gitnex/adapters/CommitsAdapter.java index 8c3b03ef..b49d4ea9 100644 --- a/app/src/main/java/org/mian/gitnex/items/CommitsItems.java +++ b/app/src/main/java/org/mian/gitnex/adapters/CommitsAdapter.java @@ -1,4 +1,4 @@ -package org.mian.gitnex.items; +package org.mian.gitnex.adapters; import android.content.Context; import android.text.Html; @@ -12,9 +12,6 @@ import org.mian.gitnex.R; import org.mian.gitnex.helpers.ClickListener; import org.mian.gitnex.helpers.TimeHelper; import org.mian.gitnex.util.TinyDB; -import org.ocpsoft.prettytime.PrettyTime; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Locale; @@ -23,7 +20,7 @@ import java.util.Locale; * Author M M Arif */ -public class CommitsItems extends AbstractItem { +public class CommitsAdapter extends AbstractItem { final private Context ctx; private String commitTitle; @@ -33,11 +30,11 @@ public class CommitsItems extends AbstractItem { + public class ViewHolder extends FastAdapter.ViewHolder { final TinyDB tinyDb = new TinyDB(ctx); final String locale = tinyDb.getString("locale"); @@ -133,7 +130,7 @@ public class CommitsItems extends AbstractItem payloads) { + public void bindView(CommitsAdapter item, @NonNull List payloads) { commitTitleVw.setText(item.getCommitTitle()); commitCommitterVw.setText(ctx.getString(R.string.commitCommittedBy, item.getcommitCommitter())); @@ -150,7 +147,7 @@ public class CommitsItems extends AbstractItem implements Filterable { +public class IssuesAdapter extends AbstractItem { + + final private Context ctx; + private String issueTitle; + private int issueNumber; + private String issueAssigneeAvatar; + private Date issueCreatedTime; + private int issueCommentsCount; + private String userFullname; + private String userLogin; + + private boolean isSelectable = true; + + public IssuesAdapter(Context ctx) { + this.ctx = ctx; + } - private Context context; - private final int TYPE_LOAD = 0; - private List issuesList; - private List issuesListFull; - private OnLoadMoreListener loadMoreListener; - private boolean isLoading = false, isMoreDataAvailable = true; + public IssuesAdapter withNewItems(String issueTitle, int issueNumber, String issueAssigneeAvatar, Date issueCreatedTime, int issueCommentsCount, String userFullname, String userLogin) { - public IssuesAdapter(Context context, List issuesListMain) { + this.setNewItems(issueTitle, issueNumber, issueAssigneeAvatar, issueCreatedTime, issueCommentsCount, userFullname, userLogin); + return this; + + } + + private void setNewItems(String issueTitle, int issueNumber, String issueAssigneeAvatar, Date issueCreatedTime, int issueCommentsCount, String userFullname, String userLogin) { - this.context = context; - this.issuesList = issuesListMain; - issuesListFull = new ArrayList<>(issuesList); + this.issueTitle = issueTitle; + this.issueNumber = issueNumber; + this.issueAssigneeAvatar = issueAssigneeAvatar; + this.issueCreatedTime = issueCreatedTime; + this.issueCommentsCount = issueCommentsCount; + this.userFullname = userFullname; + this.userLogin = userLogin; + + } + + private int getIssueNumber() { + return issueNumber; + } - } + public String getIssueTitle() { + return issueTitle; + } - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + private String getIssueAssigneeAvatar() { + return issueAssigneeAvatar; + } - LayoutInflater inflater = LayoutInflater.from(context); + private Date getIssueCreatedTime() { + return issueCreatedTime; + } + + private int getIssueCommentsCount() { + return issueCommentsCount; + } + + private String getUserFullname() { + return userFullname; + } + + private String getUserLogin() { + return userLogin; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public IssuesAdapter withEnabled(boolean enabled) { + return null; + } + + @Override + public boolean isSelectable() { + return isSelectable; + } + + @Override + public IssuesAdapter withSelectable(boolean selectable) { + this.isSelectable = selectable; + return this; + } + + @Override + public int getType() { + return R.id.relativeLayoutFrameIssuesList; + } - if(viewType == TYPE_LOAD){ - return new IssuesHolder(inflater.inflate(R.layout.list_issues, parent,false)); - } - else { - return new LoadHolder(inflater.inflate(R.layout.row_load,parent,false)); - } + @Override + public int getLayoutRes() { + return R.layout.list_issues; + } - } + @NonNull + @Override + public IssuesAdapter.ViewHolder getViewHolder(@NonNull View v) { + return new IssuesAdapter.ViewHolder(v); + } - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + public class ViewHolder extends FastAdapter.ViewHolder { - if(position >= getItemCount()-1 && isMoreDataAvailable && !isLoading && loadMoreListener!=null) { + final TinyDB tinyDb = new TinyDB(ctx); + final String locale = tinyDb.getString("locale"); + final String timeFormat = tinyDb.getString("dateFormat"); - isLoading = true; - loadMoreListener.onLoadMore(); + private TextView issueNumber; + private ImageView issueAssigneeAvatar; + private TextView issueTitle; + private TextView issueCreatedTime; + private TextView issueCommentsCount; - } + public ViewHolder(View itemView) { - if(getItemViewType(position) == TYPE_LOAD) { + super(itemView); - ((IssuesHolder)holder).bindData(issuesList.get(position)); + issueNumber = itemView.findViewById(R.id.issueNumber); + issueAssigneeAvatar = itemView.findViewById(R.id.assigneeAvatar); + issueTitle = itemView.findViewById(R.id.issueTitle); + issueCommentsCount = itemView.findViewById(R.id.issueCommentsCount); + issueCreatedTime = itemView.findViewById(R.id.issueCreatedTime); - } + } - } + @Override + public void bindView(@NonNull IssuesAdapter item, @NonNull List payloads) { - @Override - public int getItemViewType(int position) { + if (!item.getUserFullname().equals("")) { + issueAssigneeAvatar.setOnClickListener(new ClickListener(ctx.getResources().getString(R.string.issueCreator) + item.getUserFullname(), ctx)); + } + else { + issueAssigneeAvatar.setOnClickListener(new ClickListener(ctx.getResources().getString(R.string.issueCreator) + item.getUserLogin(), ctx)); + } - if(issuesList.get(position).getTitle() != null) { - return TYPE_LOAD; - } - else { - return 1; - } + PicassoService.getInstance(ctx).get().load(item.getIssueAssigneeAvatar()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar); - } + String issueNumber_ = "" + ctx.getResources().getString(R.string.hash) + item.getIssueNumber() + ""; + issueTitle.setText(Html.fromHtml(issueNumber_ + " " + item.getIssueTitle())); - @Override - public int getItemCount() { + issueNumber.setText(String.valueOf(item.getIssueNumber())); + issueCommentsCount.setText(String.valueOf(item.getIssueCommentsCount())); - return issuesList.size(); + switch (timeFormat) { - } + case "pretty": { + PrettyTime prettyTime = new PrettyTime(new Locale(locale)); + String createdTime = prettyTime.format(item.getIssueCreatedTime()); + issueCreatedTime.setText(createdTime); + issueCreatedTime.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(item.getIssueCreatedTime()), ctx)); + break; + } + case "normal": { + DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd '" + ctx.getResources().getString(R.string.timeAtText) + "' HH:mm", new Locale(locale)); + String createdTime = formatter.format(item.getIssueCreatedTime()); + issueCreatedTime.setText(createdTime); + break; + } + case "normal1": { + DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy '" + ctx.getResources().getString(R.string.timeAtText) + "' HH:mm", new Locale(locale)); + String createdTime = formatter.format(item.getIssueCreatedTime()); + issueCreatedTime.setText(createdTime); + break; + } - class IssuesHolder extends RecyclerView.ViewHolder { + } - private TextView issueNumber; - private ImageView issueAssigneeAvatar; - private TextView issueTitle; - private TextView issueCreatedTime; - private TextView issueCommentsCount; - private RelativeLayout relativeLayoutFrame; + } - IssuesHolder(View itemView) { + @Override + public void unbindView(@NonNull IssuesAdapter item) { - super(itemView); + issueTitle.setText(null); + issueCommentsCount.setText(null); + issueCreatedTime.setText(null); - issueNumber = itemView.findViewById(R.id.issueNumber); - issueAssigneeAvatar = itemView.findViewById(R.id.assigneeAvatar); - issueTitle = itemView.findViewById(R.id.issueTitle); - issueCommentsCount = itemView.findViewById(R.id.issueCommentsCount); - LinearLayout frameCommentsCount = itemView.findViewById(R.id.frameCommentsCount); - issueCreatedTime = itemView.findViewById(R.id.issueCreatedTime); - relativeLayoutFrame = itemView.findViewById(R.id.relativeLayoutFrame); + } - issueTitle.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { + } - Context context = v.getContext(); - //Log.i("issueNumber", issueNumber.getText().toString()); + public static class IssueTitleClickEvent extends ClickEventHook { - Intent intent = new Intent(context, IssueDetailActivity.class); - intent.putExtra("issueNumber", issueNumber.getText()); + @Nullable + @Override + public List onBindMany(@NonNull RecyclerView.ViewHolder viewHolder) { - TinyDB tinyDb = new TinyDB(context); - tinyDb.putString("issueNumber", issueNumber.getText().toString()); - tinyDb.putString("issueType", "issue"); - context.startActivity(intent); + if (viewHolder instanceof IssuesAdapter.ViewHolder) { + return EventHookUtil.toList(((ViewHolder) viewHolder).issueTitle); + } - } - }); - frameCommentsCount.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { + return super.onBindMany(viewHolder); - Context context = v.getContext(); - //Log.i("issueNumber", issueNumber.getText().toString()); + } - Intent intent = new Intent(context, IssueDetailActivity.class); - intent.putExtra("issueNumber", issueNumber.getText()); + @Override + public void onClick(View v, int position, @NonNull FastAdapter fastAdapter, IssuesAdapter item) { - TinyDB tinyDb = new TinyDB(context); - tinyDb.putString("issueNumber", issueNumber.getText().toString()); - tinyDb.putString("issueType", "issue"); - context.startActivity(intent); + Context context = v.getContext(); - } - }); + Intent intent = new Intent(context, IssueDetailActivity.class); + intent.putExtra("issueNumber", item.getIssueNumber()); - } + TinyDB tinyDb = new TinyDB(context); + tinyDb.putString("issueNumber", String.valueOf(item.getIssueNumber())); + tinyDb.putString("issueType", "issue"); + context.startActivity(intent); - @SuppressLint("SetTextI18n") - void bindData(Issues issuesModel){ + } - final TinyDB tinyDb = new TinyDB(context); - final String locale = tinyDb.getString("locale"); - final String timeFormat = tinyDb.getString("dateFormat"); - - /*if(issuesModel.getPull_request() != null) { - if (!issuesModel.getPull_request().isMerged()) { - relativeLayoutFrame.setVisibility(View.GONE); - relativeLayoutFrame.setLayoutParams(new RecyclerView.LayoutParams(0, 0)); - } - }*/ - - if (!issuesModel.getUser().getFull_name().equals("")) { - issueAssigneeAvatar.setOnClickListener(new ClickListener(context.getResources().getString(R.string.issueCreator) + issuesModel.getUser().getFull_name(), context)); - } else { - issueAssigneeAvatar.setOnClickListener(new ClickListener(context.getResources().getString(R.string.issueCreator) + issuesModel.getUser().getLogin(), context)); - } - - PicassoService.getInstance(context).get().load(issuesModel.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar); - - String issueNumber_ = "" + context.getResources().getString(R.string.hash) + issuesModel.getNumber() + ""; - issueTitle.setText(Html.fromHtml(issueNumber_ + " " + issuesModel.getTitle())); - - issueNumber.setText(String.valueOf(issuesModel.getNumber())); - issueCommentsCount.setText(String.valueOf(issuesModel.getComments())); - - issueCreatedTime.setText(TimeHelper.formatTime(issuesModel.getCreated_at(), new Locale(locale), timeFormat, context)); - - if(timeFormat.equals("pretty")) { - issueCreatedTime.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(issuesModel.getCreated_at()), context)); - } - - } - - } - - static class LoadHolder extends RecyclerView.ViewHolder { - - LoadHolder(View itemView) { - super(itemView); - } - - } - - public void setMoreDataAvailable(boolean moreDataAvailable) { - - isMoreDataAvailable = moreDataAvailable; - - } - - public void notifyDataChanged() { - - notifyDataSetChanged(); - isLoading = false; - - } - - public interface OnLoadMoreListener { - - void onLoadMore(); - - } - - public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) { - - this.loadMoreListener = loadMoreListener; - - } - - @Override - public Filter getFilter() { - return issuesFilter; - } - - private Filter issuesFilter = new Filter() { - @Override - protected FilterResults performFiltering(CharSequence constraint) { - List filteredList = new ArrayList<>(); - - if (constraint == null || constraint.length() == 0) { - filteredList.addAll(issuesList); - } else { - String filterPattern = constraint.toString().toLowerCase().trim(); - - for (Issues item : issuesList) { - if (item.getTitle().toLowerCase().contains(filterPattern) || item.getBody().toLowerCase().contains(filterPattern)) { - filteredList.add(item); - } - } - } - - FilterResults results = new FilterResults(); - results.values = filteredList; - - return results; - } - - @Override - protected void publishResults(CharSequence constraint, FilterResults results) { - issuesList.clear(); - issuesList.addAll((List) results.values); - notifyDataSetChanged(); - } - }; + } } diff --git a/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java index 5f750cb9..1c5c7e1e 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java @@ -78,7 +78,7 @@ public class MyReposListAdapter extends RecyclerView.Adapter { + private Context ctx; private ProgressBar mProgressBarClosed; - private RecyclerView recyclerViewClosed; - private List issuesListClosed; - private ClosedIssuesAdapter adapterClosed; - private ApiInterface apiClosed; - private String TAG = "closedIssuesListFragment - "; - private Context context; - private int pageSize = 1; + private boolean loadNextFlag = false; + private String TAG = StaticGlobalVariables.tagIssuesListClosed; private TextView noDataIssuesClosed; - private String issueState = "closed"; - private int resultLimit = 50; - private String requestType = "issues" ; + private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; + private String requestType = StaticGlobalVariables.issuesRequestType; + private String issueState = StaticGlobalVariables.issueStateClosed; + + private List items = new ArrayList<>(); + private FastItemAdapter fastItemAdapter; + private ItemAdapter footerAdapter; + private EndlessRecyclerOnScrollListener endlessRecyclerOnScrollListener; @Nullable @Override @@ -62,71 +68,69 @@ public class IssuesClosedFragment extends Fragment { setHasOptionsMenu(true); TinyDB tinyDb = new TinyDB(getContext()); - String repoFullName = tinyDb.getString("repoFullName"); - //Log.i("repoFullName", tinyDb.getString("repoFullName")); - String[] parts = repoFullName.split("/"); - final String repoOwner = parts[0]; - final String repoName = parts[1]; final String instanceUrl = tinyDb.getString("instanceUrl"); final String loginUid = tinyDb.getString("loginUid"); final String instanceToken = "token " + tinyDb.getString(loginUid + "-token"); + String repoFullName = tinyDb.getString("repoFullName"); + String[] parts = repoFullName.split("/"); + final String repoOwner = parts[0]; + final String repoName = parts[1]; - final SwipeRefreshLayout swipeRefresh = v.findViewById(R.id.pullToRefreshClosed); + if (VersionCheck.compareVersion("1.12.0", tinyDb.getString("giteaVersion")) < 1) { + resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + } - context = getContext(); - recyclerViewClosed = v.findViewById(R.id.recyclerViewClosed); - issuesListClosed = new ArrayList<>(); - - mProgressBarClosed = v.findViewById(R.id.progress_barClosed); noDataIssuesClosed = v.findViewById(R.id.noDataIssuesClosed); + mProgressBarClosed = v.findViewById(R.id.progress_barClosed); + final SwipeRefreshLayout swipeRefreshLayout = v.findViewById(R.id.pullToRefreshClosed); + + RecyclerView recyclerView = v.findViewById(R.id.recyclerViewClosed); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + recyclerView.setHasFixedSize(true); + + fastItemAdapter = new FastItemAdapter<>(); + fastItemAdapter.withSelectable(true); + + footerAdapter = items(); + //noinspection unchecked + fastItemAdapter.addAdapter(StaticGlobalVariables.issuesPageInit, footerAdapter); + + fastItemAdapter.getItemFilter().withFilterPredicate((IItemAdapter.Predicate) (item, constraint) -> item.getIssueTitle().toLowerCase().contains(constraint.toString().toLowerCase())); + + fastItemAdapter.getItemFilter().withItemFilterListener(this); + + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + recyclerView.setItemAnimator(new DefaultItemAnimator()); + recyclerView.setAdapter(fastItemAdapter); + + endlessRecyclerOnScrollListener = new EndlessRecyclerOnScrollListener(footerAdapter) { - swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override - public void onRefresh() { - new Handler().postDelayed(new Runnable() { - @Override - public void run() { + public void onLoadMore(final int currentPage) { - swipeRefresh.setRefreshing(false); - loadInitial(instanceToken, repoOwner, repoName, issueState, resultLimit, requestType); - adapterClosed.notifyDataChanged(); - - } - }, 200); - } - }); - - adapterClosed = new ClosedIssuesAdapter(getContext(), issuesListClosed); - adapterClosed.setLoadMoreListener(new ClosedIssuesAdapter.OnLoadMoreListener() { - @Override - public void onLoadMore() { - - recyclerViewClosed.post(new Runnable() { - @Override - public void run() { - if(issuesListClosed.size() == 10 || pageSize == 10) { - - int page = (issuesListClosed.size() + 10) / 10; - loadMore(Authorization.returnAuthentication(getContext(), loginUid, instanceToken), repoOwner, repoName, page, issueState, resultLimit, requestType); - - } - /*else { - - Toasty.info(context, getString(R.string.noMoreData)); - - }*/ - } - }); + loadNext(instanceUrl, instanceToken, repoOwner, repoName, resultLimit, issueState, requestType, currentPage); } + + }; + + swipeRefreshLayout.setOnRefreshListener(() -> { + + mProgressBarClosed.setVisibility(View.VISIBLE); + fastItemAdapter.clear(); + endlessRecyclerOnScrollListener.resetPageCount(); + swipeRefreshLayout.setRefreshing(false); + }); - recyclerViewClosed.setHasFixedSize(true); - recyclerViewClosed.setLayoutManager(new LinearLayoutManager(context)); - recyclerViewClosed.setAdapter(adapterClosed); + recyclerView.addOnScrollListener(endlessRecyclerOnScrollListener); - apiClosed = IssuesService.createService(ApiInterface.class, instanceUrl, getContext()); - loadInitial(Authorization.returnAuthentication(getContext(), loginUid, instanceToken), repoOwner, repoName, issueState, resultLimit, requestType); + loadInitial(instanceUrl, instanceToken, repoOwner, repoName, issueState, resultLimit, requestType); + + fastItemAdapter.withEventHook(new IssuesAdapter.IssueTitleClickEvent()); + + assert savedInstanceState != null; + fastItemAdapter.withSavedInstanceState(savedInstanceState); return v; @@ -137,25 +141,21 @@ public class IssuesClosedFragment extends Fragment { super.onResume(); TinyDB tinyDb = new TinyDB(getContext()); - final String loginUid = tinyDb.getString("loginUid"); - String repoFullName = tinyDb.getString("repoFullName"); - String[] parts = repoFullName.split("/"); - final String repoOwner = parts[0]; - final String repoName = parts[1]; - final String instanceToken = "token " + tinyDb.getString(loginUid + "-token"); if(tinyDb.getBoolean("resumeClosedIssues")) { - loadInitial(Authorization.returnAuthentication(getContext(), loginUid, instanceToken), repoOwner, repoName, issueState, resultLimit, requestType); + mProgressBarClosed.setVisibility(View.VISIBLE); + fastItemAdapter.clear(); + endlessRecyclerOnScrollListener.resetPageCount(); tinyDb.putBoolean("resumeClosedIssues", false); } } - private void loadInitial(String token, String repoOwner, String repoName, String issueState, int resultLimit, String requestType) { + private void loadInitial(String instanceUrl, String token, String repoOwner, String repoName, String issueState, int resultLimit, String requestType) { - Call> call = apiClosed.getClosedIssues(token, repoOwner, repoName, 1, issueState, resultLimit, requestType); + Call> call = RetrofitClient.getInstance(instanceUrl, getContext()).getApiInterface().getClosedIssues(token, repoOwner, repoName, 1, issueState, resultLimit, requestType); call.enqueue(new Callback>() { @@ -167,27 +167,34 @@ public class IssuesClosedFragment extends Fragment { assert response.body() != null; if(response.body().size() > 0) { - issuesListClosed.clear(); - issuesListClosed.addAll(response.body()); - adapterClosed.notifyDataChanged(); + if(response.body().size() == resultLimit) { + loadNextFlag = true; + } + + for(int i = 0; i < response.body().size(); i++) { + items.add(new IssuesAdapter(getContext()).withNewItems(response.body().get(i).getTitle(), response.body().get(i).getNumber(), response.body().get(i).getUser().getAvatar_url(), response.body().get(i).getCreated_at(), response.body().get(i).getComments(), response.body().get(i).getUser().getFull_name(), response.body().get(i).getUser().getLogin())); + } + + fastItemAdapter.add(items); noDataIssuesClosed.setVisibility(View.GONE); } else { - issuesListClosed.clear(); - adapterClosed.notifyDataChanged(); noDataIssuesClosed.setVisibility(View.VISIBLE); } + mProgressBarClosed.setVisibility(View.GONE); + } else { - Log.e(TAG, String.valueOf(response.code())); + Log.i(TAG, String.valueOf(response.code())); } } @Override public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + Log.e(TAG, t.toString()); } @@ -195,95 +202,110 @@ public class IssuesClosedFragment extends Fragment { } - private void loadMore(String token, String repoOwner, String repoName, int page, String issueState, int resultLimit, String requestType){ + private void loadNext(String instanceUrl, String token, String repoOwner, String repoName, int resultLimit, String issueState, String requestType, final int currentPage) { - //add loading progress view - issuesListClosed.add(new Issues("load")); - adapterClosed.notifyItemInserted((issuesListClosed.size() - 1)); + footerAdapter.clear(); + //noinspection unchecked + footerAdapter.add(new ProgressItem().withEnabled(false)); + Handler handler = new Handler(); - Call> call = apiClosed.getClosedIssues(token, repoOwner, repoName, page, issueState, resultLimit, requestType); + handler.postDelayed(() -> { - call.enqueue(new Callback>() { + Call> call = RetrofitClient.getInstance(instanceUrl, getContext()).getApiInterface().getClosedIssues(token, repoOwner, repoName, currentPage + 1, issueState, resultLimit, requestType); - @Override - public void onResponse(@NonNull Call> call, @NonNull Response> response) { + call.enqueue(new Callback>() { - if(response.isSuccessful()){ + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { - //remove loading view - issuesListClosed.remove(issuesListClosed.size()-1); + if(response.isSuccessful()) { - List result = response.body(); + assert response.body() != null; - assert result != null; - if(result.size() > 0) { + if(response.body().size() > 0) { - pageSize = result.size(); - issuesListClosed.addAll(result); + loadNextFlag = response.body().size() == resultLimit; + + for(int i = 0; i < response.body().size(); i++) { + + fastItemAdapter.add(fastItemAdapter.getAdapterItemCount(), new IssuesAdapter(getContext()).withNewItems(response.body().get(i).getTitle(), response.body().get(i).getNumber(), response.body().get(i).getUser().getAvatar_url(), response.body().get(i).getCreated_at(), response.body().get(i).getComments(), response.body().get(i).getUser().getFull_name(), response.body().get(i).getUser().getLogin())); + + } + + footerAdapter.clear(); + mProgressBarClosed.setVisibility(View.GONE); + + } + else { + footerAdapter.clear(); + } + + mProgressBarClosed.setVisibility(View.GONE); } else { - - Toasty.info(context, getString(R.string.noMoreData)); - adapterClosed.setMoreDataAvailable(false); - + Log.i(TAG, String.valueOf(response.code())); } - adapterClosed.notifyDataChanged(); - - } - else { - - Log.e(TAG, String.valueOf(response.code())); - } - } + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + Log.i(TAG, t.toString()); + } - Log.e(TAG, t.toString()); + }); - } + }, 1000); + + if(!loadNextFlag) { + footerAdapter.clear(); + } - }); } @Override public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - boolean connToInternet = AppUtil.haveNetworkConnection(Objects.requireNonNull(getContext())); - inflater.inflate(R.menu.search_menu, menu); super.onCreateOptionsMenu(menu, inflater); MenuItem searchItem = menu.findItem(R.id.action_search); androidx.appcompat.widget.SearchView searchView = (androidx.appcompat.widget.SearchView) searchItem.getActionView(); searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); - //searchView.setQueryHint(getContext().getString(R.string.strFilter)); - - /*if(!connToInternet) { - return; - }*/ searchView.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { + return false; } @Override public boolean onQueryTextChange(String newText) { - adapterClosed.getFilter().filter(newText); - return false; - + fastItemAdapter.filter(newText); + return true; } }); + endlessRecyclerOnScrollListener.enable(); + + } + + @Override + public void itemsFiltered(@Nullable CharSequence constraint, @Nullable List results) { + + endlessRecyclerOnScrollListener.disable(); + } + + @Override + public void onReset() { + + endlessRecyclerOnScrollListener.enable(); } } diff --git a/app/src/main/java/org/mian/gitnex/fragments/IssuesMainFragment.java b/app/src/main/java/org/mian/gitnex/fragments/IssuesMainFragment.java new file mode 100644 index 00000000..0391e5d7 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/IssuesMainFragment.java @@ -0,0 +1,155 @@ +package org.mian.gitnex.fragments; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.ViewPager; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import com.google.android.material.tabs.TabLayout; +import org.mian.gitnex.R; +import org.mian.gitnex.util.TinyDB; +import java.util.Objects; + +/** + * Author M M Arif + */ + +public class IssuesMainFragment extends Fragment { + + private Context ctx; + + public IssuesMainFragment() { + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + View v = inflater.inflate(R.layout.fragment_issues_main, container, false); + setHasOptionsMenu(true); + + TinyDB tinyDb = new TinyDB(getContext()); + + SectionsPagerAdapter mSectionsPagerAdapter = new IssuesMainFragment.SectionsPagerAdapter(getChildFragmentManager()); + + ViewPager mViewPager = v.findViewById(R.id.issuesContainer); + mViewPager.setAdapter(mSectionsPagerAdapter); + + Typeface myTypeface; + if(tinyDb.getInt("customFontId") == 0) { + + myTypeface = Typeface.createFromAsset(Objects.requireNonNull(getContext()).getAssets(), "fonts/roboto.ttf"); + + } + else if (tinyDb.getInt("customFontId") == 1) { + + myTypeface = Typeface.createFromAsset(Objects.requireNonNull(getContext()).getAssets(), "fonts/manroperegular.ttf"); + + } + else if (tinyDb.getInt("customFontId") == 2) { + + myTypeface = Typeface.createFromAsset(Objects.requireNonNull(getContext()).getAssets(), "fonts/sourcecodeproregular.ttf"); + + } + else { + + myTypeface = Typeface.createFromAsset(Objects.requireNonNull(getContext()).getAssets(), "fonts/roboto.ttf"); + + } + + TabLayout tabLayout = v.findViewById(R.id.tabs); + + ViewGroup vg = (ViewGroup) tabLayout.getChildAt(0); + int tabsCount = vg.getChildCount(); + + for (int j = 0; j < tabsCount; j++) { + + ViewGroup vgTab = (ViewGroup) vg.getChildAt(j); + int tabChildCount = vgTab.getChildCount(); + + for (int i = 0; i < tabChildCount; i++) { + + View tabViewChild = vgTab.getChildAt(i); + if (tabViewChild instanceof TextView) { + ((TextView) tabViewChild).setTypeface(myTypeface); + } + + } + + } + + mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); + tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager)); + + return v; + + } + + public static class SectionsPagerAdapter extends FragmentStatePagerAdapter { + + SectionsPagerAdapter(FragmentManager fm) { + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + } + + @NonNull + @Override + public Fragment getItem(int position) { + + Fragment fragment = null; + switch (position) { + case 0: // open issues + fragment = new IssuesOpenFragment(); + break; + case 1: // closed issues + fragment = new IssuesClosedFragment(); + break; + } + assert fragment != null; + return fragment; + } + + @Override + public int getCount() { + return 2; + } + + } + + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + + menu.clear(); + Objects.requireNonNull(getActivity()).getMenuInflater().inflate(R.menu.repo_dotted_menu, menu); + super.onCreateOptionsMenu(menu, inflater); + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + int id = item.getItemId(); + + switch (id) { + case android.R.id.home: + return true; + case R.id.repoMenu: + BottomSheetRepoFragment bottomSheet = new BottomSheetRepoFragment(); + bottomSheet.show(getChildFragmentManager(), "repoBottomSheet"); + return true; + default: + return super.onOptionsItemSelected(item); + } + + } + +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/IssuesOpenFragment.java b/app/src/main/java/org/mian/gitnex/fragments/IssuesOpenFragment.java index 8a1f2727..902b6bdc 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/IssuesOpenFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/IssuesOpenFragment.java @@ -1,10 +1,10 @@ package org.mian.gitnex.fragments; -import android.content.Context; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -22,266 +22,287 @@ import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.ProgressBar; import android.widget.TextView; +import com.mikepenz.fastadapter.IItemAdapter; +import com.mikepenz.fastadapter.adapters.ItemAdapter; +import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter; +import com.mikepenz.fastadapter.listeners.ItemFilterListener; +import com.mikepenz.fastadapter_extensions.items.ProgressItem; +import com.mikepenz.fastadapter_extensions.scroll.EndlessRecyclerOnScrollListener; import org.mian.gitnex.R; +import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.VersionCheck; import org.mian.gitnex.adapters.IssuesAdapter; -import org.mian.gitnex.clients.IssuesService; -import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.Toasty; -import org.mian.gitnex.interfaces.ApiInterface; import org.mian.gitnex.models.Issues; -import org.mian.gitnex.util.AppUtil; import org.mian.gitnex.util.TinyDB; import java.util.ArrayList; import java.util.List; -import java.util.Objects; +import static com.mikepenz.fastadapter.adapters.ItemAdapter.items; /** * Author M M Arif */ -public class IssuesOpenFragment extends Fragment { - - private ProgressBar mProgressBar; - private RecyclerView recyclerView; - private List issuesList; - private IssuesAdapter adapter; - private ApiInterface api; - private String TAG = "IssuesListFragment - "; - private Context context; - private int pageSize = 1; - private TextView noDataIssues; - private int resultLimit = 50; - private String requestType = "issues"; +public class IssuesOpenFragment extends Fragment implements ItemFilterListener { - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + private ProgressBar mProgressBar; + private boolean loadNextFlag = false; + private String TAG = StaticGlobalVariables.tagIssuesListOpen; + private TextView noDataIssues; + private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; + private String requestType = StaticGlobalVariables.issuesRequestType; - final View v = inflater.inflate(R.layout.fragment_issues, container, false); - setHasOptionsMenu(true); + private List items = new ArrayList<>(); + private FastItemAdapter fastItemAdapter; + private ItemAdapter footerAdapter; + private EndlessRecyclerOnScrollListener endlessRecyclerOnScrollListener; - TinyDB tinyDb = new TinyDB(getContext()); - String repoFullName = tinyDb.getString("repoFullName"); - String[] parts = repoFullName.split("/"); - final String repoOwner = parts[0]; - final String repoName = parts[1]; - final String instanceUrl = tinyDb.getString("instanceUrl"); - final String loginUid = tinyDb.getString("loginUid"); - final String instanceToken = "token " + tinyDb.getString(loginUid + "-token"); + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - final SwipeRefreshLayout swipeRefresh = v.findViewById(R.id.pullToRefresh); + final View v = inflater.inflate(R.layout.fragment_issues, container, false); + setHasOptionsMenu(true); - context = getContext(); - recyclerView = v.findViewById(R.id.recyclerView); - issuesList = new ArrayList<>(); + TinyDB tinyDb = new TinyDB(getContext()); + final String instanceUrl = tinyDb.getString("instanceUrl"); + final String loginUid = tinyDb.getString("loginUid"); + final String instanceToken = "token " + tinyDb.getString(loginUid + "-token"); + String repoFullName = tinyDb.getString("repoFullName"); + String[] parts = repoFullName.split("/"); + final String repoOwner = parts[0]; + final String repoName = parts[1]; - mProgressBar = v.findViewById(R.id.progress_bar); - noDataIssues = v.findViewById(R.id.noDataIssues); + if (VersionCheck.compareVersion("1.12.0", tinyDb.getString("giteaVersion")) < 1) { + resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + } - swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - new Handler().postDelayed(new Runnable() { - @Override - public void run() { + noDataIssues = v.findViewById(R.id.noDataIssues); + mProgressBar = v.findViewById(R.id.progress_bar); + final SwipeRefreshLayout swipeRefreshLayout = v.findViewById(R.id.pullToRefresh); - swipeRefresh.setRefreshing(false); - loadInitial(instanceToken, repoOwner, repoName, resultLimit, requestType); - adapter.notifyDataChanged(); + RecyclerView recyclerView = v.findViewById(R.id.recyclerView); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + recyclerView.setHasFixedSize(true); - } - }, 200); - } - }); + fastItemAdapter = new FastItemAdapter<>(); + fastItemAdapter.withSelectable(true); - adapter = new IssuesAdapter(getContext(), issuesList); - adapter.setLoadMoreListener(new IssuesAdapter.OnLoadMoreListener() { - @Override - public void onLoadMore() { + footerAdapter = items(); + //noinspection unchecked + fastItemAdapter.addAdapter(StaticGlobalVariables.issuesPageInit, footerAdapter); - recyclerView.post(new Runnable() { - @Override - public void run() { - if(issuesList.size() == 10 || pageSize == 10) { + fastItemAdapter.getItemFilter().withFilterPredicate((IItemAdapter.Predicate) (item, constraint) -> item.getIssueTitle().toLowerCase().contains(constraint.toString().toLowerCase())); - int page = (issuesList.size() + 10) / 10; - loadMore(Authorization.returnAuthentication(getContext(), loginUid, instanceToken), repoOwner, repoName, page, resultLimit, requestType); + fastItemAdapter.getItemFilter().withItemFilterListener(this); - } - /*else { + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + recyclerView.setItemAnimator(new DefaultItemAnimator()); + recyclerView.setAdapter(fastItemAdapter); - Toasty.info(context, getString(R.string.noMoreData)); + endlessRecyclerOnScrollListener = new EndlessRecyclerOnScrollListener(footerAdapter) { - }*/ - } - }); + @Override + public void onLoadMore(final int currentPage) { - } - }); + loadNext(instanceUrl, instanceToken, repoOwner, repoName, resultLimit, requestType, currentPage); - recyclerView.setHasFixedSize(true); - recyclerView.setLayoutManager(new LinearLayoutManager(context)); - recyclerView.setAdapter(adapter); + } - api = IssuesService.createService(ApiInterface.class, instanceUrl, getContext()); - loadInitial(Authorization.returnAuthentication(getContext(), loginUid, instanceToken), repoOwner, repoName, resultLimit, requestType); + }; - return v; + swipeRefreshLayout.setOnRefreshListener(() -> { - } + mProgressBar.setVisibility(View.VISIBLE); + fastItemAdapter.clear(); + endlessRecyclerOnScrollListener.resetPageCount(); + swipeRefreshLayout.setRefreshing(false); - @Override - public void onResume() { + }); - super.onResume(); - TinyDB tinyDb = new TinyDB(getContext()); - final String loginUid = tinyDb.getString("loginUid"); - String repoFullName = tinyDb.getString("repoFullName"); - String[] parts = repoFullName.split("/"); - final String repoOwner = parts[0]; - final String repoName = parts[1]; - final String instanceToken = "token " + tinyDb.getString(loginUid + "-token"); + recyclerView.addOnScrollListener(endlessRecyclerOnScrollListener); - if(tinyDb.getBoolean("resumeIssues")) { + loadInitial(instanceUrl, instanceToken, repoOwner, repoName, resultLimit, requestType); - loadInitial(Authorization.returnAuthentication(getContext(), loginUid, instanceToken), repoOwner, repoName, resultLimit, requestType); - tinyDb.putBoolean("resumeIssues", false); + fastItemAdapter.withEventHook(new IssuesAdapter.IssueTitleClickEvent()); - } + assert savedInstanceState != null; + fastItemAdapter.withSavedInstanceState(savedInstanceState); - } + return v; - private void loadInitial(String token, String repoOwner, String repoName, int resultLimit, String requestType) { + } - Call> call = api.getIssues(token, repoOwner, repoName, 1, resultLimit, requestType); + @Override + public void onResume() { - call.enqueue(new Callback>() { + super.onResume(); + TinyDB tinyDb = new TinyDB(getContext()); - @Override - public void onResponse(@NonNull Call> call, @NonNull Response> response) { + if(tinyDb.getBoolean("resumeIssues")) { - if(response.isSuccessful()) { + mProgressBar.setVisibility(View.VISIBLE); + fastItemAdapter.clear(); + endlessRecyclerOnScrollListener.resetPageCount(); + tinyDb.putBoolean("resumeIssues", false); - assert response.body() != null; - if(response.body().size() > 0) { + } - issuesList.clear(); - issuesList.addAll(response.body()); - adapter.notifyDataChanged(); - noDataIssues.setVisibility(View.GONE); + } - } - else { - issuesList.clear(); - adapter.notifyDataChanged(); - noDataIssues.setVisibility(View.VISIBLE); - } - mProgressBar.setVisibility(View.GONE); - } - else { - Log.e(TAG, String.valueOf(response.code())); - } + private void loadInitial(String instanceUrl, String token, String repoOwner, String repoName, int resultLimit, String requestType) { - } + Call> call = RetrofitClient.getInstance(instanceUrl, getContext()).getApiInterface().getIssues(token, repoOwner, repoName, 1, resultLimit, requestType); - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); - } + call.enqueue(new Callback>() { - }); + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { - } + if(response.isSuccessful()) { - private void loadMore(String token, String repoOwner, String repoName, int page, int resultLimit, String requestType){ + assert response.body() != null; + if(response.body().size() > 0) { - //add loading progress view - issuesList.add(new Issues("load")); - adapter.notifyItemInserted((issuesList.size() - 1)); + if(response.body().size() == resultLimit) { + loadNextFlag = true; + } - Call> call = api.getIssues(token, repoOwner, repoName, page, resultLimit, requestType); + for(int i = 0; i < response.body().size(); i++) { + items.add(new IssuesAdapter(getContext()).withNewItems(response.body().get(i).getTitle(), response.body().get(i).getNumber(), response.body().get(i).getUser().getAvatar_url(), response.body().get(i).getCreated_at(), response.body().get(i).getComments(), response.body().get(i).getUser().getFull_name(), response.body().get(i).getUser().getLogin())); + } - call.enqueue(new Callback>() { + fastItemAdapter.add(items); + noDataIssues.setVisibility(View.GONE); - @Override - public void onResponse(@NonNull Call> call, @NonNull Response> response) { + } + else { + noDataIssues.setVisibility(View.VISIBLE); + } - if(response.isSuccessful()){ + mProgressBar.setVisibility(View.GONE); - //remove loading view - issuesList.remove(issuesList.size()-1); + } + else { + Log.i(TAG, String.valueOf(response.code())); + } - List result = response.body(); + } - assert result != null; - if(result.size() > 0) { + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - pageSize = result.size(); - issuesList.addAll(result); + Log.e(TAG, t.toString()); + } - } - else { + }); - Toasty.info(context, getString(R.string.noMoreData)); - adapter.setMoreDataAvailable(false); + } - } + private void loadNext(String instanceUrl, String token, String repoOwner, String repoName, int resultLimit, String requestType, final int currentPage) { - adapter.notifyDataChanged(); + footerAdapter.clear(); + //noinspection unchecked + footerAdapter.add(new ProgressItem().withEnabled(false)); + Handler handler = new Handler(); - } - else { + handler.postDelayed(() -> { - Log.e(TAG, String.valueOf(response.code())); + Call> call = RetrofitClient.getInstance(instanceUrl, getContext()).getApiInterface().getIssues(token, repoOwner, repoName, currentPage + 1, resultLimit, requestType); - } + call.enqueue(new Callback>() { - } + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + if(response.isSuccessful()) { - Log.e(TAG, t.toString()); + assert response.body() != null; - } + if(response.body().size() > 0) { - }); - } + loadNextFlag = response.body().size() == resultLimit; - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + for(int i = 0; i < response.body().size(); i++) { - boolean connToInternet = AppUtil.haveNetworkConnection(Objects.requireNonNull(getContext())); + fastItemAdapter.add(fastItemAdapter.getAdapterItemCount(), new IssuesAdapter(getContext()).withNewItems(response.body().get(i).getTitle(), response.body().get(i).getNumber(), response.body().get(i).getUser().getAvatar_url(), response.body().get(i).getCreated_at(), response.body().get(i).getComments(), response.body().get(i).getUser().getFull_name(), response.body().get(i).getUser().getLogin())); - inflater.inflate(R.menu.search_menu, menu); - super.onCreateOptionsMenu(menu, inflater); + } - MenuItem searchItem = menu.findItem(R.id.action_search); - androidx.appcompat.widget.SearchView searchView = (androidx.appcompat.widget.SearchView) searchItem.getActionView(); - searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); - //searchView.setQueryHint(getContext().getString(R.string.strFilter)); + footerAdapter.clear(); + noDataIssues.setVisibility(View.GONE); - /*if(!connToInternet) { - return; - }*/ + } + else { + footerAdapter.clear(); + } - searchView.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() { + mProgressBar.setVisibility(View.GONE); - @Override - public boolean onQueryTextSubmit(String query) { - return false; - } + } + else { + Log.i(TAG, String.valueOf(response.code())); + } - @Override - public boolean onQueryTextChange(String newText) { + } - adapter.getFilter().filter(newText); - return false; + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - } + Log.i(TAG, t.toString()); + } - }); + }); - } + }, 1000); + + if(!loadNextFlag) { + footerAdapter.clear(); + } + + } + + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + + inflater.inflate(R.menu.search_menu, menu); + super.onCreateOptionsMenu(menu, inflater); + + MenuItem searchItem = menu.findItem(R.id.action_search); + androidx.appcompat.widget.SearchView searchView = (androidx.appcompat.widget.SearchView) searchItem.getActionView(); + searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); + + searchView.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() { + + @Override + public boolean onQueryTextSubmit(String query) { + + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + + fastItemAdapter.filter(newText); + return true; + } + + }); + + endlessRecyclerOnScrollListener.enable(); + + } + + @Override + public void itemsFiltered(@Nullable CharSequence constraint, @Nullable List results) { + + endlessRecyclerOnScrollListener.disable(); + } + + @Override + public void onReset() { + + endlessRecyclerOnScrollListener.enable(); + } } diff --git a/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java index 1224ae67..26a03a80 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java @@ -100,7 +100,7 @@ public class ProfileFragment extends Fragment { public static class SectionsPagerAdapter extends FragmentStatePagerAdapter { SectionsPagerAdapter(FragmentManager fm) { - super(fm); + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); } @NonNull diff --git a/app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java b/app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java new file mode 100644 index 00000000..8bdc8c06 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java @@ -0,0 +1,18 @@ +package org.mian.gitnex.helpers; + +/** + * Author M M Arif + */ + +public interface StaticGlobalVariables { + + // issues variables + String tagIssuesListOpen = "IssuesListOpenFragment - "; + String tagIssuesListClosed = "IssuesListClosedFragment - "; + int issuesPageInit = 1; + int resultLimitNewGiteaInstances = 25; // Gitea 1.12 and above + int resultLimitOldGiteaInstances = 10; // Gitea 1.11 and below + String issuesRequestType = "issues"; + String issueStateClosed = "closed"; + +} diff --git a/app/src/main/res/layout/activity_create_issue.xml b/app/src/main/res/layout/activity_create_issue.xml index b9bb3f5e..a903b5f1 100644 --- a/app/src/main/res/layout/activity_create_issue.xml +++ b/app/src/main/res/layout/activity_create_issue.xml @@ -77,7 +77,7 @@ android:background="@drawable/shape_inputs" android:textColor="?attr/inputTextColor" android:textColorHint="?attr/hintColor" - android:textColorHighlight="?attr/primaryTextColor"/> + android:textColorHighlight="?attr/primaryTextColor" /> + android:textColor="?attr/inputTextColor" + android:textColorHint="?attr/hintColor" + android:textColorHighlight="?attr/primaryTextColor" /> @@ -180,8 +180,8 @@ android:textSize="14sp" tools:ignore="Autofill" android:background="@drawable/shape_inputs" - android:textColor="?attr/primaryTextColor" - android:textColorHint="?attr/primaryTextColor" + android:textColor="?attr/inputTextColor" + android:textColorHint="?attr/hintColor" android:inputType="none" android:textColorHighlight="?attr/primaryTextColor"/> @@ -203,8 +203,8 @@ android:textSize="14sp" tools:ignore="Autofill" android:background="@drawable/shape_inputs" - android:textColor="?attr/primaryTextColor" - android:textColorHint="?attr/primaryTextColor" + android:textColor="?attr/inputTextColor" + android:textColorHint="?attr/hintColor" android:textColorHighlight="?attr/primaryTextColor"/>