Skip to content

Commit

Permalink
Merge pull request #86 from Picard4/Group-14-Palmar
Browse files Browse the repository at this point in the history
Revised the code and added documentation to make it easier to underst…
  • Loading branch information
Picard4 authored Apr 29, 2024
2 parents 9cf57f5 + 70bb082 commit 0361d96
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,21 @@ public void onPageScrollStateChanged(int state) {
tenButton.setBackgroundColor(optionSelectedColor);

tenButton.setOnClickListener(v -> {
leaderboardPagerAdapter.changeNumberOfDisplayedRanks(10);
leaderboardPagerAdapter.changeLargestNumberRankToDisplay(10);
findViewById(R.id.btnTen).setBackgroundColor(optionSelectedColor);
findViewById(R.id.btnTwentyFive).setBackgroundColor(optionAvailableColor);
findViewById(R.id.btnAll).setBackgroundColor(optionAvailableColor);
});

twentyFiveButton.setOnClickListener(v -> {
leaderboardPagerAdapter.changeNumberOfDisplayedRanks(25);
leaderboardPagerAdapter.changeLargestNumberRankToDisplay(25);
findViewById(R.id.btnTen).setBackgroundColor(optionAvailableColor);
findViewById(R.id.btnTwentyFive).setBackgroundColor(optionSelectedColor);
findViewById(R.id.btnAll).setBackgroundColor(optionAvailableColor);
});

allButton.setOnClickListener(v -> {
leaderboardPagerAdapter.changeNumberOfDisplayedRanks(0);
leaderboardPagerAdapter.changeLargestNumberRankToDisplay(0);
findViewById(R.id.btnTen).setBackgroundColor(optionAvailableColor);
findViewById(R.id.btnTwentyFive).setBackgroundColor(optionAvailableColor);
findViewById(R.id.btnAll).setBackgroundColor(optionSelectedColor);
Expand Down Expand Up @@ -128,13 +128,13 @@ public void onNothingSelected(AdapterView<?> parent) {
everyoneButton.setBackgroundColor(optionSelectedColor);

averageButton.setOnClickListener(v -> {
leaderboardPagerAdapter.setCurrentLeaderboardType(LeaderboardFragment.LeaderboardType.AVERAGE);
leaderboardPagerAdapter.setCurrentLeaderboardRankingListType(LeaderboardFragment.RankingListType.AVERAGE);
averageButton.setBackgroundColor(optionSelectedColor);
bestButton.setBackgroundColor(optionAvailableColor);
});

bestButton.setOnClickListener(v -> {
leaderboardPagerAdapter.setCurrentLeaderboardType(LeaderboardFragment.LeaderboardType.BEST);
leaderboardPagerAdapter.setCurrentLeaderboardRankingListType(LeaderboardFragment.RankingListType.BEST);
averageButton.setBackgroundColor(optionAvailableColor);
bestButton.setBackgroundColor(optionSelectedColor);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ public class LeaderboardPagerAdapter extends FragmentPagerAdapter {
private MaxSpeedLeaderboardFragment maxSpeedLeaderboardFragment;
private AverageMovingSpeedLeaderboardFragment averageMovingSpeedLeaderboardFragment;
private LeaderboardFragment currentLeaderboardFragment;
private LeaderboardFragment.LeaderboardType currentLeaderboardType;
private LeaderboardFragment.RankingListType currentRankingListType;

// Please remove this boolean once the Leaderboard is able to use read data
private boolean altTestData;

public LeaderboardPagerAdapter(FragmentManager fm) {
Expand All @@ -35,11 +37,11 @@ public LeaderboardPagerAdapter(FragmentManager fm) {
averageMovingSpeedLeaderboardFragment = new AverageMovingSpeedLeaderboardFragment();

currentLeaderboardFragment = movingTimeLeaderboardFragment;
currentLeaderboardType = LeaderboardFragment.LeaderboardType.AVERAGE;
currentRankingListType = LeaderboardFragment.RankingListType.AVERAGE;
refreshLeaderboardFragmentData();
}

public enum LeaderboardType {
public enum LeaderboardFragmentType {
MOVING_TIME(0, "Moving Time"),
DISTANCE(1, "Distance"),
MAX_SPEED(2, "Max Speed"),
Expand All @@ -49,7 +51,7 @@ public enum LeaderboardType {
private final int value;
private final String title;

private LeaderboardType(int value, String title) {
private LeaderboardFragmentType(int value, String title) {
this.value = value;
this.title = title;
}
Expand All @@ -63,73 +65,92 @@ public String getTitle() {
}
}

/**
* Sets the currentLeaderboardFragment based on the sent position and
* keeps the Ranking list it is meant to show on-screen up to date (without refreshing).
* @param position The position corresponding to the desired LeaderboardFragment child class you want to switch to.
*/
public void setCurrentLeaderboardFragment(int position) {
if (position == LeaderboardPagerAdapter.LeaderboardType.MOVING_TIME.value)
if (position == LeaderboardFragmentType.MOVING_TIME.value)
currentLeaderboardFragment = movingTimeLeaderboardFragment;
else if (position == LeaderboardPagerAdapter.LeaderboardType.DISTANCE.value)
else if (position == LeaderboardFragmentType.DISTANCE.value)
currentLeaderboardFragment = distanceLeaderboardFragment;
else if (position == LeaderboardPagerAdapter.LeaderboardType.MAX_SPEED.value)
else if (position == LeaderboardFragmentType.MAX_SPEED.value)
currentLeaderboardFragment = maxSpeedLeaderboardFragment;
else if (position == LeaderboardPagerAdapter.LeaderboardType.AVERAGE_MOVING_SPEED.value)
else if (position == LeaderboardFragmentType.AVERAGE_MOVING_SPEED.value)
currentLeaderboardFragment = averageMovingSpeedLeaderboardFragment;
currentLeaderboardFragment.setDisplayedRankingList(currentLeaderboardType);
currentLeaderboardFragment.setDisplayedRankingList(currentRankingListType);
}

@Override
public int getCount() {
return LeaderboardPagerAdapter.LeaderboardType.values().length;
return LeaderboardFragmentType.values().length;
}

@Override
public Fragment getItem(int position) {
// Return the appropriate Fragment for each tab position
if (position == LeaderboardPagerAdapter.LeaderboardType.MOVING_TIME.value)
// Return the appropriate LeaderboardFragment for each tab position
if (position == LeaderboardFragmentType.MOVING_TIME.value)
return movingTimeLeaderboardFragment;
else if (position == LeaderboardPagerAdapter.LeaderboardType.DISTANCE.value)
else if (position == LeaderboardFragmentType.DISTANCE.value)
return distanceLeaderboardFragment;
else if (position == LeaderboardPagerAdapter.LeaderboardType.MAX_SPEED.value)
else if (position == LeaderboardFragmentType.MAX_SPEED.value)
return maxSpeedLeaderboardFragment;
else if (position == LeaderboardPagerAdapter.LeaderboardType.AVERAGE_MOVING_SPEED.value)
else if (position == LeaderboardFragmentType.AVERAGE_MOVING_SPEED.value)
return averageMovingSpeedLeaderboardFragment;
return currentLeaderboardFragment;
}

@Override
public CharSequence getPageTitle(int position) {
if (position == LeaderboardPagerAdapter.LeaderboardType.MOVING_TIME.value)
return LeaderboardPagerAdapter.LeaderboardType.MOVING_TIME.getTitle();
else if (position == LeaderboardPagerAdapter.LeaderboardType.DISTANCE.value)
return LeaderboardPagerAdapter.LeaderboardType.DISTANCE.getTitle();
else if (position == LeaderboardPagerAdapter.LeaderboardType.MAX_SPEED.value)
return LeaderboardPagerAdapter.LeaderboardType.MAX_SPEED.getTitle();
else if (position == LeaderboardPagerAdapter.LeaderboardType.AVERAGE_MOVING_SPEED.value)
return LeaderboardPagerAdapter.LeaderboardType.AVERAGE_MOVING_SPEED.getTitle();
if (position == LeaderboardFragmentType.MOVING_TIME.value)
return LeaderboardFragmentType.MOVING_TIME.getTitle();
else if (position == LeaderboardFragmentType.DISTANCE.value)
return LeaderboardFragmentType.DISTANCE.getTitle();
else if (position == LeaderboardFragmentType.MAX_SPEED.value)
return LeaderboardFragmentType.MAX_SPEED.getTitle();
else if (position == LeaderboardFragmentType.AVERAGE_MOVING_SPEED.value)
return LeaderboardFragmentType.AVERAGE_MOVING_SPEED.getTitle();
return "";
}

public void setCurrentLeaderboardType(LeaderboardFragment.LeaderboardType leaderboardType) {
if (leaderboardType == currentLeaderboardType)
/**
* Swaps the Ranking list that the currentLeaderboardFragment is showing in the GUI based on the sent rankingListType.
* @param rankingListType The type of Ranking list that the currentLeaderboardFragment should show in the GUI.
*/
public void setCurrentLeaderboardRankingListType(LeaderboardFragment.RankingListType rankingListType) {
if (rankingListType == currentRankingListType)
return;
currentLeaderboardType = leaderboardType;
currentLeaderboardFragment.setDisplayedRankingList(currentLeaderboardType);
currentRankingListType = rankingListType;
currentLeaderboardFragment.setDisplayedRankingList(currentRankingListType);
}

/**
* Refreshes the Ranking list data in every LeaderboardFragment in this LeaderboardPageAdapter based off the latest available data.
* Ensures that the currentLeaderboardFragment is still showing the Rankings data it is meant to show as per the user's specifications.
*/
public void refreshLeaderboardFragmentData() {
List<PlaceHolderTrackUser> latestLeaderboardData = readLatestLeaderboardData();
movingTimeLeaderboardFragment.updateRankingLists(latestLeaderboardData);
distanceLeaderboardFragment.updateRankingLists(latestLeaderboardData);
maxSpeedLeaderboardFragment.updateRankingLists(latestLeaderboardData);
averageMovingSpeedLeaderboardFragment.updateRankingLists(latestLeaderboardData);
currentLeaderboardFragment.setDisplayedRankingList(currentLeaderboardType);
currentLeaderboardFragment.setDisplayedRankingList(currentRankingListType);
}

public void changeNumberOfDisplayedRanks(int numberOfUsers) {
LeaderboardAdapter.setNumberOfRanksToDisplay(numberOfUsers);
currentLeaderboardFragment.setDisplayedRankingList(currentLeaderboardType);
/**
* Sets the largest-number Rank that the LeaderboardAdapters can display and
* updates the Ranking list that the currentLeaderboardFragment is showing on the GUI accordingly.
* @param largestNumberRank The largest-number rank that can be shown on-screen by any LeaderboardAdapter.
*/
public void changeLargestNumberRankToDisplay(int largestNumberRank) {
LeaderboardAdapter.setLargestNumberRankToDisplay(largestNumberRank);
currentLeaderboardFragment.setDisplayedRankingList(currentRankingListType);
}

private List<PlaceHolderTrackUser> readLatestLeaderboardData() {
// This is where we get the data from the database for the runs that will be in the leaderboard.
// This is where we get the data "from the database" for the runs that will be in the leaderboard.
// This method can be removed once the leaderboards can use real data.
List<PlaceHolderTrackUser> testData = new ArrayList();

TrackStatistics stats = new TrackStatistics();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@

public class LeaderboardAdapter extends RecyclerView.Adapter<LeaderboardAdapter.ViewHolder> {
private List<Ranking> displayedRankingList;
private static int numberOfRanksToDisplay = 10;
private static int largestNumberRankToDisplay = 10;

public LeaderboardAdapter(List<Ranking> displayedRankingList) {
this.displayedRankingList = displayedRankingList;
}

public static void setNumberOfRanksToDisplay(int numberOfRanks) {
numberOfRanksToDisplay = numberOfRanks;
/**
* Sets the largestNumberRankToDisplay, but does not change the leaderboard that is shown on-screen;
* you need to call setDisplayedRankingList(List<Ranking> rankingListToDisplay) for that.
* @param largestNumberRank The largest-number rank that can be displayed in the on-screen leaderboard.
*/
public static void setLargestNumberRankToDisplay(int largestNumberRank) {
largestNumberRankToDisplay = largestNumberRank;
}

@NonNull
Expand All @@ -47,6 +52,11 @@ public int getItemCount() {
return displayedRankingList.size();
}

/**
* Changes the list of Rankings that is shown on-screen to the rankingListToDisplay,
* or a filtered version of it if some rankings are restricted from being displayed.
* @param rankingListToDisplay The list of Rankings to be filtered based on the largest-numbered rank we want to show, and displayed in the GUI.
*/
public void setDisplayedRankingList(List<Ranking> rankingListToDisplay) {
List<Ranking> filteredRankingListToDisplay = displayOnlySetAmountOfRanks(rankingListToDisplay);

Expand All @@ -61,22 +71,27 @@ public void setDisplayedRankingList(List<Ranking> rankingListToDisplay) {
}

private List<Ranking> displayOnlySetAmountOfRanks(List<Ranking> 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)
// Display all the ranks if all ranks are needed or
// the largest-numbered rank is less than the largest-numbered rank we wish to display.
if (largestNumberRankToDisplay <= 0 ||
rankingListToDisplay.get(rankingListToDisplay.size() - 1).getRank() <= largestNumberRankToDisplay)
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<Ranking> newRankingListToDisplay = new ArrayList<>();
for (int i = 0; i < rankingListToDisplay.size(); i++) {
Ranking nextRanking = rankingListToDisplay.get(i);
if (nextRanking.getRank() > numberOfRanksToDisplay)
if (nextRanking.getRank() > largestNumberRankToDisplay)
break;
newRankingListToDisplay.add(nextRanking);
}
return newRankingListToDisplay;
}

/**
* The class that Rankings are bound to so that they can be shown in the GUI.
*/
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView avatar;
TextView usernameText;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import de.dennisguse.opentracks.data.models.Ranking;
import de.dennisguse.opentracks.ui.leaderboard.LeaderboardPagerAdapter;

/**
* The base class for every Leaderboard supported by OpenTracks.
*/
public abstract class LeaderboardFragment extends Fragment {

private static DecimalFormat scoreDecimalFormat;
Expand All @@ -30,7 +33,7 @@ public LeaderboardFragment() {
leaderboardAdapter = new LeaderboardAdapter(new ArrayList<>());
}

public enum LeaderboardType {
public enum RankingListType {
AVERAGE,
BEST;
}
Expand All @@ -47,18 +50,41 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
return view;
}

/**
* Reads the sent latestLeaderboardData to rank every user in the latestLeaderboardData by the average of their scores across their provided tracks
* that they are willing to share with the public.
* What exactly the "score" is depends on the child class that implements this method.
* @param latestLeaderboardData The leaderboard data that this LeaderboardFragment's Average Ranking list will be based on.
* @return A list of Average Rankings calculated from the sent latestLeaderboardData, in order from first to last.
*/
protected abstract List<Ranking> calculateLatestAverageRankingsData(List<LeaderboardPagerAdapter.PlaceHolderTrackUser> latestLeaderboardData);

/**
* Reads the sent latestLeaderboardData to rank every user in the latestLeaderboardData by their best score across their provided tracks
* that they are willing to share with the public.
* What exactly the "score" is depends on the child class that implements this method.
* @param latestLeaderboardData The leaderboard data that this LeaderboardFragment's Best Ranking list will be based on.
* @return A list of Best Rankings calculated from the sent latestLeaderboardData, in order from first to last.
*/
protected abstract List<Ranking> calculateLatestBestRankingsData(List<LeaderboardPagerAdapter.PlaceHolderTrackUser> latestLeaderboardData);

/**
* Updates every Ranking list stored in this LeaderboardFragment using the sent latestLeaderboardData.
* @param latestLeaderboardData The data that every Ranking list stored in this LeaderboardFragment will be based on.
*/
public void updateRankingLists(List<LeaderboardPagerAdapter.PlaceHolderTrackUser> latestLeaderboardData) {
this.averageRankingList = calculateLatestAverageRankingsData(latestLeaderboardData);
this.bestRankingList = calculateLatestBestRankingsData(latestLeaderboardData);
}

public void setDisplayedRankingList(LeaderboardType leaderboardType) {
if (leaderboardType == LeaderboardType.AVERAGE)
/**
* Changes the Ranking list that this LeaderboardFragment is displaying in the GUI.
* @param rankingListType The type of Ranking list that this LeaderboardFragment ought to display.
*/
public void setDisplayedRankingList(RankingListType rankingListType) {
if (rankingListType == RankingListType.AVERAGE)
leaderboardAdapter.setDisplayedRankingList(averageRankingList);
else if (leaderboardType == LeaderboardType.BEST)
else if (rankingListType == RankingListType.BEST)
leaderboardAdapter.setDisplayedRankingList(bestRankingList);
}

Expand All @@ -68,6 +94,10 @@ protected static DecimalFormat getScoreDecimalFormat() {
return scoreDecimalFormat;
}

/**
* A class that assists with calculating average scores by allowing the statistic that is being
* tracked as a "score" to be summed within the PlaceHolderTrackUser.
*/
protected class SummedStatTrackUser {
private LeaderboardPagerAdapter.PlaceHolderTrackUser placeHolderTrackUser;
private int sumFactorCount;
Expand Down

0 comments on commit 0361d96

Please sign in to comment.