From 94952f18207e596a818aeb467ecb75b42e5a7ec4 Mon Sep 17 00:00:00 2001 From: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Date: Tue, 18 Aug 2020 20:33:54 +0530 Subject: [PATCH] [GSoC] Added Leaderboard Filters (#3902) * Attempt to add filters * Basic Filter Working * Filter Improved * Filter Completed --- .../profile/leaderboard/DataSourceClass.java | 19 ++-- .../leaderboard/DataSourceFactory.java | 38 +++++++- .../leaderboard/LeaderboardFragment.java | 97 ++++++++++++++++--- .../leaderboard/LeaderboardListViewModel.java | 23 ++++- .../profile/leaderboard/ViewModelFactory.java | 1 + .../main/res/layout/fragment_leaderboard.xml | 36 ++++++- app/src/main/res/values/arrays.xml | 25 +++++ app/src/main/res/values/strings.xml | 6 ++ 8 files changed, 222 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/DataSourceClass.java b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/DataSourceClass.java index ac522cfd7..7feac4659 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/DataSourceClass.java +++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/DataSourceClass.java @@ -2,8 +2,6 @@ package fr.free.nrw.commons.profile.leaderboard; import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.LOADED; import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.LOADING; -import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.PAGE_SIZE; -import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.START_OFFSET; import androidx.annotation.NonNull; import androidx.lifecycle.MutableLiveData; @@ -20,10 +18,19 @@ public class DataSourceClass extends PageKeyedDataSource progressLiveStatus; private CompositeDisposable compositeDisposable = new CompositeDisposable(); + private String duration; + private String category; + private int limit; + private int offset; - public DataSourceClass(OkHttpJsonApiClient okHttpJsonApiClient,SessionManager sessionManager) { + public DataSourceClass(OkHttpJsonApiClient okHttpJsonApiClient,SessionManager sessionManager, + String duration, String category, int limit, int offset) { this.okHttpJsonApiClient = okHttpJsonApiClient; this.sessionManager = sessionManager; + this.duration = duration; + this.category = category; + this.limit = limit; + this.offset = offset; progressLiveStatus = new MutableLiveData<>(); } @@ -38,7 +45,7 @@ public class DataSourceClass extends PageKeyedDataSource { compositeDisposable.add(disposable); progressLiveStatus.postValue(LOADING); @@ -68,7 +75,7 @@ public class DataSourceClass extends PageKeyedDataSource callback) { compositeDisposable.add(okHttpJsonApiClient .getLeaderboard(Objects.requireNonNull(sessionManager.getCurrentAccount()).name, - "all_time", "upload", String.valueOf(PAGE_SIZE), String.valueOf(params.key)) + duration, category, String.valueOf(limit), String.valueOf(params.key)) .doOnSubscribe(disposable -> { compositeDisposable.add(disposable); progressLiveStatus.postValue(LOADING); @@ -76,7 +83,7 @@ public class DataSourceClass extends PageKeyedDataSource { if (response != null && response.getStatus() == 200) { progressLiveStatus.postValue(LOADED); - callback.onResult(response.getLeaderboardList(), params.key + PAGE_SIZE); + callback.onResult(response.getLeaderboardList(), params.key + limit); } }, t -> { diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/DataSourceFactory.java b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/DataSourceFactory.java index 3b5428a2a..c43cd3631 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/DataSourceFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/DataSourceFactory.java @@ -12,6 +12,42 @@ public class DataSourceFactory extends DataSource.Factory create() { - DataSourceClass dataSourceClass = new DataSourceClass(okHttpJsonApiClient, sessionManager); + DataSourceClass dataSourceClass = new DataSourceClass(okHttpJsonApiClient, sessionManager, duration, category, limit, offset); liveData.postValue(dataSourceClass); return dataSourceClass; } diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java index 0cd45df85..09738a815 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java @@ -2,13 +2,19 @@ package fr.free.nrw.commons.profile.leaderboard; import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.LOADED; import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.LOADING; +import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.PAGE_SIZE; +import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.START_OFFSET; import android.accounts.Account; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; import android.widget.ProgressBar; +import android.widget.Spinner; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.MergeAdapter; @@ -35,6 +41,12 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment { @BindView(R.id.progressBar) ProgressBar progressBar; + @BindView(R.id.category_spinner) + Spinner categorySpinner; + + @BindView(R.id.duration_spinner) + Spinner durationSpinner; + @Inject SessionManager sessionManager; @@ -48,32 +60,91 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment { private CompositeDisposable compositeDisposable = new CompositeDisposable(); + String duration; + String category; + int limit = PAGE_SIZE; + int offset = START_OFFSET; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_leaderboard, container, false); ButterKnife.bind(this, rootView); + progressBar.setVisibility(View.VISIBLE); hideLayouts(); - setLeaderboard(); + setSpinners(); + + String[] durationValues = getContext().getResources().getStringArray(R.array.leaderboard_duration_values); + String[] categoryValues = getContext().getResources().getStringArray(R.array.leaderboard_category_values); + + duration = durationValues[0]; + category = categoryValues[0]; + + setLeaderboard(duration, category, limit, offset); + + durationSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + + duration = durationValues[durationSpinner.getSelectedItemPosition()]; + refreshLeaderboard(); + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + } + }); + + categorySpinner.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + category = categoryValues[categorySpinner.getSelectedItemPosition()]; + refreshLeaderboard(); + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + } + }); + return rootView; } + private void refreshLeaderboard() { + if (viewModel != null) { + viewModel.refresh(duration, category, limit, offset); + setLeaderboard(duration, category, limit, offset); + } + } + + private void setSpinners() { + ArrayAdapter categoryAdapter = ArrayAdapter.createFromResource(getContext(), + R.array.leaderboard_categories, android.R.layout.simple_spinner_item); + categoryAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + categorySpinner.setAdapter(categoryAdapter); + + ArrayAdapter durationAdapter = ArrayAdapter.createFromResource(getContext(), + R.array.leaderboard_durations, android.R.layout.simple_spinner_item); + durationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + durationSpinner.setAdapter(durationAdapter); + } + /** * To call the API to get results * which then sets the views using setLeaderboardUser method */ - private void setLeaderboard() { + private void setLeaderboard(String duration, String category, int limit, int offset) { if (checkAccount()) { try { compositeDisposable.add(okHttpJsonApiClient .getLeaderboard(Objects.requireNonNull(sessionManager.getCurrentAccount()).name, - "all_time", "upload", null, null) + duration, category, null, null) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( response -> { if (response != null && response.getStatus() == 200) { - setViews(response); + setViews(response, duration, category, limit, offset); } }, t -> { @@ -92,22 +163,22 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment { * Set the views * @param response Leaderboard Response Object */ - private void setViews(LeaderboardResponse response) { + private void setViews(LeaderboardResponse response, String duration, String category, int limit, int offset) { viewModel = new ViewModelProvider(this, viewModelFactory).get(LeaderboardListViewModel.class); + viewModel.setParams(duration, category, limit, offset); LeaderboardListAdapter leaderboardListAdapter = new LeaderboardListAdapter(); UserDetailAdapter userDetailAdapter= new UserDetailAdapter(response); MergeAdapter mergeAdapter = new MergeAdapter(userDetailAdapter, leaderboardListAdapter); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); leaderboardListRecyclerView.setLayoutManager(linearLayoutManager); leaderboardListRecyclerView.setAdapter(mergeAdapter); - viewModel.getListLiveData().observe(getViewLifecycleOwner(), leaderboardListAdapter::submitList); viewModel.getProgressLoadStatus().observe(getViewLifecycleOwner(), status -> { - if (Objects.requireNonNull(status).equalsIgnoreCase(LOADING)) { - showProgressBar(); - } else if (status.equalsIgnoreCase(LOADED)) { - hideProgressBar(); - } + if (Objects.requireNonNull(status).equalsIgnoreCase(LOADING)) { + showProgressBar(); + } else if (status.equalsIgnoreCase(LOADED)) { + hideProgressBar(); + } }); } @@ -117,6 +188,8 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment { private void hideProgressBar() { if (progressBar != null) { progressBar.setVisibility(View.GONE); + categorySpinner.setVisibility(View.VISIBLE); + durationSpinner.setVisibility(View.VISIBLE); leaderboardListRecyclerView.setVisibility(View.VISIBLE); } } @@ -134,6 +207,8 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment { * used to hide the layouts while fetching results from api */ private void hideLayouts(){ + categorySpinner.setVisibility(View.INVISIBLE); + durationSpinner.setVisibility(View.INVISIBLE); leaderboardListRecyclerView.setVisibility(View.INVISIBLE); } diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardListViewModel.java b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardListViewModel.java index 1c9655269..56008a03b 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardListViewModel.java +++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardListViewModel.java @@ -17,11 +17,13 @@ public class LeaderboardListViewModel extends ViewModel { private DataSourceFactory dataSourceFactory; private LiveData> listLiveData; private CompositeDisposable compositeDisposable = new CompositeDisposable(); - private LiveData progressLoadStatus = new MutableLiveData<>(); - public LeaderboardListViewModel(OkHttpJsonApiClient okHttpJsonApiClient, SessionManager sessionManager) { - dataSourceFactory = new DataSourceFactory(okHttpJsonApiClient, compositeDisposable, sessionManager); + public LeaderboardListViewModel(OkHttpJsonApiClient okHttpJsonApiClient, SessionManager + sessionManager) { + + dataSourceFactory = new DataSourceFactory(okHttpJsonApiClient, + compositeDisposable, sessionManager); initializePaging(); } @@ -42,6 +44,21 @@ public class LeaderboardListViewModel extends ViewModel { } + public void refresh(String duration, String category, int limit, int offset) { + dataSourceFactory.setDuration(duration); + dataSourceFactory.setCategory(category); + dataSourceFactory.setLimit(limit); + dataSourceFactory.setOffset(offset); + dataSourceFactory.getMutableLiveData().getValue().invalidate(); + } + + public void setParams(String duration, String category, int limit, int offset) { + dataSourceFactory.setDuration(duration); + dataSourceFactory.setCategory(category); + dataSourceFactory.setLimit(limit); + dataSourceFactory.setOffset(offset); + } + public LiveData getProgressLoadStatus() { return progressLoadStatus; } diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/ViewModelFactory.java b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/ViewModelFactory.java index cabf686c2..9d98ea117 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/ViewModelFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/ViewModelFactory.java @@ -12,6 +12,7 @@ public class ViewModelFactory implements ViewModelProvider.Factory { private OkHttpJsonApiClient okHttpJsonApiClient; private SessionManager sessionManager; + @Inject public ViewModelFactory(OkHttpJsonApiClient okHttpJsonApiClient, SessionManager sessionManager) { this.okHttpJsonApiClient = okHttpJsonApiClient; diff --git a/app/src/main/res/layout/fragment_leaderboard.xml b/app/src/main/res/layout/fragment_leaderboard.xml index 9f35a2f06..6b5145bc2 100644 --- a/app/src/main/res/layout/fragment_leaderboard.xml +++ b/app/src/main/res/layout/fragment_leaderboard.xml @@ -4,12 +4,44 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="match_parent"> + + + + + + + + + android:layout_height="0dp" + android:layout_marginTop="10dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/filters" /> 1 0 + + + @string/leaderboard_upload + @string/leaderboard_used + @string/leaderboard_nearby + + + + upload + used + nearby + + + + @string/leaderboard_weekly + @string/leaderboard_yearly + @string/leaderboard_all_time + + + + weekly + yearly + all_time + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f73c0f73b..e29ae70f4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -666,4 +666,10 @@ Upload your first media by tapping on the add button. Avatar Set Successfully Error setting new avatar, please try again Set as avatar + Yearly + Weekly + All Time + Upload + Nearby + Used