From 660e61694eacee240d10d1f27848f8935823a61e Mon Sep 17 00:00:00 2001 From: Palmarino DiMarco Date: Sun, 28 Apr 2024 16:14:13 -0400 Subject: [PATCH] Overhauled the Top X system described in Ticket 39. Filtering a Leaderboard by the Top X no longer calls a refresh; it also filters based on Rank rather than Ranking, meaning that if there are three users tied for 25th place and we want the top 25, all three 25th place Rankings will now be shown. Expanded the test data to make demoing easier. --- .../ui/leaderboard/LeaderboardActivity.java | 18 ++- .../leaderboard/LeaderboardPagerAdapter.java | 105 ++++++++++++++++-- .../LeaderboardAdapter.java | 36 +++++- .../LeaderboardFragment.java | 47 +------- 4 files changed, 137 insertions(+), 69 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/ui/leaderboard/LeaderboardActivity.java b/src/main/java/de/dennisguse/opentracks/ui/leaderboard/LeaderboardActivity.java index 8374fdf1b..5fc275c63 100644 --- a/src/main/java/de/dennisguse/opentracks/ui/leaderboard/LeaderboardActivity.java +++ b/src/main/java/de/dennisguse/opentracks/ui/leaderboard/LeaderboardActivity.java @@ -20,8 +20,6 @@ public class LeaderboardActivity extends AppCompatActivity { - private int numberOfUsers = 10; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -72,26 +70,26 @@ public void onPageScrollStateChanged(int state) { backButton.setOnClickListener(v -> onBackPressed()); + // The default number of ranks shown is 10. + // Please check the LeaderboardAdapter's fields if you wish to change the default. tenButton.setBackgroundColor(optionSelectedColor); + tenButton.setOnClickListener(v -> { - numberOfUsers = 10; - leaderboardPagerAdapter.setNumberOfUsers(numberOfUsers); + leaderboardPagerAdapter.changeNumberOfDisplayedRanks(10); findViewById(R.id.btnTen).setBackgroundColor(optionSelectedColor); findViewById(R.id.btnTwentyFive).setBackgroundColor(optionAvailableColor); findViewById(R.id.btnAll).setBackgroundColor(optionAvailableColor); }); twentyFiveButton.setOnClickListener(v -> { - numberOfUsers = 25; - leaderboardPagerAdapter.setNumberOfUsers(numberOfUsers); + leaderboardPagerAdapter.changeNumberOfDisplayedRanks(25); findViewById(R.id.btnTen).setBackgroundColor(optionAvailableColor); findViewById(R.id.btnTwentyFive).setBackgroundColor(optionSelectedColor); findViewById(R.id.btnAll).setBackgroundColor(optionAvailableColor); }); allButton.setOnClickListener(v -> { - numberOfUsers = 0; - leaderboardPagerAdapter.setNumberOfUsers(numberOfUsers); + leaderboardPagerAdapter.changeNumberOfDisplayedRanks(0); findViewById(R.id.btnTen).setBackgroundColor(optionAvailableColor); findViewById(R.id.btnTwentyFive).setBackgroundColor(optionAvailableColor); findViewById(R.id.btnAll).setBackgroundColor(optionSelectedColor); @@ -130,13 +128,13 @@ public void onNothingSelected(AdapterView parent) { everyoneButton.setBackgroundColor(optionSelectedColor); averageButton.setOnClickListener(v -> { - leaderboardPagerAdapter.setCurrentLeaderboardType(LeaderboardFragment.LeaderboardType.Average); + leaderboardPagerAdapter.setCurrentLeaderboardType(LeaderboardFragment.LeaderboardType.AVERAGE); averageButton.setBackgroundColor(optionSelectedColor); bestButton.setBackgroundColor(optionAvailableColor); }); bestButton.setOnClickListener(v -> { - leaderboardPagerAdapter.setCurrentLeaderboardType(LeaderboardFragment.LeaderboardType.Best); + leaderboardPagerAdapter.setCurrentLeaderboardType(LeaderboardFragment.LeaderboardType.BEST); averageButton.setBackgroundColor(optionAvailableColor); bestButton.setBackgroundColor(optionSelectedColor); }); diff --git a/src/main/java/de/dennisguse/opentracks/ui/leaderboard/LeaderboardPagerAdapter.java b/src/main/java/de/dennisguse/opentracks/ui/leaderboard/LeaderboardPagerAdapter.java index 9b74a5df0..6cc292742 100644 --- a/src/main/java/de/dennisguse/opentracks/ui/leaderboard/LeaderboardPagerAdapter.java +++ b/src/main/java/de/dennisguse/opentracks/ui/leaderboard/LeaderboardPagerAdapter.java @@ -12,6 +12,7 @@ import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.ui.leaderboard.leaderboardFragment.AverageMovingSpeedLeaderboardFragment; +import de.dennisguse.opentracks.ui.leaderboard.leaderboardFragment.LeaderboardAdapter; import de.dennisguse.opentracks.ui.leaderboard.leaderboardFragment.MaxSpeedLeaderboardFragment; import de.dennisguse.opentracks.ui.leaderboard.leaderboardFragment.DistanceLeaderboardFragment; import de.dennisguse.opentracks.ui.leaderboard.leaderboardFragment.LeaderboardFragment; @@ -34,7 +35,7 @@ public LeaderboardPagerAdapter(FragmentManager fm) { averageMovingSpeedLeaderboardFragment = new AverageMovingSpeedLeaderboardFragment(); currentLeaderboardFragment = movingTimeLeaderboardFragment; - currentLeaderboardType = LeaderboardFragment.LeaderboardType.Average; + currentLeaderboardType = LeaderboardFragment.LeaderboardType.AVERAGE; refreshLeaderboardFragmentData(); } @@ -122,13 +123,11 @@ public void refreshLeaderboardFragmentData() { currentLeaderboardFragment.setDisplayedRankingList(currentLeaderboardType); } - public void setNumberOfUsers(int numberOfUsers) { - movingTimeLeaderboardFragment.setDisplayAmount(numberOfUsers); - distanceLeaderboardFragment.setDisplayAmount(numberOfUsers); - maxSpeedLeaderboardFragment.setDisplayAmount(numberOfUsers); - averageMovingSpeedLeaderboardFragment.setDisplayAmount(numberOfUsers); - refreshLeaderboardFragmentData(); + public void changeNumberOfDisplayedRanks(int numberOfUsers) { + LeaderboardAdapter.setNumberOfRanksToDisplay(numberOfUsers); + currentLeaderboardFragment.setDisplayedRankingList(currentLeaderboardType); } + private List readLatestLeaderboardData() { // This is where we get the data from the database for the runs that will be in the leaderboard. List testData = new ArrayList(); @@ -157,7 +156,7 @@ private List readLatestLeaderboardData() { stats.setMovingTime(Duration.ofHours(1)); testData.add(new PlaceHolderTrackUser("User Two", "Steamboat Springs", true, stats)); - if (altTestData) { + if (!altTestData) { stats = new TrackStatistics(); stats.setTotalDistance(new Distance(1000)); stats.setMaxSpeed(new Speed(20)); @@ -177,7 +176,7 @@ private List readLatestLeaderboardData() { stats.setMovingTime(Duration.ofHours(6)); testData.add(new PlaceHolderTrackUser("User Four","Steamboat Springs", true, stats)); - if (altTestData) { + if (!altTestData) { stats = new TrackStatistics(); stats.setTotalDistance(new Distance(2500)); stats.setMaxSpeed(new Speed(55)); @@ -191,7 +190,7 @@ private List readLatestLeaderboardData() { stats.setMovingTime(Duration.ofHours(4)); testData.add(new PlaceHolderTrackUser("User Five","North California CA", true, stats)); - if (altTestData) { + if (!altTestData) { stats = new TrackStatistics(); stats.setTotalDistance(new Distance(1000)); stats.setMaxSpeed(new Speed(50)); @@ -223,7 +222,7 @@ private List readLatestLeaderboardData() { stats.setMovingTime(Duration.ofMinutes(30)); testData.add(new PlaceHolderTrackUser("User Nine", "North California CA", true, stats)); - if (altTestData) { + if (!altTestData) { stats = new TrackStatistics(); stats.setTotalDistance(new Distance(154)); stats.setMaxSpeed(new Speed(150)); @@ -241,6 +240,90 @@ private List readLatestLeaderboardData() { stats.setMaxSpeed(new Speed(120)); stats.setMovingTime(Duration.ofMinutes(34)); testData.add(new PlaceHolderTrackUser("User Twelve","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(154)); + stats.setMaxSpeed(new Speed(150)); + stats.setMovingTime(Duration.ofMinutes(59)); + testData.add(new PlaceHolderTrackUser("User Thirteen","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(134)); + stats.setMaxSpeed(new Speed(130)); + stats.setMovingTime(Duration.ofMinutes(59)); + testData.add(new PlaceHolderTrackUser("User Fourteen","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(159)); + stats.setMaxSpeed(new Speed(120)); + stats.setMovingTime(Duration.ofMinutes(34)); + testData.add(new PlaceHolderTrackUser("User Fifteen","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(154)); + stats.setMaxSpeed(new Speed(150)); + stats.setMovingTime(Duration.ofMinutes(59)); + testData.add(new PlaceHolderTrackUser("User Sixteen","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(134)); + stats.setMaxSpeed(new Speed(130)); + stats.setMovingTime(Duration.ofMinutes(59)); + testData.add(new PlaceHolderTrackUser("User Seventeen","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(159)); + stats.setMaxSpeed(new Speed(120)); + stats.setMovingTime(Duration.ofMinutes(34)); + testData.add(new PlaceHolderTrackUser("User Eighteen","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(154)); + stats.setMaxSpeed(new Speed(150)); + stats.setMovingTime(Duration.ofMinutes(59)); + testData.add(new PlaceHolderTrackUser("User Nineteen","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(134)); + stats.setMaxSpeed(new Speed(130)); + stats.setMovingTime(Duration.ofMinutes(59)); + testData.add(new PlaceHolderTrackUser("User Twenty","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(159)); + stats.setMaxSpeed(new Speed(120)); + stats.setMovingTime(Duration.ofMinutes(34)); + testData.add(new PlaceHolderTrackUser("User Twenty One","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(154)); + stats.setMaxSpeed(new Speed(150)); + stats.setMovingTime(Duration.ofMinutes(59)); + testData.add(new PlaceHolderTrackUser("User Twenty Two","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(134)); + stats.setMaxSpeed(new Speed(130)); + stats.setMovingTime(Duration.ofMinutes(59)); + testData.add(new PlaceHolderTrackUser("User Twenty Three","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(159)); + stats.setMaxSpeed(new Speed(120)); + stats.setMovingTime(Duration.ofMinutes(34)); + testData.add(new PlaceHolderTrackUser("User Twenty Four","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(154)); + stats.setMaxSpeed(new Speed(150)); + stats.setMovingTime(Duration.ofMinutes(59)); + testData.add(new PlaceHolderTrackUser("User Twenty Five","Steamboat Springs", true, stats)); + + stats = new TrackStatistics(); + stats.setTotalDistance(new Distance(134)); + stats.setMaxSpeed(new Speed(130)); + stats.setMovingTime(Duration.ofMinutes(59)); + testData.add(new PlaceHolderTrackUser("User Twenty Six","Steamboat Springs", true, stats)); } stats = new TrackStatistics(); diff --git a/src/main/java/de/dennisguse/opentracks/ui/leaderboard/leaderboardFragment/LeaderboardAdapter.java b/src/main/java/de/dennisguse/opentracks/ui/leaderboard/leaderboardFragment/LeaderboardAdapter.java index 6544dd209..3c6f2360c 100644 --- a/src/main/java/de/dennisguse/opentracks/ui/leaderboard/leaderboardFragment/LeaderboardAdapter.java +++ b/src/main/java/de/dennisguse/opentracks/ui/leaderboard/leaderboardFragment/LeaderboardAdapter.java @@ -7,6 +7,8 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; import java.util.List; import de.dennisguse.opentracks.R; @@ -14,11 +16,16 @@ public class LeaderboardAdapter extends RecyclerView.Adapter { private List displayedRankingList; + private static int numberOfRanksToDisplay = 10; public LeaderboardAdapter(List displayedRankingList) { this.displayedRankingList = displayedRankingList; } + public static void setNumberOfRanksToDisplay(int numberOfRanks) { + numberOfRanksToDisplay = numberOfRanks; + } + @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { @@ -40,17 +47,36 @@ public int getItemCount() { return displayedRankingList.size(); } - public void setDisplayedRankingList(List displayedRankingList) { - // This if statement is just here to improve efficiency if the user switched LeaderboardFragments but not LeaderboardType - if (this.displayedRankingList == displayedRankingList) + public void setDisplayedRankingList(List rankingListToDisplay) { + List filteredRankingListToDisplay = displayOnlySetAmountOfRanks(rankingListToDisplay); + + // This if statement is just here to improve efficiency if the user switched LeaderboardFragments but not anything else + if (this.displayedRankingList == filteredRankingListToDisplay) return; - this.displayedRankingList = displayedRankingList; + this.displayedRankingList = filteredRankingListToDisplay; - // Since the rankingList could have been remade from the ground up, we have to call notifyDataSetChanged(); + // Since the displayedRankingList could have been remade from the ground up, we have to call notifyDataSetChanged(); notifyDataSetChanged(); } + private List displayOnlySetAmountOfRanks(List rankingListToDisplay) { + // Display all the ranks if all ranks are needed or the largest-numbered rank is less than the number of ranks we wish to display + if (numberOfRanksToDisplay <= 0 || rankingListToDisplay.get(rankingListToDisplay.size() - 1).getRank() <= numberOfRanksToDisplay) + return rankingListToDisplay; + + // The Top X restriction is based on the Rank, not the User's Ranking. + // If three Users are tied for 25th and we want the Top 25, the three 25th place Rankings should all be shown. + List newRankingListToDisplay = new ArrayList<>(); + for (int i = 0; i < rankingListToDisplay.size(); i++) { + Ranking nextRanking = rankingListToDisplay.get(i); + if (nextRanking.getRank() > numberOfRanksToDisplay) + break; + newRankingListToDisplay.add(nextRanking); + } + return newRankingListToDisplay; + } + static class ViewHolder extends RecyclerView.ViewHolder { ImageView avatar; TextView usernameText; diff --git a/src/main/java/de/dennisguse/opentracks/ui/leaderboard/leaderboardFragment/LeaderboardFragment.java b/src/main/java/de/dennisguse/opentracks/ui/leaderboard/leaderboardFragment/LeaderboardFragment.java index d81649cab..af8f78853 100644 --- a/src/main/java/de/dennisguse/opentracks/ui/leaderboard/leaderboardFragment/LeaderboardFragment.java +++ b/src/main/java/de/dennisguse/opentracks/ui/leaderboard/leaderboardFragment/LeaderboardFragment.java @@ -26,16 +26,13 @@ public abstract class LeaderboardFragment extends Fragment { private List averageRankingList; private List bestRankingList; - private int displayAmount = 0; - public LeaderboardFragment() { leaderboardAdapter = new LeaderboardAdapter(new ArrayList<>()); - } public enum LeaderboardType { - Average, - Best; + AVERAGE, + BEST; } @Nullable @@ -53,51 +50,15 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c protected abstract List calculateLatestAverageRankingsData(List latestLeaderboardData); protected abstract List calculateLatestBestRankingsData(List latestLeaderboardData); - public void setDisplayAmount(int newDisplayAmount){ - this.displayAmount = newDisplayAmount; - } - - public void displayOnlySetAmount() { - // Display all the ranks - if (this.displayAmount <= 0) - return; - - int currentDisplayAmount = this.displayAmount; - - // Average Ranking List - // In case there is less rankings then the requested amount - if (this.averageRankingList.size() < currentDisplayAmount) - currentDisplayAmount = this.averageRankingList.size(); - - List newAverageRankingList = new ArrayList<>(); - for (int i = 0; i < currentDisplayAmount; i++) { - newAverageRankingList.add(this.averageRankingList.get(i)); - } - this.averageRankingList = newAverageRankingList; - - // Best Ranking List - // In case there is less rankings then the requested amount - currentDisplayAmount = this.displayAmount; - if (this.bestRankingList.size() < currentDisplayAmount) - currentDisplayAmount = this.bestRankingList.size(); - - List newBestRankingList = new ArrayList<>(); - for (int i = 0; i < currentDisplayAmount; i++) { - newBestRankingList.add(this.bestRankingList.get(i)); - } - this.bestRankingList = newBestRankingList; - } public void updateRankingLists(List latestLeaderboardData) { this.averageRankingList = calculateLatestAverageRankingsData(latestLeaderboardData); this.bestRankingList = calculateLatestBestRankingsData(latestLeaderboardData); - - displayOnlySetAmount(); } public void setDisplayedRankingList(LeaderboardType leaderboardType) { - if (leaderboardType == LeaderboardType.Average) + if (leaderboardType == LeaderboardType.AVERAGE) leaderboardAdapter.setDisplayedRankingList(averageRankingList); - else if (leaderboardType == LeaderboardType.Best) + else if (leaderboardType == LeaderboardType.BEST) leaderboardAdapter.setDisplayedRankingList(bestRankingList); }