Improve branches UI and add pagination to it

This commit is contained in:
M M Arif
2025-03-09 21:21:13 +05:00
parent 9f9e041643
commit 460fe86d7d
6 changed files with 241 additions and 122 deletions

View File

@@ -29,7 +29,6 @@ import com.google.android.material.tabs.TabLayoutMediator;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.gitnex.tea4j.v2.models.Branch;
import org.gitnex.tea4j.v2.models.Milestone;
import org.gitnex.tea4j.v2.models.Organization;
import org.gitnex.tea4j.v2.models.Repository;
@@ -247,9 +246,6 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe
public void onButtonClicked(String text) {
switch (text) {
case "chooseBranch":
chooseBranch();
break;
case "openWebRepo":
AppUtil.openUrlInBrowser(this, repository.getRepository().getHtmlUrl());
break;
@@ -418,68 +414,6 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe
}
}
private void chooseBranch() {
progressDialog = new Dialog(this);
progressDialog.setCancelable(false);
progressDialog.setContentView(R.layout.custom_progress_loader);
progressDialog.show();
Call<List<Branch>> call =
RetrofitClient.getApiInterface(ctx)
.repoListBranches(repository.getOwner(), repository.getName(), null, null);
call.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<List<Branch>> call,
@NonNull Response<List<Branch>> response) {
progressDialog.hide();
if (response.code() == 200) {
List<String> branchesList = new ArrayList<>();
int selectedBranch = 0;
assert response.body() != null;
for (int i = 0; i < response.body().size(); i++) {
Branch branches = response.body().get(i);
branchesList.add(branches.getName());
if (repository.getBranchRef().equals(branches.getName())) {
selectedBranch = i;
}
}
materialAlertDialogBuilder
.setTitle(R.string.pageTitleChooseBranch)
.setSingleChoiceItems(
branchesList.toArray(new String[0]),
selectedBranch,
(dialogInterface, i) -> {
repository.setBranchRef(branchesList.get(i));
if (getFragmentRefreshListenerFiles() != null) {
getFragmentRefreshListenerFiles()
.onRefresh(branchesList.get(i));
}
dialogInterface.dismiss();
})
.setNeutralButton(R.string.cancelButton, null);
materialAlertDialogBuilder.create().show();
}
}
@Override
public void onFailure(@NonNull Call<List<Branch>> call, @NonNull Throwable t) {
progressDialog.hide();
}
});
}
private void getRepoInfo(final String owner, String repo) {
LinearProgressIndicator loading = findViewById(R.id.loadingIndicator);
@@ -670,18 +604,6 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe
mainIntent.removeExtra("goToSectionType");
switch (Objects.requireNonNull(goToSectionType)) {
case "branchesList":
viewPager.setCurrentItem(1);
chooseBranch();
break;
case "branch":
viewPager.setCurrentItem(1);
String selectedBranch = mainIntent.getStringExtra("selectedBranch");
repository.setBranchRef(selectedBranch);
if (getFragmentRefreshListenerFiles() != null) {
getFragmentRefreshListenerFiles().onRefresh(selectedBranch);
}
break;
case "file":
viewPager.setCurrentItem(1);
String branch1 = mainIntent.getStringExtra("branch");

View File

@@ -0,0 +1,76 @@
package org.mian.gitnex.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import org.gitnex.tea4j.v2.models.Branch;
import org.mian.gitnex.R;
/**
* @author mmarif
*/
public class BranchAdapter extends RecyclerView.Adapter<BranchAdapter.BranchViewHolder> {
private final List<Branch> branches = new ArrayList<>();
private final OnBranchClickListener listener;
public interface OnBranchClickListener {
void onBranchClick(String branchName);
}
public BranchAdapter(OnBranchClickListener listener) {
this.listener = listener;
}
@NonNull @Override
public BranchViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view =
LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_branches, parent, false);
return new BranchViewHolder(view);
}
@Override
public int getItemCount() {
return branches.size();
}
public void addBranches(List<Branch> newBranches) {
int startPosition = branches.size();
branches.addAll(newBranches);
notifyItemRangeInserted(startPosition, newBranches.size());
}
public void clear() {
int oldSize = branches.size();
branches.clear();
notifyItemRangeRemoved(0, oldSize);
}
public static class BranchViewHolder extends RecyclerView.ViewHolder {
TextView textView;
BranchViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.branch_name);
}
}
@Override
public void onBindViewHolder(BranchViewHolder holder, int position) {
Branch branch = branches.get(position);
holder.textView.setText(branch.getName());
holder.textView.setOnClickListener(
v -> {
if (listener != null) {
listener.onBranchClick(branch.getName());
}
});
}
}

View File

