Skip to content

Commit

Permalink
Release version 4.3.1
Browse files Browse the repository at this point in the history
  • Loading branch information
matco committed Feb 15, 2024
2 parents dba1f63 + 79f5582 commit cc09d27
Show file tree
Hide file tree
Showing 16 changed files with 261 additions and 108 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 4.3.1
- Fix and update custom fields in the Garmin Activity to allow much higher total scores
- Fix duplicated scores in the last lap of the Garmin Activity in endless mode
- Determine the winner properly when a standard match is ended prematurely

## 4.3.0
- Add support for Vivoactive 5
- Drop support for Vivoactive 3
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ It is available on the Garmin store here:

Here are the best features of the app:
- Choose the type of match (double or single)
- Choose a number of sets
- Choose a number of sets or play in endless mode
- Choose which player has service (you, your opponent, or random)
- Display the boundaries of the court according to the type of match
- Show the service corners and your position
- Display match duration
- Declares the winner of the match
- Use the menu (long press on "Up") to reset scores and start a new match
- Use the menu (long press on "Up") to reset or end the match anytime
- Save matches as Garmin Connect activities

## Installation
Expand Down
14 changes: 10 additions & 4 deletions source/model/BetterTest.mc
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,18 @@ module BetterTest {
assertNotSame(condition, null, message);
}

