From 756b9eb80ab9bf9604bf67806487866c2e07fa34 Mon Sep 17 00:00:00 2001 From: M M Arif Date: Sun, 22 Mar 2026 20:48:54 +0500 Subject: [PATCH] refactor explore and starred repos to activites --- app/src/main/AndroidManifest.xml | 8 + .../gitnex/activities/ExploreActivity.java | 96 ++++++++++ .../mian/gitnex/activities/MainActivity.java | 14 +- .../activities/MostVisitedReposActivity.java | 7 +- .../activities/StarredReposActivity.java | 155 +++++++++++++++ .../gitnex/fragments/ExploreFragment.java | 90 --------- .../fragments/HomeDashboardFragment.java | 11 +- .../StarredRepositoriesFragment.java | 177 ------------------ ...gment_explore.xml => activity_explore.xml} | 25 +-- .../main/res/layout/activity_repositories.xml | 132 +++++++++++++ app/src/main/res/navigation/nav_graph.xml | 15 -- 11 files changed, 428 insertions(+), 302 deletions(-) create mode 100644 app/src/main/java/org/mian/gitnex/activities/ExploreActivity.java create mode 100644 app/src/main/java/org/mian/gitnex/activities/StarredReposActivity.java delete mode 100644 app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java delete mode 100644 app/src/main/java/org/mian/gitnex/fragments/StarredRepositoriesFragment.java rename app/src/main/res/layout/{fragment_explore.xml => activity_explore.xml} (64%) create mode 100644 app/src/main/res/layout/activity_repositories.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 43d5f0f8..320443e0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -178,6 +178,14 @@ android:name=".activities.MostVisitedReposActivity" android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" android:windowSoftInputMode="adjustResize"/> + + tab.setText(tabTitles[position])) + .attach(); + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private static class ExplorePagerAdapter extends FragmentStateAdapter { + + public ExplorePagerAdapter(@NonNull FragmentActivity fa) { + super(fa); + } + + @NonNull @Override + public Fragment createFragment(int position) { + return switch (position) { + case 0 -> new ExploreRepositoriesFragment(); + case 1 -> new ExploreIssuesFragment(); + case 2 -> new ExplorePublicOrganizationsFragment(); + case 3 -> new ExploreUsersFragment(); + default -> throw new IllegalStateException("Unexpected position: " + position); + }; + } + + @Override + public int getItemCount() { + return 4; + } + } +} diff --git a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java index f16205d4..7937174c 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -160,7 +160,10 @@ public class MainActivity extends BaseActivity R.id.notificationsFragment, null, navOptions); return true; } else if (itemId == R.id.exploreFragment) { - navController.navigate(R.id.exploreFragment, null, navOptions); + Intent intent = new Intent(ctx, ExploreActivity.class); + startActivity(intent); + this.overridePendingTransition( + android.R.anim.fade_in, android.R.anim.fade_out); return true; } } catch (IllegalArgumentException ignored) { @@ -506,7 +509,9 @@ public class MainActivity extends BaseActivity navController.navigate(R.id.notificationsFragment, null, navOptions); break; case "explore": - navController.navigate(R.id.exploreFragment, null, navOptions); + Intent intent = new Intent(ctx, ExploreActivity.class); + startActivity(intent); + this.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); break; case "profile": Intent intentProfile = new Intent(this, ProfileActivity.class); @@ -573,8 +578,9 @@ public class MainActivity extends BaseActivity navController.navigate(R.id.repositoriesFragment, null, navOptions); break; case 5: - binding.toolbarTitle.setText(getResources().getString(R.string.pageTitleExplore)); - navController.navigate(R.id.exploreFragment, null, navOptions); + Intent intent = new Intent(ctx, ExploreActivity.class); + startActivity(intent); + this.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); break; case 6: navController.navigate(R.id.notificationsFragment, null, navOptions); diff --git a/app/src/main/java/org/mian/gitnex/activities/MostVisitedReposActivity.java b/app/src/main/java/org/mian/gitnex/activities/MostVisitedReposActivity.java index 18aa47e4..f067a0d5 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MostVisitedReposActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MostVisitedReposActivity.java @@ -95,10 +95,9 @@ public class MostVisitedReposActivity extends BaseActivity { private void setupRefreshLayout() { binding.pullToRefresh.setOnRefreshListener( - () -> { - new Handler(Looper.getMainLooper()) - .postDelayed(() -> fetchDataAsync(currentActiveAccountId), 250); - }); + () -> + new Handler(Looper.getMainLooper()) + .postDelayed(() -> fetchDataAsync(currentActiveAccountId), 250)); } private void fetchDataAsync(int accountId) { diff --git a/app/src/main/java/org/mian/gitnex/activities/StarredReposActivity.java b/app/src/main/java/org/mian/gitnex/activities/StarredReposActivity.java new file mode 100644 index 00000000..e78bee98 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/activities/StarredReposActivity.java @@ -0,0 +1,155 @@ +package org.mian.gitnex.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import java.util.ArrayList; +import java.util.List; +import org.gitnex.tea4j.v2.models.Repository; +import org.mian.gitnex.adapters.ReposListAdapter; +import org.mian.gitnex.databinding.ActivityRepositoriesBinding; +import org.mian.gitnex.helpers.Constants; +import org.mian.gitnex.helpers.EndlessRecyclerViewScrollListener; +import org.mian.gitnex.viewmodels.RepositoriesViewModel; + +/** + * @author mmarif + */ +public class StarredReposActivity extends BaseActivity { + + private ActivityRepositoriesBinding binding; + private RepositoriesViewModel viewModel; + private ReposListAdapter adapter; + private EndlessRecyclerViewScrollListener scrollListener; + private int resultLimit; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = ActivityRepositoriesBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + resultLimit = Constants.getCurrentResultLimit(this); + viewModel = new ViewModelProvider(this).get(RepositoriesViewModel.class); + + setupUI(); + setupSearch(); + observeViewModel(); + + refreshData(); + } + + private void setupUI() { + binding.btnBack.setOnClickListener(v -> finish()); + binding.btnSearch.setOnClickListener(v -> binding.searchView.show()); + binding.btnNewRepository.setOnClickListener( + v -> startActivity(new Intent(ctx, CreateRepoActivity.class))); + + adapter = new ReposListAdapter(new ArrayList<>(), this); + LinearLayoutManager layoutManager = new LinearLayoutManager(this); + binding.recyclerView.setLayoutManager(layoutManager); + binding.recyclerView.setAdapter(adapter); + + scrollListener = + new EndlessRecyclerViewScrollListener(layoutManager) { + @Override + public void onLoadMore(int page, int totalItemsCount, RecyclerView view) { + if (binding.searchView.isShowing()) return; + + viewModel.fetchRepos( + ctx, "starredRepos", "", null, page, resultLimit, null, false); + } + }; + binding.recyclerView.addOnScrollListener(scrollListener); + + binding.pullToRefresh.setOnRefreshListener( + () -> { + binding.pullToRefresh.setRefreshing(false); + refreshData(); + }); + } + + private void setupSearch() { + binding.searchResultsRecycler.setAdapter(adapter); + + binding.searchView + .getEditText() + .addTextChangedListener( + new TextWatcher() { + @Override + public void onTextChanged( + CharSequence s, int start, int before, int count) { + adapter.getFilter().filter(s.toString().trim()); + } + + @Override + public void beforeTextChanged( + CharSequence s, int start, int count, int after) {} + + @Override + public void afterTextChanged(Editable s) {} + }); + + binding.searchView.addTransitionListener( + (searchView, previousState, newState) -> { + if (newState + == com.google.android.material.search.SearchView.TransitionState + .HIDDEN) { + List originalList = viewModel.getRepos().getValue(); + if (originalList != null) { + adapter.updateList(originalList); + } + binding.recyclerView.scrollToPosition(0); + } + }); + } + + private void observeViewModel() { + viewModel + .getRepos() + .observe( + this, + list -> { + adapter.updateList(list); + updateUiState(); + }); + + viewModel + .getIsLoading() + .observe( + this, + loading -> { + boolean hasData = adapter.getItemCount() > 0; + binding.expressiveLoader.setVisibility( + loading && !hasData ? View.VISIBLE : View.GONE); + }); + + viewModel.getHasLoadedOnce().observe(this, hasLoaded -> updateUiState()); + } + + private void updateUiState() { + boolean isEmpty = adapter.getItemCount() == 0; + boolean loaded = Boolean.TRUE.equals(viewModel.getHasLoadedOnce().getValue()); + binding.layoutEmpty.getRoot().setVisibility(loaded && isEmpty ? View.VISIBLE : View.GONE); + } + + private void refreshData() { + if (scrollListener != null) scrollListener.resetState(); + viewModel.resetPagination(); + viewModel.fetchRepos(this, "starredRepos", "", null, 1, resultLimit, null, true); + } + + @Override + public void onResume() { + super.onResume(); + if (MainActivity.reloadRepos) { + refreshData(); + MainActivity.reloadRepos = false; + } + } +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java deleted file mode 100644 index 997d3fa0..00000000 --- a/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.mian.gitnex.fragments; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.viewpager2.adapter.FragmentStateAdapter; -import androidx.viewpager2.widget.ViewPager2; -import com.google.android.material.tabs.TabLayout; -import com.google.android.material.tabs.TabLayoutMediator; -import org.mian.gitnex.R; -import org.mian.gitnex.helpers.AppDatabaseSettings; -import org.mian.gitnex.helpers.ViewPager2Transformers; - -/** - * @author mmarif - */ -public class ExploreFragment extends Fragment { - - @Nullable @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - - View view = inflater.inflate(R.layout.fragment_explore, container, false); - - Context ctx = getContext(); - - ViewPager2 viewPager = view.findViewById(R.id.containerExplore); - viewPager.setOffscreenPageLimit(1); - TabLayout tabLayout = view.findViewById(R.id.tabsExplore); - - ViewGroup vg = (ViewGroup) tabLayout.getChildAt(0); - - viewPager.setAdapter(new ViewPagerAdapter(this)); - - ViewPager2Transformers.returnSelectedTransformer( - viewPager, - Integer.parseInt( - AppDatabaseSettings.getSettingsValue( - ctx, AppDatabaseSettings.APP_TABS_ANIMATION_KEY))); - - String[] tabTitles = { - getResources().getString(R.string.navRepos), - getResources().getString(R.string.pageTitleIssues), - getResources().getString(R.string.navOrg), - getResources().getString(R.string.pageTitleUsers) - }; - new TabLayoutMediator( - tabLayout, viewPager, (tab, position) -> tab.setText(tabTitles[position])) - .attach(); - - return view; - } - - public static class ViewPagerAdapter extends FragmentStateAdapter { - - public ViewPagerAdapter(@NonNull ExploreFragment fa) { - super(fa); - } - - @NonNull @Override - public Fragment createFragment(int position) { - Fragment fragment = - switch (position) { - case 0 -> // Repositories - new ExploreRepositoriesFragment(); - case 1 -> // Issues - new ExploreIssuesFragment(); - case 2 -> // Organizations - new ExplorePublicOrganizationsFragment(); - case 3 -> // Users - new ExploreUsersFragment(); - default -> null; - }; - assert fragment != null; - return fragment; - } - - @Override - public int getItemCount() { - return 4; - } - } -} diff --git a/app/src/main/java/org/mian/gitnex/fragments/HomeDashboardFragment.java b/app/src/main/java/org/mian/gitnex/fragments/HomeDashboardFragment.java index f1a300a6..88dd71cb 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/HomeDashboardFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/HomeDashboardFragment.java @@ -22,6 +22,7 @@ import org.mian.gitnex.activities.MainActivity; import org.mian.gitnex.activities.MostVisitedReposActivity; import org.mian.gitnex.activities.NotesActivity; import org.mian.gitnex.activities.ProfileActivity; +import org.mian.gitnex.activities.StarredReposActivity; import org.mian.gitnex.adapters.UserAccountsAdapter; import org.mian.gitnex.database.api.BaseApi; import org.mian.gitnex.database.api.UserAccountsApi; @@ -155,7 +156,15 @@ public class HomeDashboardFragment extends Fragment { }); binding.repoStarredCard .getRoot() - .setOnClickListener(v -> navigateTo(R.id.action_to_starredRepositories)); + .setOnClickListener( + v -> { + Intent intent = + new Intent(requireContext(), StarredReposActivity.class); + startActivity(intent); + requireActivity() + .overridePendingTransition( + android.R.anim.fade_in, android.R.anim.fade_out); + }); binding.repoWatchedCard .getRoot() .setOnClickListener(v -> navigateTo(R.id.action_to_watchedRepositories)); diff --git a/app/src/main/java/org/mian/gitnex/fragments/StarredRepositoriesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/StarredRepositoriesFragment.java deleted file mode 100644 index a228a135..00000000 --- a/app/src/main/java/org/mian/gitnex/fragments/StarredRepositoriesFragment.java +++ /dev/null @@ -1,177 +0,0 @@ -package org.mian.gitnex.fragments; - -import android.content.Intent; -import android.os.Bundle; -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.view.inputmethod.EditorInfo; -import androidx.annotation.NonNull; -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 java.util.ArrayList; -import org.mian.gitnex.R; -import org.mian.gitnex.activities.CreateRepoActivity; -import org.mian.gitnex.activities.MainActivity; -import org.mian.gitnex.adapters.ReposListAdapter; -import org.mian.gitnex.databinding.FragmentRepositoriesBinding; -import org.mian.gitnex.helpers.Constants; -import org.mian.gitnex.helpers.EndlessRecyclerViewScrollListener; -import org.mian.gitnex.viewmodels.RepositoriesViewModel; - -/** - * @author mmarif - */ -public class StarredRepositoriesFragment extends Fragment { - - private FragmentRepositoriesBinding binding; - private RepositoriesViewModel viewModel; - private ReposListAdapter adapter; - private EndlessRecyclerViewScrollListener scrollListener; - private int resultLimit; - private boolean isSearching = false; - - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - binding = FragmentRepositoriesBinding.inflate(inflater, container, false); - resultLimit = Constants.getCurrentResultLimit(requireContext()); - viewModel = new ViewModelProvider(this).get(RepositoriesViewModel.class); - - setupRecyclerView(); - setupSwipeRefresh(); - setupMenu(); - observeViewModel(); - - refreshData(); - return binding.getRoot(); - } - - private void setupRecyclerView() { - adapter = new ReposListAdapter(new ArrayList<>(), requireContext()); - LinearLayoutManager layoutManager = new LinearLayoutManager(requireContext()); - binding.recyclerView.setLayoutManager(layoutManager); - binding.recyclerView.setAdapter(adapter); - - scrollListener = - new EndlessRecyclerViewScrollListener(layoutManager) { - @Override - public void onLoadMore(int page, int totalItemsCount, RecyclerView view) { - if (!isSearching) { - viewModel.fetchRepos( - requireContext(), - "starredRepos", - "", - null, - page, - resultLimit, - null, - false); - } - } - }; - binding.recyclerView.addOnScrollListener(scrollListener); - - binding.addNewRepo.setOnClickListener( - v -> startActivity(new Intent(getContext(), CreateRepoActivity.class))); - } - - private void setupMenu() { - requireActivity() - .addMenuProvider( - new MenuProvider() { - @Override - public void onCreateMenu( - @NonNull Menu menu, @NonNull MenuInflater menuInflater) { - menuInflater.inflate(R.menu.search_menu, menu); - MenuItem searchItem = menu.findItem(R.id.action_search); - SearchView searchView = (SearchView) searchItem.getActionView(); - if (searchView != null) { - searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); - searchView.setOnQueryTextListener( - new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String query) { - return false; - } - - @Override - public boolean onQueryTextChange(String newText) { - isSearching = !newText.isEmpty(); - adapter.getFilter().filter(newText); - return true; - } - }); - } - } - - @Override - public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { - return false; - } - }, - getViewLifecycleOwner(), - Lifecycle.State.RESUMED); - } - - private void observeViewModel() { - viewModel - .getRepos() - .observe( - getViewLifecycleOwner(), - list -> { - adapter.updateList(list); - updateUiState(); - }); - - viewModel - .getIsLoading() - .observe( - getViewLifecycleOwner(), - loading -> { - boolean hasData = adapter.getItemCount() > 0; - binding.expressiveLoader.setVisibility( - loading && !hasData ? View.VISIBLE : View.GONE); - }); - - viewModel.getHasLoadedOnce().observe(getViewLifecycleOwner(), hasLoaded -> updateUiState()); - } - - private void updateUiState() { - boolean isEmpty = adapter.getItemCount() == 0; - boolean loaded = Boolean.TRUE.equals(viewModel.getHasLoadedOnce().getValue()); - binding.layoutEmpty.getRoot().setVisibility(loaded && isEmpty ? View.VISIBLE : View.GONE); - } - - private void refreshData() { - if (scrollListener != null) scrollListener.resetState(); - viewModel.resetPagination(); - viewModel.fetchRepos( - requireContext(), "starredRepos", "", null, 1, resultLimit, null, true); - } - - private void setupSwipeRefresh() { - binding.pullToRefresh.setOnRefreshListener( - () -> { - binding.pullToRefresh.setRefreshing(false); - refreshData(); - }); - } - - @Override - public void onResume() { - super.onResume(); - if (MainActivity.reloadRepos) { - refreshData(); - MainActivity.reloadRepos = false; - } - } -} diff --git a/app/src/main/res/layout/fragment_explore.xml b/app/src/main/res/layout/activity_explore.xml similarity index 64% rename from app/src/main/res/layout/fragment_explore.xml rename to app/src/main/res/layout/activity_explore.xml index 4a14f5f7..f2c16483 100644 --- a/app/src/main/res/layout/fragment_explore.xml +++ b/app/src/main/res/layout/activity_explore.xml @@ -2,29 +2,32 @@ + + + app:tabMode="fixed" + app:tabGravity="fill" + app:tabIndicatorAnimationMode="elastic" + app:tabSelectedTextColor="?attr/colorPrimary" /> @@ -32,6 +35,6 @@ android:id="@+id/containerExplore" android:layout_width="match_parent" android:layout_height="match_parent" - app:layout_behavior="@string/appbar_scrolling_view_behavior"/> + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> diff --git a/app/src/main/res/layout/activity_repositories.xml b/app/src/main/res/layout/activity_repositories.xml new file mode 100644 index 00000000..8a2c319e --- /dev/null +++ b/app/src/main/res/layout/activity_repositories.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index cf7bedf6..08d28918 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -18,11 +18,6 @@ app:destination="@id/myRepositoriesFragment" app:popUpTo="@id/homeDashboardFragment" app:popUpToInclusive="false" /> - - - - -