diff --git a/build.gradle b/build.gradle index 70b4cd10290..7413c75e2af 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,8 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0-rc1' + classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.5.0' } } diff --git a/ios/SAPTracker/src/xcode/SAPTracker/APIManager.swift b/ios/SAPTracker/src/xcode/SAPTracker/APIManager.swift index 853ef759d9b..ad3b99ccbe2 100644 --- a/ios/SAPTracker/src/xcode/SAPTracker/APIManager.swift +++ b/ios/SAPTracker/src/xcode/SAPTracker/APIManager.swift @@ -63,7 +63,7 @@ public class APIManager: NSObject { // set up reachability let operationQueue = manager!.operationQueue - manager!.reachabilityManager.setReachabilityStatusChangeBlock({(AFNetworkReachabilityStatus status) -> Void in + manager!.reachabilityManager.setReachabilityStatusChangeBlock({(status) -> Void in switch (status) { case AFNetworkReachabilityStatus.ReachableViaWWAN, AFNetworkReachabilityStatus.ReachableViaWiFi: operationQueue?.suspended = false @@ -106,7 +106,7 @@ public class APIManager: NSObject { } public func teamImage(competitorId: String!, result: (imageUrl: String?) -> Void) { - getTeam(competitorId, success: { (AFHTTPRequestOperation operation, AnyObject teamResponseObject) -> Void in + getTeam(competitorId, success: { (operation, teamResponseObject) -> Void in if let team = teamResponseObject as? [String: AnyObject], let imageUrl = team["imageUri"] as? String { @@ -115,7 +115,7 @@ public class APIManager: NSObject { result(imageUrl: nil) } - }, failure: { (AFHTTPRequestOperation operation, NSError error) -> Void in + }, failure: { (operation, error) -> Void in // No image & that's ok result(imageUrl: nil) }) diff --git a/ios/SAPTracker/src/xcode/SAPTracker/QRCodeManager.swift b/ios/SAPTracker/src/xcode/SAPTracker/QRCodeManager.swift index 7402428ece2..671a4cb884e 100644 --- a/ios/SAPTracker/src/xcode/SAPTracker/QRCodeManager.swift +++ b/ios/SAPTracker/src/xcode/SAPTracker/QRCodeManager.swift @@ -96,17 +96,17 @@ class QRCodeManager : NSObject, UIAlertViewDelegate { // get event APIManager.sharedManager.getEvent(qrcodeData!.eventId, - success: { (AFHTTPRequestOperation operation, AnyObject eventResponseObject) -> Void in + success: { (operation, eventResponseObject) -> Void in self.eventDictionary = eventResponseObject as? [String: AnyObject] APIManager.sharedManager.getLeaderBoard(self.qrcodeData!.leaderBoardName, // get leader board - success: { (AFHTTPRequestOperation operation, AnyObject leaderBoardResponseObject) -> Void in + success: { (operation, leaderBoardResponseObject) -> Void in self.leaderBoardDictionary = leaderBoardResponseObject as? [String: AnyObject] APIManager.sharedManager.getCompetitor(self.qrcodeData!.competitorId, // get competitor - success: { (AFHTTPRequestOperation operation, AnyObject competitorResponseObject) -> Void in + success: { (operation, competitorResponseObject) -> Void in APIManager.sharedManager.teamImage(self.qrcodeData!.competitorId, result: { (imageUrl) -> Void in @@ -125,14 +125,14 @@ class QRCodeManager : NSObject, UIAlertViewDelegate { alertView.show() }) - }, failure: { (AFHTTPRequestOperation operation, NSError error) -> Void in + }, failure: { (operation, error) -> Void in self.delegate.activityIndicatorView?.stopAnimating() let title = String(format: NSLocalizedString("Couldn't get competitor %@", comment: ""), self.qrcodeData!.competitorId!) let alertView = UIAlertView(title: title, message: error.localizedDescription, delegate: self, cancelButtonTitle: NSLocalizedString("Cancel", comment: "")) alertView.tag = AlertView.ServerError.rawValue; alertView.show() - }) }, failure: { (AFHTTPRequestOperation operation, NSError error) -> Void in + }) }, failure: { (operation, error) -> Void in self.delegate.activityIndicatorView?.stopAnimating() let title = String(format: NSLocalizedString("Couldn't get leader board %@", comment: ""), self.qrcodeData!.leaderBoardName!) let alertView = UIAlertView(title: title, message: error.localizedDescription, delegate: self, cancelButtonTitle: NSLocalizedString("Cancel", comment: "")) @@ -140,7 +140,7 @@ class QRCodeManager : NSObject, UIAlertViewDelegate { alertView.show() }) - }, failure: { (AFHTTPRequestOperation operation, NSError error) -> Void in + }, failure: { (operation, error) -> Void in self.delegate.activityIndicatorView?.stopAnimating() let title = String(format: NSLocalizedString("Couldn't get event %@", comment: ""), self.qrcodeData!.eventId!) let alertView = UIAlertView(title: title, message: error.localizedDescription, delegate: self, cancelButtonTitle: NSLocalizedString("Cancel", comment: "")) @@ -185,7 +185,7 @@ class QRCodeManager : NSObject, UIAlertViewDelegate { checkIn.imageUrl = teamImageURL APIManager.sharedManager.checkIn(leaderBoardName, competitorId: competitorId, deviceUuid: DeviceUDIDManager.UDID, pushDeviceId: "", fromMillis: fromMillis, - success: { (AFHTTPRequestOperation operation, AnyObject eventResponseObject) -> Void in + success: { (operation, eventResponseObject) -> Void in // create core data objects let event = DataManager.sharedManager.newEvent(checkIn) @@ -197,7 +197,7 @@ class QRCodeManager : NSObject, UIAlertViewDelegate { DataManager.sharedManager.saveContext() self.delegate.qrCodeOK?() - }, failure: { (AFHTTPRequestOperation operation, NSError error) -> Void in + }, failure: { (operation, error) -> Void in self.delegate.activityIndicatorView?.stopAnimating() let title = String(format:NSLocalizedString("Couldn't check-in to %@", comment: ""), leaderBoardName); let alertView = UIAlertView(title: title, message: error.localizedDescription, delegate: self, cancelButtonTitle: NSLocalizedString("Cancel", comment: "")) diff --git a/ios/SAPTracker/src/xcode/SAPTracker/RegattaViewController.swift b/ios/SAPTracker/src/xcode/SAPTracker/RegattaViewController.swift index bee37b6ab4a..c551eed0d66 100644 --- a/ios/SAPTracker/src/xcode/SAPTracker/RegattaViewController.swift +++ b/ios/SAPTracker/src/xcode/SAPTracker/RegattaViewController.swift @@ -281,7 +281,7 @@ class RegattaViewController : UIViewController, UIActionSheetDelegate, UINavigat DataManager.sharedManager.selectedCheckIn!.userImage = jpegData APIManager.sharedManager.postTeamImage(DataManager.sharedManager.selectedCheckIn!.competitorId, imageData: jpegData, - success: { (AnyObject responseObject) -> Void in + success: { (responseObject) -> Void in // http://wiki.sapsailing.com/wiki/tracking-app/api-v1#Competitor-Information-%28in-general%29 // "Additional Notes: Competitor profile image left out for now." let responseDictionary = responseObject as![String: AnyObject] @@ -291,7 +291,7 @@ class RegattaViewController : UIViewController, UIActionSheetDelegate, UINavigat NSUserDefaults.standardUserDefaults().setBool(false, forKey: self.uploadKey) NSUserDefaults.standardUserDefaults().synchronize() }, - failure: { (NSError error) -> Void in + failure: { (error) -> Void in let alertView = UIAlertView(title: NSLocalizedString("Failed to upload image", comment: ""), message: error.localizedDescription, delegate: self, cancelButtonTitle: NSLocalizedString("Cancel", comment: "")) alertView.tag = AlertView.UploadFailed.rawValue; alertView.show() @@ -325,9 +325,9 @@ class RegattaViewController : UIViewController, UIActionSheetDelegate, UINavigat competitorId: DataManager.sharedManager.selectedCheckIn!.competitorId, deviceUuid: DeviceUDIDManager.UDID, toMillis: toMillis, - success: { (AFHTTPRequestOperation operation, AnyObject competitorResponseObject) -> Void in + success: { (operation, competitorResponseObject) -> Void in }, - failure: { (AFHTTPRequestOperation operation, NSError error) -> Void in + failure: { (operation, error) -> Void in } ) DataManager.sharedManager.deleteCheckIn(DataManager.sharedManager.selectedCheckIn!) diff --git a/ios/SAPTracker/src/xcode/SAPTracker/SendGPSFixController.swift b/ios/SAPTracker/src/xcode/SAPTracker/SendGPSFixController.swift index 4df174933cc..44ff44974a9 100644 --- a/ios/SAPTracker/src/xcode/SAPTracker/SendGPSFixController.swift +++ b/ios/SAPTracker/src/xcode/SAPTracker/SendGPSFixController.swift @@ -80,7 +80,7 @@ class SendGPSFixController: NSObject { APIManager.sharedManager.initManager(serverUrl) if APIManager.sharedManager.networkAvailable { APIManager.sharedManager.postGPSFixes(DeviceUDIDManager.UDID, gpsFixes: gpsFixes, - success: { (AFHTTPRequestOperation operation, AnyObject competitorResponseObject) -> Void in + success: { (operation, competitorResponseObject) -> Void in print("sent GPS fixes") for gpsFix in gpsFixes { DataManager.sharedManager.managedObjectContext!.deleteObject(gpsFix) @@ -88,7 +88,7 @@ class SendGPSFixController: NSObject { let notification = NSNotification(name: NotificationType.gpsFixesSynced, object: self) NSNotificationQueue.defaultQueue().enqueueNotification(notification, postingStyle: NSPostingStyle.PostASAP) }, - failure: { (AFHTTPRequestOperation operation, NSError error) -> Void in + failure: { (operation, error) -> Void in print("error sending GPS fixes") }) } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PassingInstruction.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PassingInstruction.java index d8d11bf4472..5cabd2549b9 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PassingInstruction.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PassingInstruction.java @@ -14,20 +14,34 @@ package com.sap.sailing.domain.common; public enum PassingInstruction { None(1, 2), Port(1), - Single_Unknown(1), Starboard(1), + Single_Unknown(1), Gate(2), Line(2), Offset(2), FixedBearing(1); + /** + * Those values relevant for picking by a user when creating a waypoint; for example, this list + * does not include the {@link #None} or the {@link #Single_Unknown} literals. No guarantees + * made as to the ordering. The enum literals may change, be extended, have elements inserted or + * removed at any time! + */ public static PassingInstruction[] relevantValues() { - PassingInstruction[] uiValues = new PassingInstruction[PassingInstruction.values().length - 1]; - + final PassingInstruction[] uselessValues = new PassingInstruction[] { None, Single_Unknown, Offset, FixedBearing }; + PassingInstruction[] uiValues = new PassingInstruction[PassingInstruction.values().length - uselessValues.length]; int i = 0; for (PassingInstruction p : PassingInstruction.values()) { - if (p != PassingInstruction.None) + boolean useless = false; + for (PassingInstruction u : uselessValues) { + if (u == p) { + useless = true; + break; + } + } + if (!useless) { uiValues[i++] = p; + } } return uiValues; } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PathType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PathType.java similarity index 90% rename from java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PathType.java rename to java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PathType.java index 6ed4b730224..92486408659 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PathType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PathType.java @@ -1,4 +1,4 @@ -package com.sap.sailing.simulator; +package com.sap.sailing.domain.common; public enum PathType { OMNISCIENT("1#Omniscient"), diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java index fd072fb4fdb..f9deaa0607c 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java @@ -1605,6 +1605,65 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest { assertEquals(0., leaderboard1.getTotalPoints(c[0], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1 } + /** + * When a DNS score correction is made then both, the MaxPointsReason and the score shall apply at the start of the race. + */ + @Test + public void testDNSScoreAppliesBeforeEndOfRace() throws NoWindException { + Competitor[] c = createCompetitors(4).toArray(new Competitor[0]); + final TimePoint endOfR1 = MillisecondsTimePoint.now(); + final TimePoint withinR1 = endOfR1.minus(1000); + final TimePoint startOfR1 = withinR1.minus(10000); + final TimePoint beforeStartOfR1 = startOfR1.minus(10000); + final TimePoint afterEndOfR1 = endOfR1.plus(1000); + FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), + new LowPoint(), null); + leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(startOfR1, Arrays.asList(c)) { + private static final long serialVersionUID = 8705622361027154428L; + public TimePoint getEndOfRace() { + return endOfR1; + } + }, "R1", /* medalRace */false); + leaderboard1.getScoreCorrection().correctScore(c[0], leaderboard1.getRaceColumnByName("R1"), 123.); + leaderboard1.getScoreCorrection().setMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), MaxPointsReason.DNS); + assertEquals(MaxPointsReason.NONE, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), beforeStartOfR1)); + assertEquals(MaxPointsReason.DNS, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), withinR1)); + assertEquals(MaxPointsReason.DNS, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), afterEndOfR1)); + assertNull(leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), beforeStartOfR1)); + assertEquals(123., leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), withinR1), 0.00000001); + assertEquals(123., leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), afterEndOfR1), 0.00000001); + } + + /** + * When a DNS score correction is made then both, the MaxPointsReason and the score shall apply at the start of the race. + */ + @Test + public void testDNFScoreDoesNotApplyBeforeEndOfRace() throws NoWindException { + Competitor[] c = createCompetitors(4).toArray(new Competitor[0]); + final TimePoint endOfR1 = MillisecondsTimePoint.now(); + final TimePoint withinR1 = endOfR1.minus(1000); + final TimePoint startOfR1 = withinR1.minus(10000); + final TimePoint beforeStartOfR1 = startOfR1.minus(10000); + final TimePoint afterEndOfR1 = endOfR1.plus(1000); + FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), + new LowPoint(), null); + leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(startOfR1, Arrays.asList(c)) { + private static final long serialVersionUID = 8705622361027154428L; + public TimePoint getEndOfRace() { + return endOfR1; + } + }, "R1", /* medalRace */false); + leaderboard1.getScoreCorrection().correctScore(c[0], leaderboard1.getRaceColumnByName("R1"), 123.); + leaderboard1.getScoreCorrection().setMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), MaxPointsReason.DNF); + assertEquals(MaxPointsReason.NONE, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), beforeStartOfR1)); + assertEquals(MaxPointsReason.NONE, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), withinR1)); + assertEquals(MaxPointsReason.DNF, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), afterEndOfR1)); + assertNull(leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), beforeStartOfR1)); + assertEquals(1.0, leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), withinR1), 0.00000001); // tracked rank is 1 + assertEquals(123., leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), afterEndOfR1), 0.00000001); + } + + /** * A test case for bug 1802. Competitors that didn't have a fleet (null fleet) were sorted to the end of the column even * if they had a score correction assigned. diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/MarkPassingCalculatorPerformanceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/MarkPassingCalculatorPerformanceTest.java index 704f85bcd85..77b3f050896 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/MarkPassingCalculatorPerformanceTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/MarkPassingCalculatorPerformanceTest.java @@ -67,7 +67,7 @@ public class MarkPassingCalculatorPerformanceTest extends AbstractMockedRaceMark time = System.currentTimeMillis() - time; result.put("FinderPerformance", time); System.out.println(time); - Assert.assertTrue(time < 5000); + Assert.assertTrue("Time expected to be less than 5000ms but was "+time+"ms", time < 5000); } @Test diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie.java index ddaf258dfd0..ecaa46462a7 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie.java @@ -38,24 +38,19 @@ public abstract class AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie extends @Override public int compareByBetterScore(Competitor o1, List> o1Scores, Competitor o2, List> o2Scores, boolean nullScoresAreBetter, TimePoint timePoint) { - int o1Wins = getWins(o1Scores); - int o2Wins = getWins(o2Scores); - int result = o2Wins - o1Wins; - return result; + Double o1Highest = getHighestScore(o1Scores); + Double o2Highest = getHighestScore(o2Scores); + return o2Highest.compareTo(o1Highest); } - /** - * Counts a competitor's wins by comparing the scores to {@link #MAX_POINTS} which is the score attributed to a race - * won - */ - private int getWins(List> scores) { - int wins = 0; + private double getHighestScore(List> scores) { + double highestScore = 0; for (com.sap.sse.common.Util.Pair score : scores) { - if (Math.abs(score.getB() - getScoreForRaceWinner() * score.getA().getFactor()) < 0.0000001) { - wins++; + if ((score.getB() * score.getA().getFactor()) > highestScore) { + highestScore = score.getB() * score.getA().getFactor(); } } - return wins; + return highestScore; } /** diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 76a200f12f6..32c16a2e8f3 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -439,11 +439,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race @Override public Double getNetPoints(final Competitor competitor, final RaceColumn raceColumn, final TimePoint timePoint) { return getScoreCorrection().getCorrectedScore( - new Callable() { - public Integer call() throws NoWindException { - return getTrackedRank(competitor, raceColumn, timePoint); - } - }, competitor, + ()->getTrackedRank(competitor, raceColumn, timePoint), competitor, raceColumn, timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme()).getCorrectedScore(); } @@ -684,21 +680,13 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race @Override public Entry getEntry(final Competitor competitor, final RaceColumn race, final TimePoint timePoint, Set discardedRaceColumns) throws NoWindException { - Callable trackedRankProvider = new Callable() { - public Integer call() throws NoWindException { - return getTrackedRank(competitor, race, timePoint); - } - }; + Callable trackedRankProvider = ()->getTrackedRank(competitor, race, timePoint); final Result correctedResults = getScoreCorrection().getCorrectedScore(trackedRankProvider, competitor, race, timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme()); boolean discarded = isDiscarded(competitor, race, timePoint, discardedRaceColumns); final Double correctedScore = correctedResults.getCorrectedScore(); - return new EntryImpl(trackedRankProvider, correctedScore, new Callable() { - @Override - public Double call() { - return correctedResults.getUncorrectedScore(); - } - }, correctedResults.isCorrected(), discarded ? DOUBLE_0 : correctedScore == null ? null + return new EntryImpl(trackedRankProvider, correctedScore, ()->correctedResults.getUncorrectedScore(), + correctedResults.isCorrected(), discarded ? DOUBLE_0 : correctedScore == null ? null : Double.valueOf(correctedScore * race.getFactor()), correctedResults.getMaxPointsReason(), discarded, race.getFleetOfCompetitor(competitor)); } @@ -756,12 +744,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race Map> discardedRaces = new HashMap>(); for (final RaceColumn raceColumn : getRaceColumns()) { for (final Competitor competitor : getCompetitors()) { - Callable trackedRankProvider = new Callable() { - @Override - public Integer call() throws Exception { - return getTrackedRank(competitor, raceColumn, timePoint); - } - }; + Callable trackedRankProvider = ()->getTrackedRank(competitor, raceColumn, timePoint); final Result correctedResults = getScoreCorrection().getCorrectedScore(trackedRankProvider, competitor, raceColumn, timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme()); Set discardedRacesForCompetitor = discardedRaces.get(competitor); @@ -772,7 +755,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race boolean discarded = discardedRacesForCompetitor.contains(raceColumn); final Double correctedScore = correctedResults.getCorrectedScore(); Entry entry = new EntryImpl(trackedRankProvider, correctedScore, - new Callable() { @Override public Double call() { return correctedResults.getUncorrectedScore(); } }, + ()->correctedResults.getUncorrectedScore(), correctedResults.isCorrected(), discarded ? DOUBLE_0 : (correctedScore==null?null: Double.valueOf((correctedScore * raceColumn.getFactor()))), correctedResults.getMaxPointsReason(), diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java index ee0aff0bcfe..01035929d41 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java @@ -324,11 +324,39 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection { @Override public MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) { - MaxPointsReason result = maxPointsReasons.get(raceColumn.getKey(competitor)); - if (result == null || !isMaxPointsReasonApplicable(result, timePoint, raceColumn, competitor)) { - result = MaxPointsReason.NONE; + return getAnnotatedMaxPointsReason(competitor, raceColumn, timePoint).getMaxPointsReason(); + } + + private static class AnnotatedMaxPointsReason { + private final MaxPointsReason maxPointsReason; + private final boolean maxPointsReasonExistsButIsNotApplicableForTimePoint; + public AnnotatedMaxPointsReason(MaxPointsReason maxPointsReason, + boolean maxPointsReasonExistsButIsNotApplicableForTimePoint) { + super(); + this.maxPointsReason = maxPointsReason; + this.maxPointsReasonExistsButIsNotApplicableForTimePoint = maxPointsReasonExistsButIsNotApplicableForTimePoint; } - return result; + public MaxPointsReason getMaxPointsReason() { + return maxPointsReason; + } + public boolean isMaxPointsReasonExistsButIsNotApplicableForTimePoint() { + return maxPointsReasonExistsButIsNotApplicableForTimePoint; + } + } + + private AnnotatedMaxPointsReason getAnnotatedMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) { + MaxPointsReason maxPointsReason = maxPointsReasons.get(raceColumn.getKey(competitor)); + boolean maxPointsReasonExistsButIsNotApplicableForTimePoint; + if (maxPointsReason == null) { + maxPointsReason = MaxPointsReason.NONE; + maxPointsReasonExistsButIsNotApplicableForTimePoint = false; + } else if (!isMaxPointsReasonApplicable(maxPointsReason, timePoint, raceColumn, competitor)) { + maxPointsReason = MaxPointsReason.NONE; + maxPointsReasonExistsButIsNotApplicableForTimePoint = true; + } else { + maxPointsReasonExistsButIsNotApplicableForTimePoint = false; + } + return new AnnotatedMaxPointsReason(maxPointsReason, maxPointsReasonExistsButIsNotApplicableForTimePoint); } private boolean isMaxPointsReasonApplicable(MaxPointsReason maxPointsReason, TimePoint timePoint, RaceColumn raceColumn, Competitor competitor) { @@ -360,15 +388,21 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection { final NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, final ScoringScheme scoringScheme) { Double result; - final MaxPointsReason maxPointsReason = getMaxPointsReason(competitor, raceColumn, timePoint); - if (maxPointsReason == MaxPointsReason.NONE) { - result = getCorrectedNonMaxedScore(competitor, raceColumn, trackedRankProvider, scoringScheme, numberOfCompetitorsInLeaderboardFetcher, timePoint); + final AnnotatedMaxPointsReason maxPointsReason = getAnnotatedMaxPointsReason(competitor, raceColumn, timePoint); + if (maxPointsReason.getMaxPointsReason() == MaxPointsReason.NONE) { + // could be that there is a MaxPointsReason that just doesn't apply yet at timePoint; in this + // case ignore the score correction and deliver the uncorrected result + if (maxPointsReason.isMaxPointsReasonExistsButIsNotApplicableForTimePoint()) { + result = getUncorrectedScore(competitor, raceColumn, trackedRankProvider, scoringScheme, numberOfCompetitorsInLeaderboardFetcher, timePoint); + } else { + result = getCorrectedNonMaxedScore(competitor, raceColumn, trackedRankProvider, scoringScheme, numberOfCompetitorsInLeaderboardFetcher, timePoint); + } } else { // allow explicit override even when max points reason is specified; calculation may be wrong, // e.g., in case we have an untracked race and the number of competitors is estimated incorrectly Double correctedNonMaxedScore = correctedScores.get(raceColumn.getKey(competitor)); if (correctedNonMaxedScore == null) { - result = scoringScheme.getPenaltyScore(raceColumn, competitor, maxPointsReason, + result = scoringScheme.getPenaltyScore(raceColumn, competitor, maxPointsReason.getMaxPointsReason(), getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher), numberOfCompetitorsInLeaderboardFetcher); } else { @@ -379,7 +413,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection { return new Result() { @Override public MaxPointsReason getMaxPointsReason() { - return maxPointsReason; + return maxPointsReason.getMaxPointsReason(); } @Override @@ -446,25 +480,30 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection { Double correctedNonMaxedScore = correctedScores.get(raceColumn.getKey(competitor)); Double result; if (correctedNonMaxedScore == null || isCertainlyBeforeRaceFinish(timePoint, raceColumn, competitor)) { - try { - int trackedRank = trackedRankProvider.call(); - result = scoringScheme.getScoreForRank(leaderboard, raceColumn, competitor, - trackedRank, new Callable() { - @Override - public Integer call() { - return getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher); - } - }, numberOfCompetitorsInLeaderboardFetcher, timePoint); - } catch (Exception e) { - logger.log(Level.SEVERE, "Exception while computing corrected non maxed score", e); - throw new RuntimeException(e); - } + result = getUncorrectedScore(competitor, raceColumn, trackedRankProvider, scoringScheme, + numberOfCompetitorsInLeaderboardFetcher, timePoint); } else { result = correctedNonMaxedScore; } return result; } + private Double getUncorrectedScore(final Competitor competitor, final RaceColumn raceColumn, + Callable trackedRankProvider, ScoringScheme scoringScheme, + final NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) { + final Double result; + try { + int trackedRank = trackedRankProvider.call(); + result = scoringScheme.getScoreForRank(leaderboard, raceColumn, competitor, + trackedRank, ()->getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher), + numberOfCompetitorsInLeaderboardFetcher, timePoint); + } catch (Exception e) { + logger.log(Level.SEVERE, "Exception while computing uncorrected score", e); + throw new RuntimeException(e); + } + return result; + } + @Override public Double getExplicitScoreCorrection(Competitor competitor, RaceColumn raceColumn) { return correctedScores.get(raceColumn.getKey(competitor)); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index 8b236db7afb..b692f921514 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -1398,7 +1398,15 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl } @Override - public List getCompetitorsFromBestToWorst(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) { + public List getCompetitorsFromBestToWorst(TimePoint unadjustedTimePoint, WindLegTypeAndLegBearingCache cache) { + final TimePoint timePoint; + // normalize the time point to get cache hits when asking for time points that are later than + // the last time point affected by any event received for this tracked race + if (Util.compareToWithNull(unadjustedTimePoint, getTimePointOfNewestEvent(), /* nullIsLess */ true) <= 0) { + timePoint = unadjustedTimePoint; + } else { + timePoint = getTimePointOfNewestEvent(); + } NamedReentrantReadWriteLock readWriteLock; synchronized (competitorRankingsLocks) { readWriteLock = competitorRankingsLocks.get(timePoint); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/RaceContext.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/RaceContext.java index 76f4b91c3ea..4fe52c27d2e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/RaceContext.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/RaceContext.java @@ -429,6 +429,15 @@ public class RaceContext { return 0; } + private TimePoint getLiveTimePoint() { + return new MillisecondsTimePoint(getLiveTimePointInMillis()); + } + + private long getLiveTimePointInMillis() { + final Long liveDelay = leaderboard.getDelayToLiveInMillis(); + return System.currentTimeMillis() - (liveDelay == null ? 0l : liveDelay); + } + private SimpleCompetitorDTO getWinnerOrNull() { if (getLiveRaceViewState() != RaceViewState.FINISHED) { // We can't reliably calculate the winner for non finished races @@ -436,10 +445,7 @@ public class RaceContext { } // TODO do not calculate the winner if the blue flag is currently shown. try { - TimePoint finishTime = trackedRace.getEndOfRace(); - if (finishTime == null) { - finishTime = HomeServiceUtil.getLiveTimePoint(); - } + TimePoint finishTime = getLiveTimePoint(); List competitors = leaderboard.getCompetitorsFromBestToWorst(raceColumn, finishTime); if (competitors == null || competitors.isEmpty()) { return null; @@ -542,7 +548,7 @@ public class RaceContext { if (finishTime != null && now.after(finishTime)) { return RaceViewState.FINISHED; } - if(raceLog != null) { + if (raceLog != null) { RaceLogFlagEvent abortingFlagEvent = checkForAbortFlagEvent(); if (abortingFlagEvent != null) { Flags upperFlag = abortingFlagEvent.getUpperFlag(); @@ -558,10 +564,10 @@ public class RaceContext { } } ScoreCorrection scoreCorrection = leaderboard.getScoreCorrection(); - if(trackedRace == null && scoreCorrection != null && scoreCorrection.hasCorrectionForNonTrackedFleet(raceColumn)) { + if (trackedRace == null && scoreCorrection != null && scoreCorrection.hasCorrectionForNonTrackedFleet(raceColumn)) { return RaceViewState.FINISHED; } - if(startTime != null) { + if (startTime != null) { return RaceViewState.RUNNING; } return RaceViewState.PLANNED; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java index 9e81aa5626a..6ed4b53e7e8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java @@ -449,16 +449,6 @@ public final class HomeServiceUtil { return result; } - public static TimePoint getLiveTimePoint() { - return new MillisecondsTimePoint(getLiveTimePointInMillis()); - } - - public static long getLiveTimePointInMillis() { - // TODO better solution - long livePlayDelayInMillis = 15_000; - return System.currentTimeMillis() - livePlayDelayInMillis; - } - public static String getCourseAreaNameForRegattaIdThereIsMoreThanOne(EventBase event, Leaderboard leaderboard) { /** The course area will not be shown if there is only one course area defined for the event */ if (Util.size(event.getVenue().getCourseAreas()) <= 1) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimePanel.java index e5446e7be83..50ac79933e6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimePanel.java @@ -47,9 +47,9 @@ public class RaceTimePanel extends TimePanel implements R private final Duration initialTimeAfterRaceStartInReplayMode; public RaceTimePanel(UserService userService, Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages, - RaceTimesInfoProvider raceTimesInfoProvider, boolean canReplayWhileLiveIsPossible, boolean isScreenLargeEnoughToOfferChartSupport, + RaceTimesInfoProvider raceTimesInfoProvider, boolean canReplayWhileLiveIsPossible, boolean forcePaddingRightToAlignToCharts, RegattaAndRaceIdentifier selectedRaceIdentifier, Duration initialTimeAfterRaceStartInReplayMode) { - super(timer, timeRangeProvider, stringMessages, canReplayWhileLiveIsPossible, isScreenLargeEnoughToOfferChartSupport); + super(timer, timeRangeProvider, stringMessages, canReplayWhileLiveIsPossible, forcePaddingRightToAlignToCharts); this.userService = userService; this.raceTimesInfoProvider = raceTimesInfoProvider; selectedRace = null; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 93319fa374b..86fbc58fa9e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -1578,4 +1578,12 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String perHours(); String pleaseSelectAValidWindPattern(); String downwindLegsNotSupported(); + String raceLeader(); + String opportunistLeft(); + String opportunistRight(); + String oneTurnerLeft(); + String oneTurnerRight(); + String current(); + String simulationLegendAlgorithmTimedOutText(); + String simulationLegendMixedLegText(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index aad2e35dc77..3787d15cee6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -1553,4 +1553,12 @@ currentBearing=Current Bearing percentUnit=% perHours=per hr pleaseSelectAValidWindPattern=Please select a valid wind pattern. -downwindLegsNotSupported=Downwind legs are NOT supported yet. Sorry about that :) \ No newline at end of file +downwindLegsNotSupported=Downwind legs are NOT supported yet. Sorry about that :) +raceLeader=Race Leader +opportunistLeft=Opportunist Left +opportunistRight=Opportunist Right +oneTurnerLeft=1-Turner Left +oneTurnerRight=1-Turner Right +current=Current +simulationLegendAlgorithmTimedOutText = out-of-bounds +simulationLegendMixedLegText = ambiguous wind \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 52b13606b29..cfb4a4181a5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -1526,7 +1526,7 @@ baseBearing=Mittlere Richtung raceCourseDiff=Rennkurs Abweichung baseSpeed=Mittlere Geschwindigkeit probability=Wahrscheinlichkeit -gustSize=Böenstärke +gustSize=Böengröße averageSpeed=Mittlere Geschwindigkeit speedVariance=Geschwindigkeitsvarianz averageDirection=Mittlere Richtung @@ -1538,4 +1538,12 @@ currentBearing=Strömungsrichtung percentUnit=% pleaseSelectAValidWindPattern=Bitte ein gültiges Wind-Muster auswählen. downwindLegsNotSupported=Vorwind Legs werden aktuell leider NICHT unterstützt. -perHours=pro Stunde \ No newline at end of file +perHours=pro Stunde +raceLeader=Führender +opportunistLeft=Opportunist links +opportunistRight=Opportunist rechts +oneTurnerLeft=1-Wenden-Kurs links +oneTurnerRight=1-Wenden-Kurs rechts +current=Strömung +simulationLegendAlgorithmTimedOutText=außerhalb des zul. Bereichs +simulationLegendMixedLegText=mehrdeutiger Wind \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java index 4a9ba2b6dbf..026122860d9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java @@ -89,17 +89,20 @@ public class TimePanel extends SimplePanel implemen private static ClientResources resources = GWT.create(ClientResources.class); protected static TimePanelCss timePanelCss = TimePanelCssResources.INSTANCE.css(); + private final boolean forcePaddingRightToAlignToCharts; /** - * @param isScreenLargeEnoughToOfferChartSupport - * if true, the right padding will be set such that the time panel lines up with charts such - * as the competitor chart or the wind chart shown above it + * @param forcePaddingRightToAlignToCharts + * if true, the right padding will always be set such that the time panel lines up with + * charts such as the competitor chart or the wind chart shown above it, otherwise the padding depends + * on the flag set by {@link #setLiveGenerallyPossible(boolean)} */ public TimePanel(Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages, - boolean canReplayWhileLiveIsPossible, boolean isScreenLargeEnoughToOfferChartSupport) { + boolean canReplayWhileLiveIsPossible, boolean forcePaddingRightToAlignToCharts) { this.timer = timer; this.timeRangeProvider = timeRangeProvider; this.stringMessages = stringMessages; this.canReplayWhileLiveIsPossible = canReplayWhileLiveIsPossible; + this.forcePaddingRightToAlignToCharts = forcePaddingRightToAlignToCharts; timer.addTimeListener(this); timer.addPlayStateListener(this); timeRangeProvider.addTimeRangeChangeListener(this); @@ -111,9 +114,6 @@ public class TimePanel extends SimplePanel implemen timePanelSliderFlowWrapper = new FlowPanel(); timePanelSlider.setStyleName("timePanelSlider"); timePanelSlider.getElement().getStyle().setPaddingLeft(66, Unit.PX); - if (isScreenLargeEnoughToOfferChartSupport) { - timePanelSlider.getElement().getStyle().setPaddingRight(66, Unit.PX); - } timePanelSliderFlowWrapper.add(timePanelSlider); playSpeedImg = resources.timesliderPlaySpeedIcon(); @@ -555,8 +555,19 @@ public class TimePanel extends SimplePanel implemen */ protected void setLiveGenerallyPossible(boolean possible) { backToLivePlayButton.setVisible(possible); + updateTimeSliderPadding(possible); updatePlayPauseButtonsVisibility(timer.getPlayMode()); } + + private void updateTimeSliderPadding(boolean backToLivePlayButtonVisible) { + Style timePanelStyle = timePanelSlider.getElement().getStyle(); + if (backToLivePlayButtonVisible || forcePaddingRightToAlignToCharts) { + timePanelStyle.setPaddingRight(66, Unit.PX); + } else { + timePanelStyle.clearPaddingRight(); + } + timeSlider.onResize(); + } @SuppressWarnings("unchecked") public T getSettings() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 1b6f153f552..b9b986a862e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -5,7 +5,6 @@ import java.util.Collections; import java.util.UUID; import com.google.gwt.core.client.GWT; -import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -17,6 +16,7 @@ import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootLayoutPanel; +import com.sap.sailing.gwt.common.client.formfactor.DeviceDetector; import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel; import com.sap.sailing.gwt.ui.client.MediaService; @@ -97,11 +97,12 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { sailingService.getRaceboardData(regattaName, raceName, leaderboardName, leaderboardGroupName, eventId, new AsyncCallback() { @Override public void onSuccess(RaceboardDataDTO result) { - // Determine if the screen is large enough to display charts such as the competitor chart or the wind chart. - // This decision is made once based on the initial screen height. Resizing the window afterwards will have - // no impact on the chart support, i.e. they are available/unavailable based on the initial decision. - boolean isScreenLargeEnoughToOfferChartSupport = Document.get().getClientHeight() >= 600; - checkUrlParameters(result, showMapControls, isScreenLargeEnoughToOfferChartSupport); + // Determine if the charts, such as the competitor chart or the wind chart, the edit marks + // panels, such as mark passing and mark position editors and manage media buttons should be shown. + // Automatic selection of attached video (if any) also depends on this flag. + // The decision is made once during initial page load based on the device type (mobile or not). + boolean showChartMarkEditMediaButtonsAndVideo = !DeviceDetector.isMobile(); + checkUrlParameters(result, showMapControls, showChartMarkEditMediaButtonsAndVideo); } @Override @@ -139,7 +140,7 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { } private void checkUrlParameters(RaceboardDataDTO raceboardData, boolean showMapControls, - boolean isScreenLargeEnoughToOfferChartSupport) { + boolean showChartMarkEditMediaButtonsAndVideo) { if (!raceboardData.isValidLeaderboard()) { createErrorPage(getStringMessages().noSuchLeaderboard()); return; @@ -170,7 +171,7 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, getUserService(), asyncActionsExecutor, raceboardData.getCompetitorAndTheirBoats(), timer, selectedRace.getRaceIdentifier(), leaderboardName, leaderboardGroupName, eventId, raceboardViewConfig, RaceBoardEntryPoint.this, getStringMessages(), userAgent, raceTimesInfoProvider, showMapControls, - isScreenLargeEnoughToOfferChartSupport); + showChartMarkEditMediaButtonsAndVideo); createRaceBoardInOneScreenMode(raceBoardPanel, raceboardViewConfig); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index 0ea4c8f0b8c..bdbec06f3f0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -37,7 +37,6 @@ import com.sap.sailing.gwt.ui.client.CompetitorColorProviderImpl; import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory; -import com.sap.sailing.gwt.ui.client.GlobalNavigationPanel; import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener; import com.sap.sailing.gwt.ui.client.MediaServiceAsync; import com.sap.sailing.gwt.ui.client.RaceTimePanel; @@ -133,17 +132,18 @@ public class RaceBoardPanel extends SimplePanel implements LeaderboardUpdateList * @param eventId * an optional event that can be used for "back"-navigation in case the race board shows a race in the * context of an event; may be null. - * @param isScreenLargeEnoughToOfferChartSupport - * if the screen is large enough to display charts such as the competitor chart or the wind chart, a - * padding is provided for the RaceTimePanel that aligns its right border with that of the charts, and - * the charts are created. This decision is made once on startup in the {@link RaceBoardEntryPoint} class. + * @param showChartMarkEditMediaButtonsAndVideo + * if true charts, such as the competitor chart or the wind chart, (as well as edit mark + * panels and manage media buttons) are shown and a padding is provided for the RaceTimePanel that + * aligns its right border with that of the chart. Otherwise those components will be hidden. + * This decision is made once on startup in the {@link RaceBoardEntryPoint} class. */ - public RaceBoardPanel(SailingServiceAsync sailingService, MediaServiceAsync mediaService, - UserService userService, AsyncActionsExecutor asyncActionsExecutor, Map competitorsAndTheirBoats, + public RaceBoardPanel(SailingServiceAsync sailingService, MediaServiceAsync mediaService, UserService userService, + AsyncActionsExecutor asyncActionsExecutor, Map competitorsAndTheirBoats, Timer timer, RegattaAndRaceIdentifier selectedRaceIdentifier, String leaderboardName, String leaderboardGroupName, UUID eventId, RaceBoardViewConfiguration raceboardViewConfiguration, - ErrorReporter errorReporter, final StringMessages stringMessages, - UserAgentDetails userAgent, RaceTimesInfoProvider raceTimesInfoProvider, boolean showMapControls, boolean isScreenLargeEnoughToOfferChartSupport) { + ErrorReporter errorReporter, final StringMessages stringMessages, UserAgentDetails userAgent, + RaceTimesInfoProvider raceTimesInfoProvider, boolean showMapControls, boolean showChartMarkEditMediaButtonsAndVideo) { this.sailingService = sailingService; this.mediaService = mediaService; this.stringMessages = stringMessages; @@ -220,7 +220,7 @@ public class RaceBoardPanel extends SimplePanel implements LeaderboardUpdateList leaderboardPanel.setTitle(stringMessages.leaderboard()); leaderboardPanel.getElement().getStyle().setMarginLeft(6, Unit.PX); leaderboardPanel.getElement().getStyle().setMarginTop(10, Unit.PX); - createOneScreenView(leaderboardName, leaderboardGroupName, eventId, mainPanel, showMapControls, isScreenLargeEnoughToOfferChartSupport, isScreenLargeEnoughToInitiallyDisplayLeaderboard, raceMap, userService); // initializes the raceMap field + createOneScreenView(leaderboardName, leaderboardGroupName, eventId, mainPanel, showMapControls, showChartMarkEditMediaButtonsAndVideo, isScreenLargeEnoughToInitiallyDisplayLeaderboard, raceMap, userService); // initializes the raceMap field leaderboardPanel.addLeaderboardUpdateListener(this); // in case the URL configuration contains the name of a competitors filter set we try to activate it // FIXME the competitorsFilterSets has now moved to CompetitorSearchTextBox (which should probably be renamed); pass on the parameters to the LeaderboardPanel and see what it does with it @@ -234,52 +234,52 @@ public class RaceBoardPanel extends SimplePanel implements LeaderboardUpdateList } } timePanel = new RaceTimePanel(userService, timer, timeRangeWithZoomModel, stringMessages, raceTimesInfoProvider, - false, isScreenLargeEnoughToOfferChartSupport, selectedRaceIdentifier, getConfiguration().getInitialDurationAfterRaceStartInReplay()); + false, showChartMarkEditMediaButtonsAndVideo, selectedRaceIdentifier, getConfiguration().getInitialDurationAfterRaceStartInReplay()); timeRangeWithZoomModel.addTimeZoomChangeListener(timePanel); raceTimesInfoProvider.addRaceTimesInfoProviderListener(timePanel); } - /** - * @param event - * an optional event; may be null or else can be used to show some context information in - * the {@link GlobalNavigationPanel}. - * @param isScreenLargeEnoughToOfferChartSupport - * if the screen is large enough to display charts such as the competitor chart or the wind chart, a - * padding is provided for the RaceTimePanel that aligns its right border with that of the charts, and - * the charts are created. - */ private void createOneScreenView(String leaderboardName, String leaderboardGroupName, UUID event, FlowPanel mainPanel, - boolean showMapControls, boolean isScreenLargeEnoughToOfferChartSupport, boolean isScreenLargeEnoughToInitiallyDisplayLeaderboard, + boolean showMapControls, boolean showChartMarkEditMediaButtonsAndVideo, boolean isScreenLargeEnoughToInitiallyDisplayLeaderboard, RaceMap raceMap, UserService userService) { // create the default leaderboard and select the right race raceTimesInfoProvider.addRaceTimesInfoProviderListener(raceMap); List> components = new ArrayList>(); - if (isScreenLargeEnoughToOfferChartSupport) { - competitorChart = new MultiCompetitorRaceChart(sailingService, asyncActionsExecutor, competitorSelectionProvider, selectedRaceIdentifier, + if (showChartMarkEditMediaButtonsAndVideo) { + competitorChart = new MultiCompetitorRaceChart(sailingService, asyncActionsExecutor, competitorSelectionProvider, selectedRaceIdentifier, timer, timeRangeWithZoomModel, stringMessages, errorReporter, true, true, leaderboardGroupName, leaderboardName); competitorChart.getEntryWidget().setTitle(stringMessages.competitorCharts()); competitorChart.setVisible(false); components.add(competitorChart); - windChart = new WindChart(sailingService, selectedRaceIdentifier, timer, timeRangeWithZoomModel, new WindChartSettings(), + + windChart = new WindChart(sailingService, selectedRaceIdentifier, timer, timeRangeWithZoomModel, new WindChartSettings(), stringMessages, asyncActionsExecutor, errorReporter, /* compactChart */ true); windChart.setVisible(false); windChart.getEntryWidget().setTitle(stringMessages.windChart()); components.add(windChart); } + editMarkPassingPanel = new EditMarkPassingsPanel(sailingService, selectedRaceIdentifier, stringMessages, competitorSelectionProvider, errorReporter, timer); - editMarkPassingPanel.setLeaderboard(leaderboardPanel.getLeaderboard()); - editMarkPassingPanel.getEntryWidget().setTitle(stringMessages.editMarkPassings()); - components.add(editMarkPassingPanel); - editMarkPositionPanel = new EditMarkPositionPanel(raceMap, leaderboardPanel, selectedRaceIdentifier, leaderboardName, stringMessages, sailingService, timer, timeRangeWithZoomModel, - asyncActionsExecutor, errorReporter); - editMarkPositionPanel.setLeaderboard(leaderboardPanel.getLeaderboard()); - components.add(editMarkPositionPanel); - boolean autoSelectMedia = getConfiguration().isAutoSelectMedia(); + if (showChartMarkEditMediaButtonsAndVideo) { + editMarkPassingPanel.setLeaderboard(leaderboardPanel.getLeaderboard()); + editMarkPassingPanel.getEntryWidget().setTitle(stringMessages.editMarkPassings()); + components.add(editMarkPassingPanel); + } + + editMarkPositionPanel = new EditMarkPositionPanel(raceMap, leaderboardPanel, selectedRaceIdentifier, leaderboardName, + stringMessages, sailingService, timer, timeRangeWithZoomModel, asyncActionsExecutor, errorReporter); + if (showChartMarkEditMediaButtonsAndVideo) { + editMarkPositionPanel.setLeaderboard(leaderboardPanel.getLeaderboard()); + components.add(editMarkPositionPanel); + } + + boolean autoSelectMedia = getConfiguration().isAutoSelectMedia() && showChartMarkEditMediaButtonsAndVideo; MediaPlayerManagerComponent mediaPlayerManagerComponent = new MediaPlayerManagerComponent( selectedRaceIdentifier, raceTimesInfoProvider, timer, mediaService, userService, stringMessages, errorReporter, userAgent, this, autoSelectMedia); - leaderboardAndMapViewer = new SideBySideComponentViewer(leaderboardPanel, raceMap, mediaPlayerManagerComponent, components, stringMessages, userService, editMarkPassingPanel, editMarkPositionPanel); + leaderboardAndMapViewer = new SideBySideComponentViewer(leaderboardPanel, raceMap, mediaPlayerManagerComponent, + components, stringMessages, userService, editMarkPassingPanel, editMarkPositionPanel); this.setupUserManagementControlPanel(userService); componentViewers.add(leaderboardAndMapViewer); for (ComponentViewer componentViewer : componentViewers) { @@ -287,7 +287,7 @@ public class RaceBoardPanel extends SimplePanel implements LeaderboardUpdateList } boolean showLeaderboard = getConfiguration().isShowLeaderboard() && isScreenLargeEnoughToInitiallyDisplayLeaderboard; setLeaderboardVisible(showLeaderboard); - if (isScreenLargeEnoughToOfferChartSupport) { + if (showChartMarkEditMediaButtonsAndVideo) { setWindChartVisible(getConfiguration().isShowWindChart()); setCompetitorChartVisible(getConfiguration().isShowCompetitorsChart()); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 18aff760bc0..e1f5f53f692 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -158,6 +158,7 @@ import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.NotFoundException; import com.sap.sailing.domain.common.PassingInstruction; +import com.sap.sailing.domain.common.PathType; import com.sap.sailing.domain.common.PolarSheetsXYDiagramData; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RaceFetcher; @@ -430,7 +431,6 @@ import com.sap.sailing.server.operationaltransformation.UpdateServerConfiguratio import com.sap.sailing.server.operationaltransformation.UpdateSpecificRegatta; import com.sap.sailing.server.simulation.SimulationService; import com.sap.sailing.simulator.Path; -import com.sap.sailing.simulator.PathType; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationResults; import com.sap.sailing.simulator.TimedPositionWithSpeed; @@ -1494,7 +1494,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S PathDTO[] pathDTOs = new PathDTO[noOfPaths]; int index = noOfPaths - 1; for (Entry entry : paths.entrySet()) { - pathDTOs[index] = new PathDTO(entry.getKey().getTxtId()); + pathDTOs[index] = new PathDTO(entry.getKey()); // fill pathDTO with path points where speed is true wind speed List wList = new ArrayList(); for (TimedPositionWithSpeed p : entry.getValue().getPathPoints()) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java index d80af7aaee2..44332496f8e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java @@ -19,6 +19,7 @@ import java.util.logging.Logger; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.PathType; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; @@ -61,7 +62,6 @@ import com.sap.sailing.server.simulation.SimulationServiceFactory; import com.sap.sailing.simulator.BoatClassProperties; import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.Path; -import com.sap.sailing.simulator.PathType; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.Simulator; @@ -949,7 +949,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula // NOTE: pathName convention is: sort-digit + "#" + path-name // pathsAndNames is TreeMap which ensures sorting - pathDTOs[index] = new PathDTO(entry.getKey().getTxtId()); + pathDTOs[index] = new PathDTO(entry.getKey()); // fill pathDTO with path points where speed is true wind speed List wList = new ArrayList(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/PathDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/PathDTO.java index 74282eb6d3f..94968dcdcea 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/PathDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/PathDTO.java @@ -2,19 +2,31 @@ package com.sap.sailing.gwt.ui.shared; import java.util.List; +import com.sap.sailing.domain.common.PathType; import com.sap.sailing.domain.common.dto.NamedDTO; public class PathDTO extends NamedDTO { - private static final long serialVersionUID = 2381814072763342186L; + private static final long serialVersionUID = 4926185583314563898L; private List points; private boolean algorithmTimedOut; private boolean mixedLeg; + private PathType pathType; - public PathDTO() { + protected PathDTO() { + } + + public PathDTO(PathType pathType) { + super(pathType.getTxtId()); + this.pathType = pathType; } public PathDTO(final String name) { super(name); + this.pathType = null; + } + + public PathType getPathType() { + return pathType; } public List getPoints() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/PathNameFormatter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/PathNameFormatter.java new file mode 100644 index 00000000000..a08e751f583 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/PathNameFormatter.java @@ -0,0 +1,26 @@ +package com.sap.sailing.gwt.ui.shared.racemap; + +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.shared.PathDTO; + +public class PathNameFormatter { + + private final StringMessages stringMessages; + + public PathNameFormatter(StringMessages stringMessages) { + this.stringMessages = stringMessages; + } + + public String format(PathDTO path) { + switch (path.getPathType()) { + case OMNISCIENT: return stringMessages.omniscient(); + case OPPORTUNIST_LEFT: return stringMessages.opportunistLeft(); + case OPPORTUNIST_RIGHT: return stringMessages.opportunistRight(); + case ONE_TURNER_LEFT: return stringMessages.oneTurnerLeft(); + case ONE_TURNER_RIGHT: return stringMessages.oneTurnerRight(); + } + // fallback, if no path type is specified + return path.getName(); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java index 786a2b794c5..ed2ea9b35c6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java @@ -56,6 +56,7 @@ public class RaceSimulationOverlay extends FullCanvasOverlay { private final SailingServiceAsync sailingService; private final AsyncActionsExecutor asyncActionsExecutor; private final ColorPalette colors; + private final PathNameFormatter pathNameFormatter; private SimulatorResultsDTO simulationResult; private Boolean[] visiblePaths; private PathDTO racePath; @@ -70,6 +71,7 @@ public class RaceSimulationOverlay extends FullCanvasOverlay { this.stringMessages = stringMessages; this.asyncActionsExecutor = asyncActionsExecutor; this.colors = new ColorPaletteGenerator(); + this.pathNameFormatter = new PathNameFormatter(stringMessages); } public void updateLeg(int newLeg, boolean clearCanvas, long newVersion) { @@ -225,7 +227,7 @@ public class RaceSimulationOverlay extends FullCanvasOverlay { double timewidth = txtmet.getWidth(); double txtmaxwidth = 0.0; if (racePath != null) { - txtmet = context2d.measureText(racePath.getName().split("#")[1]); + txtmet = context2d.measureText(stringMessages.raceLeader()); txtmaxwidth = Math.max(txtmaxwidth, txtmet.getWidth()); } boolean containsTimeOut = false; @@ -238,7 +240,7 @@ public class RaceSimulationOverlay extends FullCanvasOverlay { if (path.getMixedLeg()) { containsMixedLeg = true; } - txtmet = context2d.measureText(path.getName().split("#")[1]); + txtmet = context2d.measureText(pathNameFormatter.format(path)); txtmaxwidth = Math.max(txtmaxwidth, txtmet.getWidth()); } double newwidth = 0; @@ -268,13 +270,13 @@ public class RaceSimulationOverlay extends FullCanvasOverlay { int canvasHeight = (int)Math.ceil(yOffset + rectHeight*(paths.length + (racePath == null? 0 : 1))); setCanvasSize(canvas, canvasWidth, canvasHeight); if (racePath != null) { - drawRectangleWithText(context2d, xOffset, yOffset, null, - racePath.getName().split("#")[1], getFormattedTime(racePath.getPathTime()), txtmaxwidth, timewidth, deltaTime, true); + drawRectangleWithText(context2d, xOffset, yOffset, null, stringMessages.raceLeader(), + getFormattedTime(racePath.getPathTime()), txtmaxwidth, timewidth, deltaTime, true); } for (PathDTO path : paths) { String timeText = (path.getMixedLeg() ? mixedLegText : (path.getAlgorithmTimedOut() ? algorithmTimedOutText : getFormattedTime(path.getPathTime()))); drawRectangleWithText(context2d, xOffset, yOffset + (paths.length-index-(racePath==null?1:0)) * rectHeight, this.colors.getColor(paths.length-1-index), - path.getName().split("#")[1], timeText, txtmaxwidth, timewidth, (path.getMixedLeg()?deltaMixedLeg:(path.getAlgorithmTimedOut()?deltaTimeOut:deltaTime)), visiblePaths[paths.length-1-index]); + pathNameFormatter.format(path), timeText, txtmaxwidth, timewidth, (path.getMixedLeg()?deltaMixedLeg:(path.getAlgorithmTimedOut()?deltaTimeOut:deltaTime)), visiblePaths[paths.length-1-index]); index++; } } @@ -350,12 +352,11 @@ public class RaceSimulationOverlay extends FullCanvasOverlay { simulationResult = result; PathDTO[] paths = result.getPaths(); if (result.getLegDuration() > 0) { - racePath = new PathDTO(); + racePath = new PathDTO("0#Race Leader"); List racePathPoints = new ArrayList(); racePathPoints.add(new SimulatorWindDTO(null, 0, 0, paths[0].getPoints().get(0).timepoint)); racePathPoints.add(new SimulatorWindDTO(null, 0, 0, paths[0].getPoints().get(0).timepoint + result.getLegDuration())); racePath.setPoints(racePathPoints); - racePath.setName("0#Race Leader"); } else { racePath = null; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java index 43b0aa7834d..baa5898a268 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java @@ -11,6 +11,7 @@ import com.google.gwt.i18n.client.TimeZone; import com.google.gwt.maps.client.MapWidget; import com.sap.sailing.domain.common.AbstractBearing; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.shared.racemap.CoordinateSystem; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; import com.sap.sailing.simulator.util.SailingSimulatorConstants; @@ -22,6 +23,8 @@ import com.sap.sailing.simulator.util.SailingSimulatorConstants; * */ public class PathLegendCanvasOverlay extends FullCanvasOverlay { + + private final StringMessages stringMessages; private List pathOverlays; @@ -43,13 +46,17 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay { private String algorithmTimedOutText = "out-of-bounds"; private String mixedLegText = "ambiguous wind"; - public PathLegendCanvasOverlay(MapWidget map, int zIndex, char mode, CoordinateSystem coordinateSystem) { + public PathLegendCanvasOverlay(MapWidget map, int zIndex, char mode, CoordinateSystem coordinateSystem, + StringMessages stringMessages) { super(map, zIndex, coordinateSystem); + this.stringMessages = stringMessages; this.mode = mode; if (this.mode == SailingSimulatorConstants.ModeEvent) { yOffset = 170; } setPathOverlays(null); + this.algorithmTimedOutText = stringMessages.simulationLegendAlgorithmTimedOutText(); + this.mixedLegText = stringMessages.simulationLegendMixedLegText(); } @Override @@ -138,7 +145,7 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay { double cY = yOffset + (pathOverlays.size()-1) * rectHeight + 75.0; context2d.setGlobalAlpha(0.80); context2d.setFillStyle("white"); - double bgWidth = 100.0; + double bgWidth = 120.0; double bgHeight = 70.0; if (this.curSpeed == 0.0) { bgHeight = 20.0; @@ -148,7 +155,7 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay { context2d.setFont(textFont); context2d.setFillStyle(textColor); //TextMetrics txtmet; - String cText = "Current: " + SimulatorMainPanel.formatSliderValue(curSpeed) + "kn"; + String cText = stringMessages.current() + ": " + stringMessages.knotsValue(curSpeed); txtmet = context2d.measureText(cText); double txtwidth = txtmet.getWidth(); context2d.fillText(cText, cX-(txtwidth/2.0), cY-25); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java index 66b06549926..8e5e6f18c06 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java @@ -50,6 +50,7 @@ import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; import com.sap.sailing.gwt.ui.shared.racemap.GoogleMapAPIKey; import com.sap.sailing.gwt.ui.shared.racemap.GoogleMapStyleHelper; +import com.sap.sailing.gwt.ui.shared.racemap.PathNameFormatter; import com.sap.sailing.gwt.ui.simulator.util.ColorPalette; import com.sap.sailing.gwt.ui.simulator.util.ColorPaletteGenerator; import com.sap.sailing.gwt.ui.simulator.util.SimulatorResources; @@ -110,7 +111,8 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ private static Logger LOGGER = Logger.getLogger(SimulatorMap.class.getName()); private static boolean SHOW_ONLY_PATH_POLYLINE = false; private char raceCourseDirection; - private final CoordinateSystem coordinateSystem; + private final CoordinateSystem coordinateSystem; + private final PathNameFormatter pathNameFormatter; public enum ViewName { SUMMARY, REPLAY, WINDDISPLAY @@ -182,7 +184,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ } ReplayPathCanvasOverlay replayPathCanvasOverlay = new ReplayPathCanvasOverlay(map, SimulatorMapOverlaysZIndexes.PATH_ZINDEX, - pathName.split("#")[1], timer, windParams, algorithmTimedOut, currentPath.getMixedLeg(), coordinateSystem); + pathNameFormatter.format(currentPath), timer, windParams, algorithmTimedOut, currentPath.getMixedLeg(), coordinateSystem); replayPathCanvasOverlays.add(replayPathCanvasOverlay); replayPathCanvasOverlay.setPathColor(colorPalette.getColor(Integer.parseInt(pathName.split("#")[0])-1)); @@ -337,6 +339,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ TimePanel timePanel, WindFieldGenParamsDTO windParams, SimpleBusyIndicator busyIndicator, char mode, SimulatorMainPanel parent, boolean showMapControls, CoordinateSystem coordinateSystem) { this.coordinateSystem = coordinateSystem; + this.pathNameFormatter = new PathNameFormatter(stringMessages); this.simulatorService = simulatorSvc; this.stringMessages = stringMessages; this.errorReporter = errorReporter; @@ -595,7 +598,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ windLineCanvasOverlay = new WindLineCanvasOverlay(map, SimulatorMapOverlaysZIndexes.WINDLINE_ZINDEX, timer, coordinateSystem); } replayPathCanvasOverlays = new ArrayList(); - legendCanvasOverlay = new PathLegendCanvasOverlay(map, SimulatorMapOverlaysZIndexes.PATHLEGEND_ZINDEX, mode, coordinateSystem); + legendCanvasOverlay = new PathLegendCanvasOverlay(map, SimulatorMapOverlaysZIndexes.PATHLEGEND_ZINDEX, mode, coordinateSystem, stringMessages); Window.addResizeHandler(new ResizeHandler() { @Override public void onResize(ResizeEvent event) { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationService.java index 29bc4cafb08..2cb465addda 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationService.java @@ -4,8 +4,8 @@ import java.util.Map; import java.util.concurrent.ExecutionException; import com.sap.sailing.domain.common.LegIdentifier; +import com.sap.sailing.domain.common.PathType; import com.sap.sailing.simulator.Path; -import com.sap.sailing.simulator.PathType; import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.SimulationResults; diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationServiceImpl.java index 11b9da2822f..7946320eb4b 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationServiceImpl.java @@ -26,6 +26,7 @@ import com.sap.sailing.domain.common.LegIdentifier; import com.sap.sailing.domain.common.LegIdentifierImpl; import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.NoWindException; +import com.sap.sailing.domain.common.PathType; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; @@ -42,7 +43,6 @@ import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; import com.sap.sailing.server.RacingEventService; import com.sap.sailing.simulator.Path; -import com.sap.sailing.simulator.PathType; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.SimulationResults; diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java index 5e126420003..43b08ca6c32 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java @@ -11,10 +11,10 @@ import junit.framework.Assert; import org.junit.Test; import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.PathType; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.simulator.Path; -import com.sap.sailing.simulator.PathType; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.impl.PolarDiagram49STG; diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SimulationResults.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SimulationResults.java index e26a93f4603..a22dc4a7034 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SimulationResults.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SimulationResults.java @@ -3,6 +3,7 @@ package com.sap.sailing.simulator; import java.util.Date; import java.util.Map; +import com.sap.sailing.domain.common.PathType; import com.sap.sailing.domain.common.Position; import com.sap.sse.common.TimePoint; diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/Simulator.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/Simulator.java index 03dfcd9043b..2b8744ef29f 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/Simulator.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/Simulator.java @@ -2,6 +2,7 @@ package com.sap.sailing.simulator; import java.util.List; +import com.sap.sailing.domain.common.PathType; import com.sap.sailing.simulator.impl.SparseSimulationDataException; diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorImpl.java index d993a4bd300..a4d76d0807d 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorImpl.java @@ -4,8 +4,8 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; +import com.sap.sailing.domain.common.PathType; import com.sap.sailing.simulator.Path; -import com.sap.sailing.simulator.PathType; import com.sap.sailing.simulator.RaceProperties; import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.Simulator; diff --git a/mobile/.gitignore b/mobile/.gitignore index eaa5cd3cc18..850c7ad7ed7 100644 --- a/mobile/.gitignore +++ b/mobile/.gitignore @@ -1,3 +1,4 @@ com.sap.sailing.android.tracking.app.test-test/bin/ /target -build.info \ No newline at end of file +build.info +assets_gen \ No newline at end of file diff --git a/mobile/com.sap.sailing.android.shared/build.gradle b/mobile/com.sap.sailing.android.shared/build.gradle index 26ee1c69d88..82321d8bf32 100644 --- a/mobile/com.sap.sailing.android.shared/build.gradle +++ b/mobile/com.sap.sailing.android.shared/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.library' +apply plugin: 'com.getkeepsafe.dexcount' android { buildToolsVersion rootProject.ext.buildTools diff --git a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/AppUtils.java b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/AppUtils.java index 64bff031398..8b51c6e4084 100644 --- a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/AppUtils.java +++ b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/AppUtils.java @@ -76,13 +76,21 @@ public class AppUtils { /** * Read the build.info from the asset folder, which is written at build time with git information * - * @return content of the build.info file or an empty string, if no file was found + * @return content of the build.info (or build_gradle.info) file or an empty string, if no file was found */ public String getBuildInfo() { String buildInfo = ""; BufferedReader reader = null; try { - reader = new BufferedReader(new InputStreamReader(mContext.getAssets().open("build.info"))); + String file = "build.info"; + String[] files = mContext.getAssets().list(""); + for (String item : files) { + if ("build_gradle.info".equals(item)) { + file = item; + break; + } + } + reader = new BufferedReader(new InputStreamReader(mContext.getAssets().open(file))); buildInfo = reader.readLine(); } catch (IOException e) { Log.d(TAG, "Can't open file with build info", e); diff --git a/mobile/com.sap.sailing.android.tracking.app/build.gradle b/mobile/com.sap.sailing.android.tracking.app/build.gradle index 28f1e791c1f..ef0f71c26ea 100644 --- a/mobile/com.sap.sailing.android.tracking.app/build.gradle +++ b/mobile/com.sap.sailing.android.tracking.app/build.gradle @@ -1,4 +1,5 @@ apply plugin: "com.android.application" +apply plugin: 'com.getkeepsafe.dexcount' apply from: "../git.gradle" def packageName="com.sap.sailing.android.tracking.app" @@ -22,7 +23,7 @@ android { manifest.srcFile "AndroidManifest.xml" java.srcDirs = ["src"] res.srcDirs = ["res"] - assets.srcDirs = ["assets"] + assets.srcDirs = ["assets", "assets_gen"] } androidTest { diff --git a/mobile/com.sap.sailing.buoy.positioning/build.gradle b/mobile/com.sap.sailing.buoy.positioning/build.gradle index 114f6e5a44b..0df5cff8868 100644 --- a/mobile/com.sap.sailing.buoy.positioning/build.gradle +++ b/mobile/com.sap.sailing.buoy.positioning/build.gradle @@ -1,4 +1,5 @@ apply plugin: "com.android.application" +apply plugin: 'com.getkeepsafe.dexcount' apply from: "../git.gradle" def packageName="com.sap.sailing.android.buoy.positioning.app" @@ -22,7 +23,7 @@ android { manifest.srcFile "AndroidManifest.xml" java.srcDirs = ["src"] res.srcDirs = ["res"] - assets.srcDirs = ["assets"] + assets.srcDirs = ["assets", "assets_gen"] } androidTest { diff --git a/mobile/com.sap.sailing.racecommittee.app/build.gradle b/mobile/com.sap.sailing.racecommittee.app/build.gradle index 78e5e863cb1..d8044526281 100644 --- a/mobile/com.sap.sailing.racecommittee.app/build.gradle +++ b/mobile/com.sap.sailing.racecommittee.app/build.gradle @@ -1,4 +1,5 @@ apply plugin: "com.android.application" +apply plugin: 'com.getkeepsafe.dexcount' apply from: "../git.gradle" def packageName="com.sap.sailing.racecommittee.app" @@ -25,7 +26,7 @@ android { manifest.srcFile "AndroidManifest.xml" java.srcDirs = ["src"] res.srcDirs = ["res"] - assets.srcDirs = ["assets"] + assets.srcDirs = ["assets", "assets_gen"] } androidTest { diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml b/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml index 2394e54f3a8..d996574e796 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml @@ -78,13 +78,14 @@ Wenderichtung auswählen Strafe auswählen - - Backbord - Steuerbord - Gate - Linie - Ablauf - + Leer + Backbord + Steuerbord + Einzel, unbekannt + Gate + Linie + Ablauf + Feste Peilung Veröffentlichen Zurücknehmen diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml b/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml index 56c8a122899..9e26b4bb2ea 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml @@ -91,13 +91,14 @@ Pick a rounding direction Select penalty reason - - Port - Starboard - Gate - Line - Offset - + None + Port + Starboard + Single, Unknown + Gate + Line + Offset + Fixed Bearing Publish Unpublish diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/CourseFragmentMarks.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/CourseFragmentMarks.java index 704b1d7a159..85fa591c342 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/CourseFragmentMarks.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/CourseFragmentMarks.java @@ -453,15 +453,61 @@ public class CourseFragmentMarks extends CourseFragment implements CourseMarkAda private void createPassingInstructionDialog(final CourseListDataElementWithIdImpl courseElement) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_AlertDialog); - builder.setTitle(R.string.pick_a_rounding_direction).setItems(R.array.rounding_directions, new DialogInterface.OnClickListener() { + final PassingInstruction[] passingInstructionsRelevantForUserEntry = PassingInstruction.relevantValues(); + final CharSequence[] i18NPassingInstructions = getI18NPassingInstructions(passingInstructionsRelevantForUserEntry); + builder.setTitle(R.string.pick_a_rounding_direction).setItems(i18NPassingInstructions, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int position) { - PassingInstruction pickedDirection = PassingInstruction.relevantValues()[position]; + PassingInstruction pickedDirection = passingInstructionsRelevantForUserEntry[position]; onPassingInstructionPicked(courseElement, pickedDirection); } }); builder.create().show(); } + /** + * Cosntructs a message text for each of the {@link PassingIntsruction} values passed. The message strings + * returned correspond in their order with the {@link PassingInstruction}s passed in the array. + */ + private CharSequence[] getI18NPassingInstructions(PassingInstruction[] passingInstructionsRelevantForUserEntry) { + final CharSequence[] result = new CharSequence[passingInstructionsRelevantForUserEntry.length]; + int i=0; + for (final PassingInstruction passingInstruction : passingInstructionsRelevantForUserEntry) { + result[i++] = getI18NPassingInstruction(passingInstruction); + } + return result; + } + + private CharSequence getI18NPassingInstruction(PassingInstruction passingInstruction) { + CharSequence result = ""; + switch (passingInstruction) { + case FixedBearing: + result = getString(R.string.passing_instruction_fixed_bearing); + break; + case Gate: + result = getString(R.string.passing_instruction_gate); + break; + case Line: + result = getString(R.string.passing_instruction_line); + break; + case None: + result = getString(R.string.passing_instruction_none); + break; + case Offset: + result = getString(R.string.passing_instruction_offset); + break; + case Port: + result = getString(R.string.passing_instruction_port); + break; + case Single_Unknown: + result = getString(R.string.passing_instruction_single_unknown); + break; + case Starboard: + result = getString(R.string.passing_instruction_starboard); + break; + } + return result; + } + protected void onPassingInstructionPicked(CourseListDataElementWithIdImpl courseElement, PassingInstruction pickedDirection) { courseElement.setPassingInstructions(pickedDirection); if (!PassingInstruction.Gate.equals(pickedDirection) && !PassingInstruction.Line.equals(pickedDirection)) { diff --git a/mobile/git.gradle b/mobile/git.gradle index a62b6b3ed8e..33eefa6e6b2 100644 --- a/mobile/git.gradle +++ b/mobile/git.gradle @@ -42,7 +42,9 @@ preBuild.doLast { // save the combined build info into assets/build.info file def result_line = git_branch_or_tag + "-" + git_version + " (" + build_time + ")\n" - def assetsDir = android.sourceSets.main.assets.srcDirs.toArray()[0] - def buildInfoFile = new File(assetsDir, 'build.info').getAbsolutePath() + def assetsDir = android.sourceSets.main.assets.srcDirs.toArray()[1] + delete(assetsDir) + mkdir(assetsDir) + def buildInfoFile = new File(assetsDir, 'build_gradle.info').getAbsolutePath() new File(buildInfoFile).write(result_line) }