From 3a733ce9064b3e47da9a97a7759e3fa2b0e9e209 Mon Sep 17 00:00:00 2001 From: M M Arif Date: Mon, 17 Jul 2023 23:31:27 +0500 Subject: [PATCH] Fix repository forks pagination and filter --- .../gitnex/activities/RepoForksActivity.java | 182 +++++------------- .../gitnex/adapters/RepoForksAdapter.java | 74 ++++++- .../viewmodels/RepositoryForksViewModel.java | 104 ++++++++++ 3 files changed, 222 insertions(+), 138 deletions(-) create mode 100644 app/src/main/java/org/mian/gitnex/viewmodels/RepositoryForksViewModel.java diff --git a/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java b/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java index cdd13510..fab994d6 100644 --- a/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java @@ -4,28 +4,20 @@ import android.annotation.SuppressLint; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.EditorInfo; import androidx.annotation.NonNull; -import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; +import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; -import java.util.ArrayList; -import java.util.List; -import org.gitnex.tea4j.v2.models.Repository; import org.mian.gitnex.R; import org.mian.gitnex.adapters.RepoForksAdapter; -import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.ActivityRepoForksBinding; -import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.contexts.RepositoryContext; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; +import org.mian.gitnex.viewmodels.RepositoryForksViewModel; /** * @author M M Arif @@ -33,13 +25,8 @@ import retrofit2.Response; public class RepoForksActivity extends BaseActivity { private ActivityRepoForksBinding activityRepoForksBinding; - private final String TAG = "RepositoryForks"; - - private int resultLimit; private int pageSize = 1; - private List forksList; private RepoForksAdapter adapter; - private RepositoryContext repository; @SuppressLint("DefaultLocale") @@ -55,15 +42,11 @@ public class RepoForksActivity extends BaseActivity { setSupportActionBar(toolbar); repository = RepositoryContext.fromIntent(getIntent()); - final String repoOwner = repository.getOwner(); - final String repoName = repository.getName(); activityRepoForksBinding.toolbarTitle.setText( ctx.getResources().getString(R.string.infoTabRepoForksCount)); activityRepoForksBinding.close.setOnClickListener(v -> finish()); - resultLimit = Constants.getCurrentResultLimit(ctx); - forksList = new ArrayList<>(); activityRepoForksBinding.pullToRefresh.setOnRefreshListener( () -> @@ -73,128 +56,77 @@ public class RepoForksActivity extends BaseActivity { pageSize = 1; activityRepoForksBinding.pullToRefresh.setRefreshing( false); - loadInitial(repoOwner, repoName, pageSize, resultLimit); - adapter.notifyDataChanged(); + fetchData(); + activityRepoForksBinding.progressBar.setVisibility( + View.VISIBLE); }, - 200)); - - adapter = new RepoForksAdapter(ctx, forksList); - adapter.setLoadMoreListener( - () -> - activityRepoForksBinding.recyclerView.post( - () -> { - if (forksList.size() == resultLimit - || pageSize == resultLimit) { - - int page = (forksList.size() + resultLimit) / resultLimit; - loadMore(repoOwner, repoName, page, resultLimit); - } - })); + 150)); activityRepoForksBinding.recyclerView.setHasFixedSize(true); activityRepoForksBinding.recyclerView.setLayoutManager(new LinearLayoutManager(ctx)); - activityRepoForksBinding.recyclerView.setAdapter(adapter); - loadInitial(repoOwner, repoName, pageSize, resultLimit); + fetchData(); } - private void loadInitial(String repoOwner, String repoName, int pageSize, int resultLimit) { + private void fetchData() { - Call> call = - RetrofitClient.getApiInterface(ctx) - .listForks(repoOwner, repoName, pageSize, resultLimit); + RepositoryForksViewModel repositoryForksViewModel = + new ViewModelProvider(this).get(RepositoryForksViewModel.class); - call.enqueue( - new Callback<>() { + repositoryForksViewModel + .getForksList(repository.getOwner(), repository.getName(), ctx) + .observe( + this, + forksListMain -> { + adapter = new RepoForksAdapter(ctx, forksListMain); + adapter.setLoadMoreListener( + new RepoForksAdapter.OnLoadMoreListener() { - @Override - public void onResponse( - @NonNull Call> call, - @NonNull Response> response) { + @Override + public void onLoadMore() { - if (response.isSuccessful()) { + pageSize += 1; + repositoryForksViewModel.loadMore( + repository.getOwner(), + repository.getName(), + pageSize, + ctx, + adapter); + activityRepoForksBinding.progressBar.setVisibility( + View.VISIBLE); + } - assert response.body() != null; + @Override + public void onLoadFinished() { - if (response.body().size() > 0) { - forksList.clear(); - forksList.addAll(response.body()); - adapter.notifyDataChanged(); + activityRepoForksBinding.progressBar.setVisibility( + View.GONE); + } + }); + + if (adapter.getItemCount() > 0) { + activityRepoForksBinding.recyclerView.setAdapter(adapter); activityRepoForksBinding.noData.setVisibility(View.GONE); } else { - forksList.clear(); adapter.notifyDataChanged(); + activityRepoForksBinding.recyclerView.setAdapter(adapter); activityRepoForksBinding.noData.setVisibility(View.VISIBLE); } activityRepoForksBinding.progressBar.setVisibility(View.GONE); - } else { - Log.e(TAG, String.valueOf(response.code())); - } - } - - @Override - public void onFailure( - @NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); - } - }); - } - - private void loadMore(String repoOwner, String repoName, int page, int resultLimit) { - - activityRepoForksBinding.progressLoadMore.setVisibility(View.VISIBLE); - - Call> call = - RetrofitClient.getApiInterface(ctx) - .listForks(repoOwner, repoName, page, resultLimit); - - call.enqueue( - new Callback<>() { - - @Override - public void onResponse( - @NonNull Call> call, - @NonNull Response> response) { - - if (response.isSuccessful()) { - - // remove loading view - forksList.remove(forksList.size() - 1); - - List result = response.body(); - assert result != null; - - if (result.size() > 0) { - pageSize = result.size(); - forksList.addAll(result); - } else { - adapter.setMoreDataAvailable(false); - } - - adapter.notifyDataChanged(); - activityRepoForksBinding.progressLoadMore.setVisibility(View.GONE); - } else { - Log.e(TAG, String.valueOf(response.code())); - } - } - - @Override - public void onFailure( - @NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); - } - }); + }); } @Override - public boolean onCreateOptionsMenu(Menu menu) { + public boolean onCreateOptionsMenu(@NonNull Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.search_menu, menu); + super.onCreateOptionsMenu(menu); MenuItem searchItem = menu.findItem(R.id.action_search); - SearchView searchView = (SearchView) searchItem.getActionView(); + androidx.appcompat.widget.SearchView searchView = + (androidx.appcompat.widget.SearchView) searchItem.getActionView(); searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); searchView.setOnQueryTextListener( @@ -207,27 +139,13 @@ public class RepoForksActivity extends BaseActivity { @Override public boolean onQueryTextChange(String newText) { - filter(newText); - return true; + if (activityRepoForksBinding.recyclerView.getAdapter() != null) { + adapter.getFilter().filter(newText); + } + return false; } }); - - return super.onCreateOptionsMenu(menu); - } - - private void filter(String text) { - List userRepositories = new ArrayList<>(); - - for (Repository d : forksList) { - if (d.getOwner().getLogin().contains(text) - || d.getName().toLowerCase().contains(text) - || d.getDescription().toLowerCase().contains(text)) { - - userRepositories.add(d); - } - } - - adapter.updateList(userRepositories); + return false; } @Override diff --git a/app/src/main/java/org/mian/gitnex/adapters/RepoForksAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/RepoForksAdapter.java index 264ae6b6..05c7e908 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/RepoForksAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/RepoForksAdapter.java @@ -8,12 +8,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; +import android.widget.Filter; +import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.amulyakhare.textdrawable.TextDrawable; import com.amulyakhare.textdrawable.util.ColorGenerator; +import java.util.ArrayList; import java.util.List; import java.util.Locale; import org.mian.gitnex.R; @@ -29,19 +32,62 @@ import org.mian.gitnex.helpers.contexts.RepositoryContext; /** * @author M M Arif */ -public class RepoForksAdapter extends RecyclerView.Adapter { +public class RepoForksAdapter extends RecyclerView.Adapter + implements Filterable { + private final List forksListFull; private final Context context; private List forksList; - private Runnable loadMoreListener; - private boolean isLoading = false; - private boolean isMoreDataAvailable = true; + private OnLoadMoreListener loadMoreListener; + private boolean isLoading = false, isMoreDataAvailable = true; + + private final Filter forksFilter = + new Filter() { + @Override + protected FilterResults performFiltering(CharSequence constraint) { + List filteredList = new ArrayList<>(); + + if (constraint == null || constraint.length() == 0) { + filteredList.addAll(forksListFull); + } else { + String filterPattern = constraint.toString().toLowerCase().trim(); + + for (org.gitnex.tea4j.v2.models.Repository item : forksListFull) { + if (item.getFullName().toLowerCase().contains(filterPattern) + || item.getOwner() + .getLogin() + .toLowerCase() + .contains(filterPattern) + || item.getOwner() + .getEmail() + .toLowerCase() + .contains(filterPattern)) { + filteredList.add(item); + } + } + } + + FilterResults results = new FilterResults(); + results.values = filteredList; + + return results; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + + forksList.clear(); + forksList.addAll((List) results.values); + notifyDataChanged(); + } + }; public RepoForksAdapter( Context ctx, List forksListMain) { this.context = ctx; this.forksList = forksListMain; + forksListFull = new ArrayList<>(forksList); } @NonNull @Override @@ -59,7 +105,7 @@ public class RepoForksAdapter extends RecyclerView.Adapter> forksList; + private int resultLimit; + + public LiveData> getForksList(String repoOwner, String repoName, Context ctx) { + + forksList = new MutableLiveData<>(); + resultLimit = Constants.getCurrentResultLimit(ctx); + loadInitialList(repoOwner, repoName, ctx); + return forksList; + } + + public void loadInitialList(String repoOwner, String repoName, Context ctx) { + + Call> call = + RetrofitClient.getApiInterface(ctx).listForks(repoOwner, repoName, 1, resultLimit); + + call.enqueue( + new Callback<>() { + + @Override + public void onResponse( + @NonNull Call> call, + @NonNull Response> response) { + + if (response.isSuccessful()) { + forksList.postValue(response.body()); + } else { + Toasty.error(ctx, ctx.getString(R.string.genericError)); + } + } + + @Override + public void onFailure( + @NonNull Call> call, @NonNull Throwable t) { + + Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError)); + } + }); + } + + public void loadMore( + String repoOwner, String repoName, int page, Context ctx, RepoForksAdapter adapter) { + + Call> call = + RetrofitClient.getApiInterface(ctx) + .listForks(repoOwner, repoName, page, resultLimit); + + call.enqueue( + new Callback<>() { + + @Override + public void onResponse( + @NonNull Call> call, + @NonNull Response> response) { + + if (response.isSuccessful()) { + + List list = forksList.getValue(); + assert list != null; + assert response.body() != null; + + if (response.body().size() != 0) { + list.addAll(response.body()); + adapter.updateList(list); + } else { + adapter.setMoreDataAvailable(false); + } + } else { + Toasty.error(ctx, ctx.getString(R.string.genericError)); + } + } + + @Override + public void onFailure( + @NonNull Call> call, @NonNull Throwable t) { + + Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError)); + } + }); + } +}