function assertEqual(actual as Object, expected as Object?, message as String) as Void {
Test.assertEqualMessage(actual, expected, message + " (expected [" + expected + "], actual [" + actual + "])");
function assertEqual(actual as Object?, expected as Object?, message as String) as Void {
if(actual == null) {
fail(message);
}
Test.assertEqualMessage(actual as Object, expected, message + " (expected [" + expected + "], actual [" + actual + "])");
}

function assertNotEqual(actual as Object, expected as Object?, message as String) as Void {
Test.assertNotEqualMessage(actual, expected, message);
function assertNotEqual(actual as Object?, expected as Object?, message as String) as Void {
if(actual == null) {
fail(message);
}
Test.assertNotEqualMessage(actual as Object, expected, message);
}

function assertSame(actual as Object?, expected as Object?, message as String) as Void {
Expand Down
5 changes: 3 additions & 2 deletions source/model/Helpers.mc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ using Toybox.Time.Gregorian;
module Helpers {

//TODO use Lang.format instead
function formatString(string as String, parameters as Dictionary<String, String?>) as String {
function formatString(string as String, parameters as Dictionary<String, Object>) as String {
var result = string;
var parameters_keys = parameters.keys();
//compiler does no accept for loop without incrementation phase
Expand All @@ -17,7 +17,8 @@ module Helpers {
if(parameter_index != null) {
var result_before = parameter_index > 0 ? result.substring(0, parameter_index) : "";
var result_after = result.substring(parameter_index + parameter_key.length(), result.length());
result = result_before + parameters[parameters_keys[i]] as String + result_after;
var parameter = parameters[parameters_keys[i]] as Object;
result = result_before + parameter.toString() + result_after;
//compiler does no accept for loop without incrementation phase
//loog again with same parameter until it is no more found in the string
i--;
Expand Down
33 changes: 19 additions & 14 deletions source/model/Match.mc
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ class Match {

//manage activity session
session = ActivityRecording.createSession({:sport => sport, :subSport => sub_sport, :name => WatchUi.loadResource(Rez.Strings.fit_activity_name) as String});
fieldSetPlayer1 = session.createField("set_player_1", SET_WON_PLAYER_1_FIELD_ID, FitContributor.DATA_TYPE_SINT8, {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => WatchUi.loadResource(Rez.Strings.fit_set_unit_label) as String});
fieldSetPlayer2 = session.createField("set_player_2", SET_WON_PLAYER_2_FIELD_ID, FitContributor.DATA_TYPE_SINT8, {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => WatchUi.loadResource(Rez.Strings.fit_set_unit_label) as String});
fieldScorePlayer1 = session.createField("score_player_1", TOTAL_SCORE_PLAYER_1_FIELD_ID, FitContributor.DATA_TYPE_SINT8, {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => WatchUi.loadResource(Rez.Strings.fit_score_unit_label) as String});
fieldScorePlayer2 = session.createField("score_player_2", TOTAL_SCORE_PLAYER_2_FIELD_ID, FitContributor.DATA_TYPE_SINT8, {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => WatchUi.loadResource(Rez.Strings.fit_score_unit_label) as String});
fieldSetScorePlayer1 = session.createField("set_score_player_1", SET_SCORE_PLAYER_1_FIELD_ID, FitContributor.DATA_TYPE_SINT8, {:mesgType => FitContributor.MESG_TYPE_LAP, :units => WatchUi.loadResource(Rez.Strings.fit_score_unit_label) as String});
fieldSetScorePlayer2 = session.createField("set_score_player_2", SET_SCORE_PLAYER_2_FIELD_ID, FitContributor.DATA_TYPE_SINT8, {:mesgType => FitContributor.MESG_TYPE_LAP, :units => WatchUi.loadResource(Rez.Strings.fit_score_unit_label) as String});
fieldSetPlayer1 = session.createField("set_player_1", SET_WON_PLAYER_1_FIELD_ID, FitContributor.DATA_TYPE_UINT8, {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => WatchUi.loadResource(Rez.Strings.fit_set_unit_label) as String});
fieldSetPlayer2 = session.createField("set_player_2", SET_WON_PLAYER_2_FIELD_ID, FitContributor.DATA_TYPE_UINT8, {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => WatchUi.loadResource(Rez.Strings.fit_set_unit_label) as String});
fieldScorePlayer1 = session.createField("score_player_1", TOTAL_SCORE_PLAYER_1_FIELD_ID, FitContributor.DATA_TYPE_UINT16, {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => WatchUi.loadResource(Rez.Strings.fit_score_unit_label) as String});
fieldScorePlayer2 = session.createField("score_player_2", TOTAL_SCORE_PLAYER_2_FIELD_ID, FitContributor.DATA_TYPE_UINT16, {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => WatchUi.loadResource(Rez.Strings.fit_score_unit_label) as String});
fieldSetScorePlayer1 = session.createField("set_score_player_1", SET_SCORE_PLAYER_1_FIELD_ID, FitContributor.DATA_TYPE_UINT8, {:mesgType => FitContributor.MESG_TYPE_LAP, :units => WatchUi.loadResource(Rez.Strings.fit_score_unit_label) as String});
fieldSetScorePlayer2 = session.createField("set_score_player_2", SET_SCORE_PLAYER_2_FIELD_ID, FitContributor.DATA_TYPE_UINT8, {:mesgType => FitContributor.MESG_TYPE_LAP, :units => WatchUi.loadResource(Rez.Strings.fit_score_unit_label) as String});
session.start();

(Application.getApp() as BadmintonApp).getBus().dispatch(new BusEvent(:onMatchBegin, null));
Expand All @@ -128,8 +128,10 @@ class Match {
var you_total_score = getTotalScore(YOU);
var opponent_total_score = getTotalScore(OPPONENT);

//in endless mode, the winner must be determined now
if(isEndless()) {
//in there is no winner yet, the winner must be determined now
//this occurs in endless mode, or when the user ends the match manually
//in standard mode, the winner has already been determined when the last set has been won
if(winner_player == null) {
//determine winner based on sets
if(you_sets_won != opponent_sets_won) {
winner = you_sets_won > opponent_sets_won ? YOU : OPPONENT;
Expand All @@ -138,6 +140,11 @@ class Match {
if(winner == null && you_total_score != opponent_total_score) {
winner = you_total_score > opponent_total_score ? YOU : OPPONENT;
}

//manage activity session
var set = getCurrentSet();
fieldSetScorePlayer1.setData(set.getScore(YOU));
fieldSetScorePlayer2.setData(set.getScore(OPPONENT));
}
else {
winner = winner_player;
Expand Down Expand Up @@ -187,17 +194,15 @@ class Match {
var set = getCurrentSet();
set.score(scorer);

//manage activity session
//remember that the match can be ended anytime (if the user decides to stop it)
//the activity must always be kept up to date
fieldSetScorePlayer1.setData(set.getScore(YOU));
fieldSetScorePlayer2.setData(set.getScore(OPPONENT));

//end the set if it has been won
var set_winner = isSetWon(set);
if(set_winner != null) {
set.end(set_winner);

//manage activity session
fieldSetScorePlayer1.setData(set.getScore(YOU));
fieldSetScorePlayer2.setData(set.getScore(OPPONENT));

if(!isEndless()) {
var match_winner = isWon();
if(match_winner != null) {
Expand Down
11 changes: 7 additions & 4 deletions source/test/BetterMathTest.mc
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import Toybox.Lang;
import Toybox.Test;

module BetterMathTest {

(:test)
function testMin(logger) {
function testMin(logger as Logger) as Boolean {
BetterTest.assertEqual(BetterMath.min(-2f, 2f), -2f, "Minimum between -2 and 2 is -2");
BetterTest.assertEqual(BetterMath.min(2f, -2f), -2f, "Minimum between 2 and -2 is -2");
BetterTest.assertEqual(BetterMath.min(2f, 2f), 2f, "Minimum between 2 and 2 is 2");
Expand All @@ -11,7 +14,7 @@ module BetterMathTest {
}

(:test)
function testMax(logger) {
function testMax(logger as Logger) as Boolean {
BetterTest.assertEqual(BetterMath.max(-2f, 2f), 2f, "Maximum between -2 and 2 is 2");
BetterTest.assertEqual(BetterMath.max(2f, -2f), 2f, "Maximum between 2 and -2 is 2");
BetterTest.assertEqual(BetterMath.max(2f, 2f), 2f, "Maximum between 2 and 2 is 2");
Expand All @@ -21,15 +24,15 @@ module BetterMathTest {
}

(:test)
function testMean(logger) {
function testMean(logger as Logger) as Boolean {
BetterTest.assertEqual(BetterMath.mean(2f, 4f), 3f, "Middle of 2 and 4 is 3");
BetterTest.assertEqual(BetterMath.mean(-4f, 4f), 0f, "Middle of -4 and 4 is 0");
BetterTest.assertEqual(BetterMath.mean(-4f, -2f), -3f, "Middle of -4 and -2 is -3");
return true;
}

(:test)
function testWeightedMean(logger) {
function testWeightedMean(logger as Logger) as Boolean {
BetterTest.assertEqual(BetterMath.weightedMean(0f, 4f, 0.25), 1f, "1/4 of path between 0 and 4 is 1");
BetterTest.assertEqual(BetterMath.weightedMean(0f, 4f, 0.5), 2f, "1/2 of path between 0 and 4 is 2");
BetterTest.assertEqual(BetterMath.weightedMean(0f, 4f, 0f), 0f, "0 of path between 0 and 4 is 0");
Expand Down
5 changes: 4 additions & 1 deletion source/test/GeometryTest.mc
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import Toybox.Lang;
import Toybox.Test;

module GeometryTest {

(:test)
function testChordLength(logger) {
function testChordLength(logger as Logger) as Boolean {
BetterTest.assertEqual(Geometry.chordLength(5f, 4f), 6f, "In a circle with a radius of 5, the chord length at the distance of 4 from the center of the circle is 6");
return true;
}
Expand Down
6 changes: 4 additions & 2 deletions source/test/HelpersTest.mc
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import Toybox.Lang;
import Toybox.Test;
using Toybox.Time;

module HelpersTest {

(:test)
function testFormatString(logger) {
function testFormatString(logger as Logger) as Boolean {
var string = "${name} is the ${family-relation} of ${other_name}";
var replacements = {"name" => "Luke", "family-relation" => "son", "other_name" => "Anakin"};
BetterTest.assertEqual(Helpers.formatString(string, replacements), "Luke is the son of Anakin", "Format string function fill a special string with data from a dictionary");
Expand All @@ -17,7 +19,7 @@ module HelpersTest {
}

(:test)
function testFormatDuration(logger) {
function testFormatDuration(logger as Logger) as Boolean {
var duration = 2 * 3600 + 28 * 60 + 42;
BetterTest.assertEqual(Helpers.formatDuration(new Time.Duration(duration)), "02:28:42", "Formatting a duration gives the good string");

Expand Down
10 changes: 6 additions & 4 deletions source/test/IQTest.mc
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Toybox.Lang;
import Toybox.Test;
import Toybox.System;
import Toybox.Activity;
import Toybox.ActivityRecording;
Expand All @@ -7,7 +9,7 @@ module IQTest {
//this is more a documentation than a test
//this test can be run on the Fenix 5 (max API v3.1.0), the Fenix 6 (max API v3.4.0) or the Fenix 7 (max API v4.2.0)
(:test)
function testActivity(logger) {
function testActivity(logger as Logger) as Boolean {
var version = System.getDeviceSettings().monkeyVersion;
var v320 = version[0] > 3 || version[0] == 3 && version[1] >= 2;
var v410 = version[0] > 4 || version[0] == 4 && version[1] >= 1;
Expand All @@ -17,14 +19,14 @@ module IQTest {
//devices >= 3.2.0 have all the new properties in Activity, if checked with "has"
//even if they don't support the new sports, they are available
if(v320) {
BetterTest.assertFalse(Activity has :SPORT_LEAP_FROG, "The new activity properties does not include leap frog");
//BetterTest.assertFalse(Activity has :SPORT_LEAP_FROG, "The new activity properties does not include leap frog"); disabled to satisfy the compiler
BetterTest.assertTrue(Activity has :SPORT_CYCLING, "The new activity properties includes cycling");
//the following assert should be valid only for devices >= 4.1.0
BetterTest.assertTrue(Activity has :SPORT_RACKET, "The new activity properties includes racket");
}
//devices < 3.2.0 don't have the new properties in Activity, if checked with "has"
else {
BetterTest.assertFalse(Activity has :SPORT_LEAP_FROG, "The new activity properties does not include leap frog");
//BetterTest.assertFalse(Activity has :SPORT_LEAP_FROG, "The new activity properties does not include leap frog"); disabled to satisfy the compiler
BetterTest.assertFalse(Activity has :SPORT_CYCLING, "The new activity properties does not include cycling");
BetterTest.assertFalse(Activity has :SPORT_RACKET, "The new activity properties does not include racket");
}
Expand All @@ -36,7 +38,7 @@ module IQTest {
//by the way, all devices have the old properties in ActivityRecording
BetterTest.assertTrue(ActivityRecording has :SPORT_CYCLING, "All devices have the old activity recording properties that includes cycling");
BetterTest.assertEqual(ActivityRecording.SPORT_CYCLING, 2, "All devices have the old activity recording properties that includes cycling (stored as 2)");
BetterTest.assertFalse(ActivityRecording has :SPORT_RACKET, "The old activity recording properties does not include racket");
//BetterTest.assertFalse(ActivityRecording has :SPORT_RACKET, "The old activity recording properties does not include racket"); disabled to satisfy the compiler

//this means that "has" can not be used to decide if the new sports and sub sports can be used
//but the new enum can be used on all devices (and this removes the deprecation messages)
Expand Down
15 changes: 9 additions & 6 deletions source/test/ListTest.mc
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import Toybox.Lang;
import Toybox.Test;

module ListTest {

(:test)
function testNewList(logger) {
function testNewList(logger as Logger) as Boolean {
var list = new List();
BetterTest.assertTrue(list.isEmpty(), "Newly created list is empty");
BetterTest.assertEqual(list.size(), 0, "Newly created list size is 0");
Expand All @@ -25,7 +28,7 @@ module ListTest {
}

(:test)
function testOneElementList(logger) {
function testOneElementList(logger as Logger) as Boolean {
var list = new List();
list.push(3);
BetterTest.assertFalse(list.isEmpty(), "Adding an element to a list makes it not empty");
Expand All @@ -36,7 +39,7 @@ module ListTest {
}

(:test)
function testTwoElementsList(logger) {
function testTwoElementsList(logger as Logger) as Boolean {
var list = new List();
list.push(3);
list.push(5);
Expand All @@ -47,7 +50,7 @@ module ListTest {
}

(:test)
function testRetrieval(logger) {
function testRetrieval(logger as Logger) as Boolean {
var list = new List();
list.push(3);
list.push(5);
Expand All @@ -65,7 +68,7 @@ module ListTest {
}

(:test)
function testIndexOf(logger) {
function testIndexOf(logger as Logger) as Boolean {
var list = new List();
list.push(3);
list.push(4);
Expand All @@ -77,7 +80,7 @@ module ListTest {
}

(:test)
function testRemoval(logger) {
function testRemoval(logger as Logger) as Boolean {
var list = new List();
list.push(3);
list.push(4);
Expand Down
Loading

0 comments on commit cc09d27

Please sign in to comment.