@@ -3,6 +3,7 @@ package org.mian.gitnex.fragments;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -14,12 +15,14 @@ import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.core.view.MenuProvider;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList;
import java.util.Collections;
@@ -33,6 +36,7 @@ import org.mian.gitnex.R;
import org.mian.gitnex.activities.CreateFileActivity;
import org.mian.gitnex.activities.FileViewActivity;
import org.mian.gitnex.activities.RepoDetailActivity;
import org.mian.gitnex.adapters.BranchAdapter;
import org.mian.gitnex.adapters.FilesAdapter;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.database.api.BaseApi;
@@ -40,6 +44,7 @@ import org.mian.gitnex.database.api.UserAccountsApi;
import org.mian.gitnex.database.models.UserAccount;
import org.mian.gitnex.databinding.FragmentFilesBinding;
import org.mian.gitnex.helpers.AppUtil;
import org.mian.gitnex.helpers.Constants;
import org.mian.gitnex.helpers.Path;
import org.mian.gitnex.helpers.contexts.RepositoryContext;
import org.mian.gitnex.viewmodels.FilesViewModel;
@@ -405,60 +410,146 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter
progressDialog.setContentView(R.layout.custom_progress_loader);
progressDialog.show();
MaterialAlertDialogBuilder materialAlertDialogBuilder =
new MaterialAlertDialogBuilder(
requireContext(), R.style.ThemeOverlay_Material3_Dialog_Alert);
MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(requireContext());
View dialogView = getLayoutInflater().inflate(R.layout.custom_branches_dialog, null);
dialogBuilder.setView(dialogView);
Call<List<Branch>> call =
RetrofitClient.getApiInterface(requireContext())
.repoListBranches(repository.getOwner(), repository.getName(), null, null);
call.enqueue(
new Callback<>() {
RecyclerView recyclerView = dialogView.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
recyclerView.addItemDecoration(
new RecyclerView.ItemDecoration() {
@Override
public void onResponse(
@NonNull Call<List<Branch>> call,
@NonNull Response<List<Branch>> response) {
public void getItemOffsets(
@NonNull Rect outRect,
@NonNull View view,
@NonNull RecyclerView parent,
@NonNull RecyclerView.State state) {
progressDialog.hide();
if (response.code() == 200) {
int position = parent.getChildAdapterPosition(view);
int spacing =
(int)
requireContext()
.getResources()
.getDimension(R.dimen.dimen20dp);
List<String> branchesList = new ArrayList<>();
int selectedBranch = 0;
assert response.body() != null;
outRect.right = spacing;
outRect.left = spacing;
for (int i = 0; i < response.body().size(); i++) {
Branch branches = response.body().get(i);
branchesList.add(branches.getName());
if (repository.getBranchRef().equals(branches.getName())) {
selectedBranch = i;
}
}
materialAlertDialogBuilder
.setTitle(R.string.pageTitleChooseBranch)
.setSingleChoiceItems(
branchesList.toArray(new String[0]),
selectedBranch,
(dialogInterface, i) -> {
repository.setBranchRef(branchesList.get(i));
binding.branchTitle.setText(branchesList.get(i));
refresh();
dialogInterface.dismiss();
})
.setNeutralButton(R.string.cancelButton, null);
materialAlertDialogBuilder.create().show();
if (position > 0) {
outRect.top = spacing;
}
}
});
dialogBuilder.setNeutralButton(R.string.close, (dialog, which) -> dialog.dismiss());
AlertDialog dialog = dialogBuilder.create();
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
final int[] page = {1};
final int resultLimit = Constants.getCurrentResultLimit(requireContext());
final boolean[] isLoading = {false};
final boolean[] isLastPage = {false};
BranchAdapter adapter =
new BranchAdapter(
branchName -> {
repository.setBranchRef(branchName);
binding.branchTitle.setText(branchName);
refresh();
dialog.dismiss();
});
recyclerView.setAdapter(adapter);
Runnable fetchBranches =
() -> {
if (isLoading[0] || isLastPage[0]) return;
isLoading[0] = true;
Call<List<Branch>> call =
RetrofitClient.getApiInterface(requireContext())
.repoListBranches(
repository.getOwner(),
repository.getName(),
page[0],
resultLimit);
call.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<List<Branch>> call,
@NonNull Response<List<Branch>> response) {
isLoading[0] = false;
if (response.code() == 200 && response.body() != null) {
List<Branch> newBranches = response.body();
adapter.addBranches(newBranches);
String totalCountStr =
response.headers().get("X-Total-Count");
if (totalCountStr != null) {
int totalItems = Integer.parseInt(totalCountStr);
int totalPages =
(int)
Math.ceil(
(double) totalItems
/ resultLimit);
isLastPage[0] = page[0] >= totalPages;
} else {
isLastPage[0] = newBranches.size() < resultLimit;
}
page[0]++;
if (page[0] == 2 && !dialog.isShowing()) {
progressDialog.dismiss();
dialog.show();
}
} else {
progressDialog.dismiss();
}
}
@Override
public void onFailure(
@NonNull Call<List<Branch>> call, @NonNull Throwable t) {
isLoading[0] = false;
progressDialog.dismiss();
}
});
};
recyclerView.addOnScrollListener(
new RecyclerView.OnScrollListener() {
@Override
public void onFailure(@NonNull Call<List<Branch>> call, @NonNull Throwable t) {
progressDialog.hide();
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager layoutManager =
(LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager != null) {
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int firstVisibleItemPosition =
layoutManager.findFirstVisibleItemPosition();
if (!isLoading[0]
&& !isLastPage[0]
&& (visibleItemCount + firstVisibleItemPosition)
>= totalItemCount - 5) {
fetchBranches.run();
}
}
}
});
adapter.clear();
fetchBranches.run();
}
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/dimen16dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pageTitleChooseBranch"
android:textAppearance="?attr/textAppearanceHeadline5"
android:layout_marginBottom="@dimen/dimen8dp"
android:padding="@dimen/dimen16dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen320dp"
android:scrollbars="vertical" />
</LinearLayout>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/branch_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen16sp" />

View File

@@ -55,7 +55,7 @@
android:paddingEnd="@dimen/dimen2dp"
android:text="@string/userName"
android:textColor="?attr/primaryTextColor"
android:textSize="14sp"/>
android:textSize="@dimen/dimen14sp"/>
<TextView
android:id="@+id/userName"