Merge remote-tracking branch 'origin/master' into design2015

This commit is contained in:
Steffen Schaefer committed 2015-07-20 13:30:45 +02:00
commit af3d40ab47
109 files changed
+2422 -1132

No files matched your search

Binary file not shown.
@@ -4,7 +4,7 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
public enum BoatClassMasterdata {
_18Footer ("18Footer", true, 8.90, 2.00, BoatHullType.MONOHULL, true, "18.Footer", "18ft", "18ft Skiff"),
_18Footer ("18Footer", true, 8.90, 2.00, BoatHullType.MONOHULL, true, "18.Footer", "18ft", "18ft Skiff", "18. Footer"),
_2_4M ("2.4 Meter", true, 4.11, 0.81, BoatHullType.MONOHULL, false, "2.4M", "2.4mR", "2.4 Metre", "2.4-metre"),
_5O5 ("5O5", true, 5.03, 1.88, BoatHullType.MONOHULL, true, "505", "5o5", "505er", "5o5er"),
_12M ("12 Meter", true, 5.97, 1.43, BoatHullType.MONOHULL, true, "12M", "12mR", "12SQM", "12-metre", "12 metre"),
@@ -31,7 +31,7 @@ public enum BoatClassMasterdata {
FINN ("Finn", true, 4.50, 1.51, BoatHullType.MONOHULL, false),
FLYING_PHANTOM ("Flying Phantom", true, 5.52, 3.00, BoatHullType.CATAMARAN, true),
FOLKBOAT ("Folkboat", true, 7.68, 2.20, BoatHullType.MONOHULL, false, "Folke", "Folkeboot"),
FUN ("FUN", true, 7.20, 2.45, BoatHullType.MONOHULL, true, "FUN O.D.", "FUN OD"),
FUN ("FUN", true, 7.20, 2.45, BoatHullType.MONOHULL, true, "FUN O.D.", "FUN OD", "Open FUN"),
F_16 ("Formula 16", true, 5.00, 2.50, BoatHullType.CATAMARAN, true, "F16", "F-16"),
HOBIE_16 ("Hobie 16", true, 5.05, 2.41, BoatHullType.CATAMARAN, false, "H16"),
H_BOAT ("H-Boat", true, 8.28, 2.18, BoatHullType.MONOHULL, true, "HB"),
@@ -6,7 +6,7 @@ package com.sap.sailing.domain.common.racelog;
*
*/
public enum RaceLogRaceStatus {
UNKNOWN(0), UNSCHEDULED(1), SCHEDULED(2), STARTPHASE(3), RUNNING(4), FINISHING(5), FINISHED(6);
UNKNOWN(0), UNSCHEDULED(1), PRESCHEDULED(2), SCHEDULED(3), STARTPHASE(4), RUNNING(5), FINISHING(6), FINISHED(7);
/** the order number represents the natural order of the states */
private int orderNumber;
@@ -16,11 +16,9 @@ public enum RaceLogRaceStatus {
}
public static boolean isActive(RaceLogRaceStatus status) {
if (status == null) {
return false;
}
return status != null &&
(status.equals(PRESCHEDULED) || status.equals(SCHEDULED) || status.equals(STARTPHASE) || status.equals(RUNNING) || status.equals(FINISHING));
return status.equals(SCHEDULED) || status.equals(STARTPHASE) || status.equals(RUNNING) || status.equals(FINISHING);
}
public int getOrderNumber() {
@@ -63,108 +63,137 @@ public class DependentStartTimeFinderTest {
@Test
public void testDependentStartTimeUpdate() {
final MillisecondsDurationImpl cAfterB = new MillisecondsDurationImpl(5000);
raceLogC.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("B", "", ""), new MillisecondsDurationImpl(5000)));
new SimpleRaceLogIdentifierImpl("B", "", ""), cAfterB));
StartTimeFinder finder = new StartTimeFinder(raceLogResolver, raceLogC);
//Tests for correct behaviour, when race depending on has no starttime set
// Tests for correct behaviour, when race depending on has no start time set
assertNull(finder.analyze().getStartTime());
final MillisecondsDurationImpl bAfterA = new MillisecondsDurationImpl(6000);
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000)));
new SimpleRaceLogIdentifierImpl("A", "", ""), bAfterA));
finder = new StartTimeFinder(raceLogResolver, raceLogB);
StartTimeFinderResult result = finder.analyze();
List<SimpleRaceLogIdentifier> expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
List<SimpleRaceLogIdentifier> expectedDependingOnRaces = new ArrayList<>();
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("A", "", ""));
assertNull(result.getStartTime());
assertEquals(bAfterA, result.getStartTimeDiff());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
// assertEquals(5000, result.getStartTimeDiff().asMillis());
finder = new StartTimeFinder(raceLogResolver, raceLogC);
//Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time is unknown
expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
// Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time
// is unknown
expectedDependingOnRaces = new ArrayList<>();
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("B", "", ""));
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("A", "", ""));
result = finder.analyze();
assertNull(result.getStartTime());
assertEquals(cAfterB, result.getStartTimeDiff());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
MillisecondsTimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(5000)));
finder = new StartTimeFinder(raceLogResolver, raceLogB);
assertEquals(10000, finder.analyze().getStartTime().asMillis());
finder = new StartTimeFinder(raceLogResolver, raceLogC);
assertEquals(15000, finder.analyze().getStartTime().asMillis());
result = finder.analyze();
assertEquals(11000, result.getStartTime().asMillis());
assertEquals(6000, result.getStartTimeDiff().asMillis());
//Test correct behaviour, when middle element changes
finder = new StartTimeFinder(raceLogResolver, raceLogC);
result = finder.analyze();
assertEquals(16000, result.getStartTime().asMillis());
assertEquals(5000, result.getStartTimeDiff().asMillis());
// Test correct behaviour, when middle element changes
raceLogB.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(15000)));
finder = new StartTimeFinder(raceLogResolver, raceLogA);
assertEquals(5000, finder.analyze().getStartTime().asMillis());
result = finder.analyze();
assertEquals(5000, result.getStartTime().asMillis());
assertNull(result.getStartTimeDiff());
finder = new StartTimeFinder(raceLogResolver, raceLogB);
assertEquals(15000, finder.analyze().getStartTime().asMillis());
result = finder.analyze();
assertEquals(15000, result.getStartTime().asMillis());
assertNull(result.getStartTimeDiff());
finder = new StartTimeFinder(raceLogResolver, raceLogC);
assertEquals(20000, finder.analyze().getStartTime().asMillis());
result = finder.analyze();
assertEquals(20000, result.getStartTime().asMillis());
assertEquals(5000, result.getStartTimeDiff().asMillis());
}
@Test
public void testDependentStartTimeCycle() {
final MillisecondsDurationImpl cAfterB = new MillisecondsDurationImpl(6000);
raceLogC.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("B", "", ""), new MillisecondsDurationImpl(5000)));
new SimpleRaceLogIdentifierImpl("B", "", ""), cAfterB));
final MillisecondsDurationImpl bAfterA = new MillisecondsDurationImpl(7000);
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000)));
new SimpleRaceLogIdentifierImpl("A", "", ""), bAfterA));
final MillisecondsDurationImpl aAfterC = new MillisecondsDurationImpl(8000);
raceLogA.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("C", "", ""), new MillisecondsDurationImpl(5000)));
new SimpleRaceLogIdentifierImpl("C", "", ""), aAfterC));
//Check that all resolve to null in case of a cycle
// Check that all resolve to null in case of a cycle
StartTimeFinder finder = new StartTimeFinder(raceLogResolver, raceLogC);
assertNull(finder.analyze().getStartTime());
assertEquals(cAfterB, finder.analyze().getStartTimeDiff());
finder = new StartTimeFinder(raceLogResolver, raceLogB);
assertNull(finder.analyze().getStartTime());
assertEquals(bAfterA, finder.analyze().getStartTimeDiff());
finder = new StartTimeFinder(raceLogResolver, raceLogA);
assertNull(finder.analyze().getStartTime());
assertEquals(aAfterC, finder.analyze().getStartTimeDiff());
//Check that all resolve correctly after changing some element in cycle
// Check that all resolve correctly after changing some element in cycle
MillisecondsTimePoint now = MillisecondsTimePoint.now();
raceLogB.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12,
new MillisecondsTimePoint(5000)));
raceLogB.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(5000)));
//now A -> C -> B
// now A -> C -> B
finder = new StartTimeFinder(raceLogResolver, raceLogA);
//Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time is unknown
List<SimpleRaceLogIdentifier> expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
// Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time
// is unknown
List<SimpleRaceLogIdentifier> expectedDependingOnRaces;
expectedDependingOnRaces = new ArrayList<>();
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("C", "", ""));
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("B", "", ""));
StartTimeFinderResult result = finder.analyze();
assertEquals(15000, result.getStartTime().asMillis());
assertEquals(19000, result.getStartTime().asMillis());
assertEquals(8000, result.getStartTimeDiff().asMillis());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
//now C -> B
// now C -> B
finder = new StartTimeFinder(raceLogResolver, raceLogC);
//Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time is unknown
expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
// Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time
// is unknown
expectedDependingOnRaces = new ArrayList<>();
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("B", "", ""));
result = finder.analyze();
assertEquals(10000, result.getStartTime().asMillis());
assertEquals(11000, result.getStartTime().asMillis());
assertEquals(6000, result.getStartTimeDiff().asMillis());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
//now B
// now B
finder = new StartTimeFinder(raceLogResolver, raceLogB);
//Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time is unknown
expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
// Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time
// is unknown
expectedDependingOnRaces = new ArrayList<>();
result = finder.analyze();
assertEquals(5000, result.getStartTime().asMillis());
assertNull(result.getStartTimeDiff());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
}
}
@@ -13,13 +13,12 @@ import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sse.common.TimePoint;
public class StartTimeFinderTest extends
PassAwareRaceLogAnalyzerTest<StartTimeFinder, StartTimeFinderResult> {
public class StartTimeFinderTest extends PassAwareRaceLogAnalyzerTest<StartTimeFinder, StartTimeFinderResult> {
@Override
protected StartTimeFinder createAnalyzer(RaceLog raceLog) {
@@ -30,7 +29,7 @@ public class StartTimeFinderTest extends
protected TargetPair getTargetEventsAndResultForPassAwareTests(int passId, AbstractLogEventAuthor author) {
RaceLogStartTimeEvent event = createEvent(RaceLogStartTimeEvent.class, 1, passId, author);
when(event.getStartTime()).thenReturn(mock(TimePoint.class));
return new TargetPair(Arrays.asList(event), new StartTimeFinderResult(event.getStartTime()));
return new TargetPair(Arrays.asList(event), new StartTimeFinderResult(event.getStartTime(), null));
}
@Test
@@ -1,8 +1,10 @@
package com.sap.sailing.domain.abstractlog.race;
import java.io.Serializable;
import com.sap.sse.common.Util.Triple;
public interface SimpleRaceLogIdentifier {
public interface SimpleRaceLogIdentifier extends Serializable {
String getRegattaLikeParentName();
String getRaceColumnName();
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.abstractlog.race.analyzing.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -16,33 +17,37 @@ public class DependentStartTimeResolver {
public DependentStartTimeResolver(RaceLogResolver raceLogResolver) {
this.raceLogResolver = raceLogResolver;
}
public StartTimeFinderResult resolve(RaceLogDependentStartTimeEvent event) {
List<SimpleRaceLogIdentifier> dependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
List<SimpleRaceLogIdentifier> dependingOnRaces = Collections.emptyList();
return internalResolve(event, dependingOnRaces);
}
StartTimeFinderResult internalResolve(RaceLogDependentStartTimeEvent event, Iterable<SimpleRaceLogIdentifier> dependingOnRaces) {
StartTimeFinderResult internalResolve(RaceLogDependentStartTimeEvent event,Iterable<SimpleRaceLogIdentifier> dependingOnRaces) {
SimpleRaceLogIdentifier identifier = event.getDependentOnRaceIdentifier();
Duration startTimeDifference = event.getStartTimeDifference();
RaceLog raceLog = raceLogResolver.resolve(identifier);
final StartTimeFinderResult result;
if (raceLog == null) {
result = new StartTimeFinderResult(dependingOnRaces, null, startTimeDifference);
} else {
List<SimpleRaceLogIdentifier> extendedDependingOnRaces = new ArrayList<>();
Util.addAll(dependingOnRaces, extendedDependingOnRaces);
extendedDependingOnRaces.add(identifier);
final StartTimeFinderResult result;
if (containsCycle(extendedDependingOnRaces)) {
result = new StartTimeFinderResult(extendedDependingOnRaces, null);
result = new StartTimeFinderResult(extendedDependingOnRaces, null, null);
} else {
StartTimeFinder dependentStartTimeFinder = new StartTimeFinder(raceLogResolver, raceLog);
StartTimeFinderResult resultOfDependentRace = dependentStartTimeFinder.analyze(extendedDependingOnRaces);
if (resultOfDependentRace.getStartTime() == null) {
resultOfDependentRace.setStartTimeDiff(startTimeDifference);
result = resultOfDependentRace;
} else {
result = new StartTimeFinderResult(resultOfDependentRace.getRacesDependingOn(),
resultOfDependentRace.getStartTime() == null ? null : resultOfDependentRace.getStartTime()
.plus(startTimeDifference));
resultOfDependentRace.getStartTime().plus(startTimeDifference), startTimeDifference);
}
}
}
return result;
@@ -44,15 +44,14 @@ public class RaceStatusAnalyzer extends RaceLogAnalyzer<RaceLogRaceStatus> {
@Override
protected RaceLogRaceStatus performAnalysis() {
ArrayListNavigableSet<RaceLogRaceStatusEvent> statusEvents = new ArrayListNavigableSet<RaceLogRaceStatusEvent>(RaceLogRaceStatusEventComparator.INSTANCE);
ArrayListNavigableSet<RaceLogRaceStatusEvent> statusEvents = new ArrayListNavigableSet<>(RaceLogRaceStatusEventComparator.INSTANCE);
for(RaceLogEvent event: getPassEvents()) {
if(event instanceof RaceLogRaceStatusEvent) {
statusEvents.add((RaceLogRaceStatusEvent) event);
}
}
for (RaceLogRaceStatusEvent event : statusEvents.descendingSet()) {
RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
statusEvent.accept(eventDispatcher);
event.accept(eventDispatcher);
return eventDispatcher.nextStatus;
}
@@ -79,7 +78,7 @@ public class RaceStatusAnalyzer extends RaceLogAnalyzer<RaceLogRaceStatus> {
private void setRaceLogStatusBasedOnStartTime(TimePoint startTime) {
if (startTime == null) {
nextStatus = RaceLogRaceStatus.UNKNOWN;
nextStatus = RaceLogRaceStatus.PRESCHEDULED;
return;
}
TimePoint now = clock.now();
@@ -29,13 +29,13 @@ public class StartTimeFinder extends RaceLogAnalyzer<StartTimeFinderResult> {
for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogStartTimeEvent) {
TimePoint startTime = ((RaceLogStartTimeEvent) event).getStartTime();
return new StartTimeFinderResult(dependingOnRaces, startTime);
return new StartTimeFinderResult(dependingOnRaces, startTime, null);
} else if (event instanceof RaceLogDependentStartTimeEvent) {
DependentStartTimeResolver dependentStartTimeResolver = new DependentStartTimeResolver(resolver);
return dependentStartTimeResolver.internalResolve((RaceLogDependentStartTimeEvent) event, dependingOnRaces);
}
}
return new StartTimeFinderResult(dependingOnRaces, null);
return new StartTimeFinderResult(dependingOnRaces, null, null);
} finally {
log.unlockAfterRead();
}
@@ -1,29 +1,25 @@
package com.sap.sailing.domain.abstractlog.race.analyzing.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
public class StartTimeFinderResult {
private final Iterable<SimpleRaceLogIdentifier> racesDependingOn;
private final TimePoint startTime;
private Duration startTimeDiff;
public StartTimeFinderResult(final Iterable<SimpleRaceLogIdentifier> racesDependingOn, final TimePoint startTime) {
this.racesDependingOn = racesDependingOn;
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> racesDependingOn, TimePoint startTime, Duration startTimeDiff) {
this.startTime = startTime;
this.startTimeDiff = startTimeDiff;
this.racesDependingOn = racesDependingOn;
}
public StartTimeFinderResult(TimePoint startTime) {
racesDependingOn = new ArrayList<SimpleRaceLogIdentifier>();
this.startTime = startTime;
}
public StartTimeFinderResult(List<SimpleRaceLogIdentifier> racesDependingOn, TimePoint startTime) {
this.racesDependingOn = racesDependingOn;
this.startTime = startTime;
public StartTimeFinderResult(TimePoint startTime, Duration startTimeDiff) {
this(/* racesDependingOn */ Collections.<SimpleRaceLogIdentifier>emptyList(), startTime, startTimeDiff);
}
public Iterable<SimpleRaceLogIdentifier> getRacesDependingOn() {
@@ -34,11 +30,16 @@ public class StartTimeFinderResult {
return startTime;
}
public boolean isDependentStartTime() {
if (racesDependingOn == null) {
return false;
public Duration getStartTimeDiff() {
return startTimeDiff;
}
return Util.isEmpty(racesDependingOn);
public void setStartTimeDiff(Duration startTimeDiff) {
this.startTimeDiff = startTimeDiff;
}
public boolean isDependentStartTime() {
return racesDependingOn != null && !Util.isEmpty(racesDependingOn);
}
@Override
@@ -47,6 +48,7 @@ public class StartTimeFinderResult {
int result = 1;
result = prime * result + ((racesDependingOn == null) ? 0 : racesDependingOn.hashCode());
result = prime * result + ((startTime == null) ? 0 : startTime.hashCode());
result = prime * result + ((startTimeDiff == null) ? 0 : startTimeDiff.hashCode());
return result;
}
@@ -69,6 +71,11 @@ public class StartTimeFinderResult {
return false;
} else if (!startTime.equals(other.startTime))
return false;
if (startTimeDiff == null) {
if (other.startTimeDiff != null)
return false;
} else if (!startTimeDiff.equals(other.startTimeDiff))
return false;
return true;
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.abstractlog.race.impl;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
public class SimpleRaceLogIdentifierImpl implements SimpleRaceLogIdentifier {
private static final long serialVersionUID = 5377117723950808853L;
protected final String regattaLikeParentName;
protected final String raceColumnName;
protected final String fleetName;
@@ -187,4 +187,9 @@ public interface RaceState extends ReadonlyRaceState {
void setAdditionalScoringInformationEnabled(TimePoint creationTimePoint, boolean enable, AdditionalScoringInformationType informationType);
boolean isAdditionalScoringInformationEnabled(AdditionalScoringInformationType informationType);
/**
* forces a RaceStateUpdate
*/
void forceUpdate();
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.abstractlog.race.state.impl;
import java.util.Collections;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -17,6 +18,7 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceStatusAnalyzer
import com.sap.sailing.domain.abstractlog.race.scoring.AdditionalScoringInformationType;
import com.sap.sailing.domain.abstractlog.race.scoring.RaceLogAdditionalScoringInformationEvent;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedureFactory;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedurePrerequisite;
@@ -42,6 +44,10 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
private final AbstractLogEventAuthor author;
private final RaceLogEventFactory factory;
public static ReadonlyRaceState create(RaceLogResolver raceLogResolver, RaceLog raceLog) {
return create(raceLogResolver, raceLog, /* forRaceLogIdentifier */ null, Collections.<SimpleRaceLogIdentifier, ReadonlyRaceState>emptyMap());
}
/**
* Creates a {@link RaceState} with the initial racing procedure type set to a fallback value and an empty configuration.
*/
@@ -63,9 +69,11 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
this(raceLogResolver, raceLog, author, eventFactory, new RaceStatusAnalyzer.StandardClock(), procedureFactory);
}
private RaceStateImpl(RaceLogResolver raceLogResolver, RaceLog raceLog, AbstractLogEventAuthor author, RaceLogEventFactory eventFactory, RaceStatusAnalyzer.Clock analyzersClock,
private RaceStateImpl(RaceLogResolver raceLogResolver, RaceLog raceLog, AbstractLogEventAuthor author,
RaceLogEventFactory eventFactory, RaceStatusAnalyzer.Clock analyzersClock,
RacingProcedureFactory procedureFactory) {
super(raceLogResolver, raceLog, analyzersClock, procedureFactory, /* update */ true);
super(raceLogResolver, raceLog, /* forRaceLogIdentifier */ null, analyzersClock, procedureFactory,
Collections.<SimpleRaceLogIdentifier, ReadonlyRaceState>emptyMap(), /* update */ true);
this.author = author;
this.factory = eventFactory;
}
@@ -221,4 +229,10 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
return event != null;
}
@Override
public void forceUpdate() {
super.update();
registerListenerOnDependentRaceIfDependentStartTime(Collections.<SimpleRaceLogIdentifier, ReadonlyRaceState>emptyMap());
}
}
@@ -1,11 +1,16 @@
package com.sap.sailing.domain.abstractlog.race.state.impl;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogChangedListener;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.ConfirmedFinishPositioningListFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishPositioningListFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishedTimeFinder;
@@ -15,10 +20,10 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.LastWindFixFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.ProtestStartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceStatusAnalyzer;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceStatusAnalyzer.Clock;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RacingProcedureTypeAnalyzer;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sailing.domain.abstractlog.race.impl.WeakRaceLogChangedVisitor;
import com.sap.sailing.domain.abstractlog.race.state.RaceStateChangedListener;
import com.sap.sailing.domain.abstractlog.race.state.RaceStateEvent;
@@ -49,21 +54,41 @@ import com.sap.sse.common.Util;
*/
public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedListener {
public static ReadonlyRaceState create(RaceLogResolver raceLogResolver, RaceLog raceLog) {
return create(raceLogResolver, raceLog, /* forRaceLogIdentifier */null,
Collections.<SimpleRaceLogIdentifier, ReadonlyRaceState> emptyMap());
}
/**
* Creates a {@link ReadonlyRaceState}.
*
* @param forRaceLogIdentifier
* If known, callers can specify the simple race log identifier for the race log to be wrapped by this
* new race state. This can help avoid endless recursions because this race state can be passed along
* together with the identifier so that when again along a dependent call chain a race state is required
* for that race log identifier, this one can be used.
*/
public static ReadonlyRaceState create(RaceLogResolver raceLogResolver, RaceLog raceLog,
ConfigurationLoader<RegattaConfiguration> configurationLoader) {
return new ReadonlyRaceStateImpl(raceLogResolver, raceLog, new ReadonlyRacingProcedureFactory(
configurationLoader));
SimpleRaceLogIdentifier forRaceLogIdentifier,
ConfigurationLoader<RegattaConfiguration> configurationLoader,
Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates) {
return new ReadonlyRaceStateImpl(raceLogResolver, raceLog, forRaceLogIdentifier, new ReadonlyRacingProcedureFactory(
configurationLoader), dependentRaceStates);
}
/**
* Creates a {@link ReadonlyRaceState} with an empty configuration ( {@link EmptyRegattaConfiguration} ).
*
* @param forRaceLogIdentifier
* If known, callers can specify the simple race log identifier for the race log to be wrapped by this
* new race state. This can help avoid endless recursions because this race state can be passed along
* together with the identifier so that when again along a dependent call chain a race state is required
* for that race log identifier, this one can be used.
*/
public static ReadonlyRaceState create(RaceLogResolver raceLogResolver, RaceLog raceLog) {
return new ReadonlyRaceStateImpl(raceLogResolver, raceLog, new ReadonlyRacingProcedureFactory(
new EmptyRegattaConfiguration()));
public static ReadonlyRaceState create(RaceLogResolver raceLogResolver, RaceLog raceLog,
SimpleRaceLogIdentifier forRaceLogIdentifier, Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates) {
return new ReadonlyRaceStateImpl(raceLogResolver, raceLog, forRaceLogIdentifier, new ReadonlyRacingProcedureFactory(
new EmptyRegattaConfiguration()), dependentRaceStates);
}
/**
@@ -127,18 +152,26 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
private RaceStateChangedListener raceStateToObserveListener;
private ReadonlyRaceStateImpl(RaceLogResolver raceLogResolver, RaceLog raceLog,
RacingProcedureFactory procedureFactory) {
this(raceLogResolver, raceLog, new RaceStatusAnalyzer.StandardClock(), procedureFactory, /* update */true);
SimpleRaceLogIdentifier forRaceLogIdentifier, RacingProcedureFactory procedureFactory,
Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates) {
this(raceLogResolver, raceLog, forRaceLogIdentifier, new RaceStatusAnalyzer.StandardClock(),
procedureFactory, dependentRaceStates, /* update */true);
}
/**
* @param raceLogResolver
* @param forRaceLogIdentifier
* If known, callers can specify the simple race log identifier for the race log to be wrapped by this
* new race state. This can help avoid endless recursions because this race state can be passed along
* together with the identifier so that when again along a dependent call chain a race state is required
* for that race log identifier, this one can be used.
* @param update
* if <code>true</code>, the race state will be updated whenever the underlying <code>raceLog</code>
* changes.
*/
protected ReadonlyRaceStateImpl(RaceLogResolver raceLogResolver, RaceLog raceLog, Clock analyzersClock,
RacingProcedureFactory procedureFactory, boolean update) {
protected ReadonlyRaceStateImpl(RaceLogResolver raceLogResolver, RaceLog raceLog,
SimpleRaceLogIdentifier forRaceLogIdentifier, Clock analyzersClock,
RacingProcedureFactory procedureFactory,
Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates, boolean update) {
this.raceLog = raceLog;
this.raceLogResolver = raceLogResolver;
this.procedureFactory = procedureFactory;
@@ -178,8 +211,18 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
// We known that recreateRacingProcedure calls update() when done, therefore this RaceState
// will be fully initialized after this line
recreateRacingProcedure();
final Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStatesAndMe;
if (forRaceLogIdentifier != null) {
dependentRaceStatesAndMe = new HashMap<>(dependentRaceStates);
dependentRaceStatesAndMe.put(forRaceLogIdentifier, this);
} else {
dependentRaceStatesAndMe = dependentRaceStates;
}
registerListenerOnDependentRaceIfDependentStartTime(dependentRaceStatesAndMe);
}
// Check wether the latest known StartTimeEvent is a non-dependent or dependent start time in case of a
protected void registerListenerOnDependentRaceIfDependentStartTime(Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates) {
// Check whether the latest known StartTimeEvent is a non-dependent or dependent start time in case of a
// dependent startTime setup listeners
this.raceLog.lockForRead();
try {
@@ -187,7 +230,7 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
if (event instanceof RaceLogStartTimeEvent) {
break;
} else if (event instanceof RaceLogDependentStartTimeEvent) {
setupListenersOnDependentRace(event);
setupListenersOnDependentRace(event, dependentRaceStates);
break;
}
}
@@ -319,7 +362,7 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
@Override
public void eventAdded(RaceLogEvent event) {
if (event instanceof RaceLogDependentStartTimeEvent) {
setupListenersOnDependentRace(event);
setupListenersOnDependentRace(event, Collections.<SimpleRaceLogIdentifier, ReadonlyRaceState>emptyMap());
} else if (event instanceof RaceLogStartTimeEvent) {
if (raceStateToObserve != null) {
raceStateToObserve.removeChangedListener(raceStateToObserveListener);
@@ -329,15 +372,25 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
update();
}
private void setupListenersOnDependentRace(RaceLogEvent event) {
private void setupListenersOnDependentRace(RaceLogEvent event, Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates) {
if (raceStateToObserve != null) {
// Remove previous listeners
raceStateToObserve.removeChangedListener(raceStateToObserveListener);
raceStateToObserve = null;
}
RaceLogDependentStartTimeEvent dependentStartTimeEvent = (RaceLogDependentStartTimeEvent) event;
RaceLog resolvedRaceLog = raceLogResolver.resolve(dependentStartTimeEvent.getDependentOnRaceIdentifier());
raceStateToObserve = ReadonlyRaceStateImpl.create(raceLogResolver, resolvedRaceLog);
final SimpleRaceLogIdentifier dependentOnRaceIdentifier = dependentStartTimeEvent.getDependentOnRaceIdentifier();
if (dependentRaceStates.containsKey(dependentOnRaceIdentifier)) {
raceStateToObserve = dependentRaceStates.get(dependentOnRaceIdentifier);
raceStateToObserve.addChangedListener(raceStateToObserveListener);
} else {
RaceLog resolvedRaceLog = raceLogResolver.resolve(dependentOnRaceIdentifier);
if (resolvedRaceLog != null) {
raceStateToObserve = ReadonlyRaceStateImpl.create(raceLogResolver, resolvedRaceLog,
dependentOnRaceIdentifier, dependentRaceStates);
raceStateToObserve.addChangedListener(raceStateToObserveListener);
}
}
}
@Override
@@ -350,7 +403,7 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
return false;
}
private void update() {
protected void update() {
RacingProcedureType type = racingProcedureAnalyzer.analyze();
if (!Util.equalsWithNull(cachedRacingProcedureType, type) && type != RacingProcedureType.UNKNOWN) {
cachedRacingProcedureType = type;
@@ -90,6 +90,11 @@ public abstract class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
protected void testRace(String raceNumber) throws IOException, InterruptedException, URISyntaxException, ParseException, SubscriberInitializationException, CreateModelException {
setUp(raceNumber);
synchronized (getSemaphor()) {
while (!isStoredDataLoaded()) {
getSemaphor().wait();
}
}
testWholeRace();
testMiddleOfRace(0);
testMiddleOfRace(2);
@@ -11,6 +11,6 @@
</listAttribute>
<stringAttribute key="projectName" value="com.sap.sailing.gwt.ui"/>
<booleanAttribute key="showLaunchOutput" value="false"/>
<stringAttribute key="url" value="http://127.0.0.1:8888/gwt/Home.html"/>
<stringAttribute key="url" value="http://127.0.0.1:8888/gwt/AdminConsole.html"/>
<stringAttribute key="urlQueryParams" value=""/>
</launchConfiguration>
@@ -22,6 +22,13 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
private boolean autoAdjustPlayMode;
private RaceTimesInfoDTO lastRaceTimesInfo;
/**
* When a marker update is required but min/max are not yet initialized, this flag is set. If later
* {@link #setMinMax(Date, Date, boolean)} is invoked and this flag is set, a {@link #redrawAllMarkers(RaceTimesInfoDTO)}
* will be issued and the flag is cleared.
*/
private boolean redrawAllMarkersPendingForMinMaxBeingInitialized;
public RaceTimePanel(Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages,
RaceTimesInfoProvider raceTimesInfoProvider, boolean canReplayWhileLiveIsPossible) {
super(timer, timeRangeProvider, stringMessages, canReplayWhileLiveIsPossible);
@@ -105,7 +112,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
// maybe show a special state for this like "Race did not start yet"
}
}
lastRaceTimesInfo = raceTimesInfo;
lastRaceTimesInfo = raceTimesInfo; // TODO bug 3122: when not redrawn yet because min/max not initialized yet, further redraw is disabled by this.
}
@Override
@@ -181,6 +188,15 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
}
}
@Override
public void setMinMax(Date min, Date max, boolean fireEvent) {
super.setMinMax(min, max, fireEvent);
if (redrawAllMarkersPendingForMinMaxBeingInitialized && timeSlider.isMinMaxInitialized()) {
redrawAllMarkers(lastRaceTimesInfo);
redrawAllMarkersPendingForMinMaxBeingInitialized = false;
}
}
/**
* When in {@link PlayModes#Replay} mode, tries to put the {@link #timer} to the time point when the last leg was
* finished first. If this time point is not (yet?) known, tries to put the {@link #timer} to the
@@ -239,8 +255,12 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
requiresMarkerUpdate = true;
}
}
if (requiresMarkerUpdate && timeSlider.isMinMaxInitialized()) {
if (requiresMarkerUpdate) {
if (timeSlider.isMinMaxInitialized()) { // TODO bug 3122 later, the lastRaceTimesInfo will be updated even if not redrawn because min/max not initialized
redrawAllMarkers(newRaceTimesInfo);
} else {
redrawAllMarkersPendingForMinMaxBeingInitialized = true;
}
}
}
File mode changed.
@@ -420,16 +420,12 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
assert min != null && max != null;
boolean changed = false;
if (!max.equals(timeRangeProvider.getToTime())) {
if (!max.equals(timeRangeProvider.getToTime()) || !min.equals(timeRangeProvider.getFromTime())) {
changed = true;
timeSlider.setMaxValue(new Double(max.getTime()), fireEvent);
}
if (!min.equals(timeRangeProvider.getFromTime())) {
changed = true;
timeSlider.setMinValue(new Double(min.getTime()), fireEvent);
timeSlider.setMinAndMaxValue(new Double(min.getTime()),new Double(max.getTime()), fireEvent);
}
if (changed) {
if(!timeRangeProvider.isZoomed()) {
if (!timeRangeProvider.isZoomed()) {
timeRangeProvider.setTimeRange(min, max, this);
}
@@ -103,8 +103,6 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
this.stringMessages = stringMessages;
competitorSelectionModel.addCompetitorSelectionChangeListener(this);
setVisible(false);
// Waypoint list
currentWaypoints = new ArrayList<>();
waypointList = new ListDataProvider<>();
@@ -269,6 +267,7 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
});
selectCompetitorLabel.setText(stringMessages.selectCompetitor());
refreshWaypoints();
setVisible(false);
HorizontalPanel tableAndButtons = new HorizontalPanel();
add(tableAndButtons, 0, 0);
tableAndButtons.setSpacing(3);
@@ -284,19 +283,26 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
enableButtons();
}
@Override
public void setVisible(boolean visible) {
processCompetitorSelectionChange(visible);
super.setVisible(visible);
}
@Override
public void addedToSelection(CompetitorDTO competitor) {
processCompetitorSelectionChange();
processCompetitorSelectionChange(isVisible());
}
@Override
public void removedFromSelection(CompetitorDTO competitor) {
processCompetitorSelectionChange();
processCompetitorSelectionChange(isVisible());
}
private void processCompetitorSelectionChange() {
private void processCompetitorSelectionChange(boolean visible) {
waypointSelectionModel.clear();
if (isVisible() && Util.size(competitorSelectionModel.getSelectedCompetitors()) == 1) {
if (visible && Util.size(competitorSelectionModel.getSelectedCompetitors()) == 1) {
selectCompetitorLabel.setText("");
refillList();
} else {
@@ -310,6 +316,7 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
clearInfo();
}
private void refillList() {
clearInfo();
competitor = competitorSelectionModel.getSelectedCompetitors().iterator().next();
@@ -1196,10 +1196,10 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
// this only works because the quickRanks are sorted
for (Entry<CompetitorDTO, Integer> competitorsByWindwardDistanceTraveledAndOneBasedLegNumber :
competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber.entrySet()) {
if (Util.contains(competitorsToShow, competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getKey())) {
if (Util.contains(competitorsToShow, competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getKey()) &&
competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getValue() != null) {
leadingCompetitorDTO = competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getKey();
legOfLeaderCompetitor = competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getValue() == null ?
0 : competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getValue();
legOfLeaderCompetitor = competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getValue();
return new com.sap.sse.common.Util.Pair<Integer, CompetitorDTO>(legOfLeaderCompetitor, leadingCompetitorDTO);
}
}
@@ -1378,14 +1378,12 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
private void showStartAndFinishAndCourseMiddleLines(final CoursePositionsDTO courseDTO) {
if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null &&
!courseDTO.course.waypoints.isEmpty()) {
com.sap.sse.common.Util.Pair<Integer, CompetitorDTO> leadingVisibleCompetitorInfo = getFarthestAheadVisibleCompetitorWithOneBasedLegNumber(getCompetitorsToShow());
int oneBasedLegOfLeadingCompetitor = leadingVisibleCompetitorInfo == null ? -1 : leadingVisibleCompetitorInfo.getA();
int numberOfLegs = courseDTO.course.waypoints.size()-1;
// draw the start line
final WaypointDTO startWaypoint = courseDTO.course.waypoints.get(0);
updateCountdownCanvas(startWaypoint);
final Position startLineLeftPosition = courseDTO.getStartMarkPositions().get(0);
final Position startLineRightPosition = courseDTO.getStartMarkPositions().get(1);
final int numberOfStartWaypointMarks = courseDTO.getStartMarkPositions() == null ? 0 : courseDTO.getStartMarkPositions().size();
final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null : courseDTO.getStartMarkPositions().get(0);
final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null : courseDTO.getStartMarkPositions().get(1);
if (courseDTO.startLineAngleToCombinedWind != null) {
startLineAdvantageText.replace(0, startLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage(
NumberFormat.getFormat("0.0").format(courseDTO.startLineLengthInMeters),
@@ -1395,19 +1393,16 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
} else {
startLineAdvantageText.delete(0, startLineAdvantageText.length());
}
startLine = showOrRemoveOrUpdateLine(startLine, /* showLine */ courseDTO.getStartMarkPositions() != null &&
courseDTO.getStartMarkPositions().size() == 2 &&
((oneBasedLegOfLeadingCompetitor <= 1 &&
startLine = showOrRemoveOrUpdateLine(startLine, /* showLine */ numberOfStartWaypointMarks == 2 &&
((courseDTO.currentLegNumber <= 1 &&
settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE)) ||
settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)),
startLineLeftPosition, startLineRightPosition, startLineInfoProvider, "#ffffff");
// TODO bug3027: show SmallTransparentInfoOverlay in addition to tooltip if course geometry option is true; remove it if option is false
// TODO bug3027: show *all* course middle lines if course geometry option is true
// draw the finish line
final Position finishLineLeftPosition = courseDTO.getFinishMarkPositions().get(0);
final Position finishLineRightPosition = courseDTO.getFinishMarkPositions().get(1);
if (courseDTO.startLineAngleToCombinedWind != null) {
final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 : courseDTO.getFinishMarkPositions().size();
final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null : courseDTO.getFinishMarkPositions().get(0);
final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null : courseDTO.getFinishMarkPositions().get(1);
if (courseDTO.finishLineAngleToCombinedWind != null) {
finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage(
NumberFormat.getFormat("0.0").format(courseDTO.finishLineLengthInMeters),
NumberFormat.getFormat("0.0").format(Math.abs(courseDTO.finishLineAngleToCombinedWind)),
@@ -1416,8 +1411,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
} else {
finishLineAdvantageText.delete(0, finishLineAdvantageText.length());
}
finishLine = showOrRemoveOrUpdateLine(finishLine, /* showLine */ courseDTO.getFinishMarkPositions() != null && courseDTO.getFinishMarkPositions().size() == 2 &&
(oneBasedLegOfLeadingCompetitor > 0 && oneBasedLegOfLeadingCompetitor == numberOfLegs && settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE))
finishLine = showOrRemoveOrUpdateLine(finishLine, /* showLine */ numberOfFinishWaypointMarks == 2 &&
(courseDTO.currentLegNumber == courseDTO.totalLegsCount &&
settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE))
|| settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY),
finishLineLeftPosition, finishLineRightPosition, finishLineInfoProvider, "#000000");
// the control point pairs for which we already decided whether or not
@@ -1430,8 +1426,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() ||
settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) ||
(settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE)
&& oneBasedLegOfLeadingCompetitor > 0
&& oneBasedLegOfLeadingCompetitor-1 == zeroBasedIndexOfStartWaypoint);
&& courseDTO.currentLegNumber > 0
&& courseDTO.currentLegNumber-1 == zeroBasedIndexOfStartWaypoint);
keysAlreadyHandled.put(key, new Pair<>(showCourseMiddleLine, zeroBasedIndexOfStartWaypoint));
}
Set<Set<ControlPointDTO>> keysToConsider = new HashSet<>(keysAlreadyHandled.keySet());
@@ -460,6 +460,8 @@ import com.sapsailing.xrr.structureimport.eventimport.RegattaJSON;
* The server side implementation of the RPC service.
*/
public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements SailingService, RaceFetcher, RegattaFetcher {
private static final int MAX_NUMBER_OF_WIND_FIXES_TO_DELIVER_IN_ONE_CALL = 10000;
private static final Logger logger = Logger.getLogger(SailingServiceImpl.class.getName());
private static final long serialVersionUID = 9031688830194537489L;
@@ -1416,7 +1418,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
TimePoint toTimePoint = to == null ? trackedRace.getEndOfRace() == null ?
MillisecondsTimePoint.now().minus(trackedRace.getDelayToLiveInMillis()) : trackedRace.getEndOfRace() : new MillisecondsTimePoint(to);
if (fromTimePoint != null && toTimePoint != null) {
int numberOfFixes = (int) ((toTimePoint.asMillis() - fromTimePoint.asMillis())/resolutionInMilliseconds);
int numberOfFixes = Math.min(MAX_NUMBER_OF_WIND_FIXES_TO_DELIVER_IN_ONE_CALL,
(int) ((toTimePoint.asMillis() - fromTimePoint.asMillis())/resolutionInMilliseconds));
result = getAveragedWindInfo(fromTimePoint, resolutionInMilliseconds, numberOfFixes,
windSourceTypeNames, trackedRace, onlyUpToNewestEvent, /* includeCombinedWindForAllLegMiddles */ false);
}
@@ -4003,7 +4006,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
Map<String, List<RegattaOverviewEntryDTO>> entriesPerFleet = new HashMap<String, List<RegattaOverviewEntryDTO>>();
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
getRegattaOverviewEntries(showOnlyRacesOfSameDay, dayToCheck, usedCourseArea,
leaderboard, boatClass.getName(), regattaName, LeaderboardNameConstants.DEFAULT_SERIES_NAME, raceColumn, entriesPerFleet);
leaderboard, boatClass == null ? "" : boatClass.getName(), regattaName, LeaderboardNameConstants.DEFAULT_SERIES_NAME, raceColumn, entriesPerFleet);
}
result.addAll(getRegattaOverviewEntriesToBeShown(showOnlyCurrentlyRunningRaces, entriesPerFleet));
}
@@ -261,6 +261,7 @@ public class RaceContext {
case FINISHING:
case RUNNING:
case SCHEDULED:
case PRESCHEDULED:
case STARTPHASE:
case UNKNOWN:
break;
@@ -45,6 +45,7 @@ import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RequiresResize;
import com.sap.sse.common.Util;
/**
* A widget that allows the user to select a value within a range of possible values using a sliding bar that responds
@@ -704,9 +705,10 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Do
* the current value
*/
public void setMaxValue(Double maxValue, boolean fireEvent) {
if (!Util.equalsWithNull(maxValue, this.maxValue)) {
this.maxValue = maxValue;
drawTickLabels();
resetCurrentValue(fireEvent);
onMinMaxValueChanged(fireEvent);
}
}
/**
@@ -716,8 +718,40 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Do
* the current value
*/
public void setMinValue(Double minValue, boolean fireEvent) {
if (!Util.equalsWithNull(minValue, this.minValue)) {
this.minValue = minValue;
drawTickLabels();
onMinMaxValueChanged(fireEvent);
}
}
/**
* Set the minimum and maximum value
*
* @param minValue
* the current value for min
* @param maxValue
* the current valuefor max
*/
public void setMinAndMaxValue(Double minValue, Double maxValue, boolean fireEvent) {
boolean changed = false;
if (!Util.equalsWithNull(minValue, this.minValue)) {
this.minValue = minValue;
changed = true;
}
if (!Util.equalsWithNull(maxValue, this.maxValue)) {
this.maxValue = maxValue;
changed = true;
}
if (changed) {
onMinMaxValueChanged(fireEvent);
}
}
/**
* Handle value changes of min and/or max value
*/
protected void onMinMaxValueChanged(boolean fireEvent) {
redraw();
resetCurrentValue(fireEvent);
}
@@ -1037,6 +1071,7 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Do
int lineWidth = lineElement.getOffsetWidth();
if (numMarkers > 0) {
Element lastMarkerLabel = null;
Marker lastMarker = null;
int lastMarkerLabelOffsetLeft = 0;
// Create the labels or make them visible
for (int i = 0; i < numMarkers; i++) {
@@ -1076,9 +1111,9 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Do
// hide last marker label if it is too close to the one just created
if (lastMarkerLabelOffsetLeft > 0 && (labelLeftOffset-lastMarkerLabelOffsetLeft) <= 20) {
if (lastMarkerLabel != null) {
if (lastMarkerLabel != null && lastMarker != null) {
lastMarkerLabel.setPropertyString("className", "gwt-SliderBar-markerlabel-tooClose");
lastMarkerLabel.setPropertyString("innerHTML", "M"); // no space for more
lastMarkerLabel.setPropertyString("innerHTML", lastMarker.name); // no space for more
int currentMarkerOffsetLeft = labelLeftOffset;
try {
currentMarkerOffsetLeft = Integer.parseInt(markerElements.get(i).getStyle().getLeft().replace("px", ""));
@@ -1088,6 +1123,7 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Do
lastMarkerLabel.getStyle().setLeft(currentMarkerOffsetLeft-8, Unit.PX);
}
}
lastMarker = marker;
lastMarkerLabel = label;
lastMarkerLabelOffsetLeft = labelLeftOffset;
}
@@ -3,12 +3,12 @@ package com.sap.sse.gwt.client.controls.slider;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.DOM;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.user.client.DOM;
import com.sap.sse.gwt.client.controls.slider.TimeTicksCalculator.NormalizedInterval;
import com.sap.sse.gwt.client.controls.slider.TimeTicksCalculator.TickPosition;
@@ -182,7 +182,7 @@ public class TimeSlider extends SliderBar {
return;
Element knobElement = knobImage.getElement();
if(curValue >= minValue && curValue <= maxValue) {
if (curValue != null && minValue != null && maxValue != null && curValue >= minValue && curValue <= maxValue) {
// Move the knob to the correct position
int lineWidth = lineElement.getOffsetWidth();
int knobWidth = knobElement.getOffsetWidth();
@@ -199,21 +199,35 @@ public class TimeSlider extends SliderBar {
}
public void setMaxValue(Double maxValue, boolean fireEvent) {
if(!isZoomed) {
if (!isZoomed) {
super.setMaxValue(maxValue, fireEvent);
} else {
this.maxValue = maxValue;
}
calculateTicks();
}
public void setMinValue(Double minValue, boolean fireEvent) {
if(!isZoomed) {
if (!isZoomed) {
super.setMinValue(minValue, fireEvent);
} else {
this.minValue = minValue;
}
}
@Override
public void setMinAndMaxValue(Double minValue, Double maxValue, boolean fireEvent) {
if (!isZoomed) {
super.setMinAndMaxValue(minValue, maxValue, fireEvent);
} else {
this.minValue = minValue;
this.maxValue = maxValue;
}
}
@Override
protected void onMinMaxValueChanged(boolean fireEvent) {
calculateTicks();
super.onMinMaxValueChanged(fireEvent);
}
public void setStepSize(double stepSize, boolean fireEvent) {
Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/dark_sap_gray_black_30" android:state_selected="true"/>
<item android:drawable="@color/dark_black"/>
</selector>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/light_sap_gray_black_30" android:state_selected="true"/>
<item android:drawable="@color/light_black"/>
</selector>
@@ -4,5 +4,5 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:datePickerMode="spinner"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
@@ -4,6 +4,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleX="1.55"
android:scaleY="1.55"
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"
android:timePickerMode="spinner"/>
@@ -4,5 +4,5 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:calendarViewShown="false"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?sap_gray_black_30"
android:orientation="vertical"
android:padding="@dimen/default_padding_half">
<TextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?textLarge"
tools:text="Singapore"/>
<TextView
android:id="@android:id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?textNormal"
android:visibility="gone"
tools:text="Day 2"
tools:visibility="visible"/>
</LinearLayout>
@@ -20,6 +20,6 @@
android:layout_gravity="center"
android:paddingLeft="@dimen/default_padding"
android:paddingRight="@dimen/default_padding"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
</LinearLayout>
@@ -25,7 +25,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="?chevron_left_24dp"
android:visibility="gone"/>
android:visibility="gone"
tools:visibility="visible"/>
<ImageView
android:id="@+id/header_flag"
@@ -112,8 +113,8 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="11"
android:scaleX="1.55"
android:scaleY="1.55">
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale">
<include
android:id="@+id/time_picker"
@@ -71,15 +71,32 @@
android:src="?marker_race"/>
<LinearLayout
android:id="@+id/race_flag"
android:id="@+id/additional_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_centerVertical="true">
<ImageView
android:id="@+id/has_dependent_races"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:src="?link_24dp"
android:visibility="gone"
tools:visibility="visible"/>
<LinearLayout
android:id="@+id/race_flag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:orientation="vertical">
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<ImageView
android:id="@+id/current_flag"
@@ -89,29 +106,18 @@
android:scaleType="centerInside"
tools:src="@drawable/alpha_flag"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/arrow_direction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:src="?arrow_down"/>
<TextView
android:id="@+id/flag_timer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:drawableLeft="?arrow_down"
android:textAppearance="?textXSmall"
tools:text="01:12"/>
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/race_name"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
@@ -119,12 +125,28 @@
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_toLeftOf="@id/race_flag"
android:layout_toStartOf="@id/race_flag"
android:orientation="vertical"
android:layout_toLeftOf="@id/additional_info"
android:layout_toStartOf="@id/additional_info"
android:orientation="vertical">
<TextView
android:id="@+id/race_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?textNormal"
tools:text="Race 3"/>
<TextView
android:id="@+id/depends_on"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?textXSmall"
android:visibility="gone"
tools:text="5 minutes after ABC"
tools:visibility="visible"/>
</LinearLayout>
</RelativeLayout>
<ImageView
@@ -8,7 +8,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@color/constant_black" />
android:src="@color/constant_black"/>
<View
android:layout_width="match_parent"
@@ -27,7 +27,7 @@
android:layout_margin="@dimen/default_margin"
android:background="@drawable/round_button"
android:padding="10dp"
android:src="@drawable/ic_power_settings_new_white_48dp" />
android:src="@drawable/ic_power_settings_new_white_48dp"/>
<TextView
android:id="@+id/regatta_data"
@@ -36,24 +36,23 @@
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/nav_button"
android:layout_toStartOf="@id/nav_button"
tools:text="Marina Bay / Viewer"
android:textSize="@dimen/textSize_32"
android:paddingLeft="@dimen/default_padding_half"
android:paddingRight="@dimen/default_padding_half"
android:textColor="@color/constant_white"
/>
android:textSize="@dimen/textSize_32"
tools:text="Marina Bay / Viewer"/>
<TextView
android:id="@+id/regatta_course"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/regatta_data"
android:layout_toLeftOf="@id/nav_button"
android:layout_toStartOf="@id/nav_button"
android:layout_above="@id/regatta_data"
android:paddingLeft="@dimen/default_padding_half"
android:paddingRight="@dimen/default_padding_half"
android:singleLine="true"
android:textColor="@color/constant_white"
tools:text="Extreme Sailing Series 2015 Act 1 Singapore"
android:textSize="@dimen/textSize_32" />
android:textSize="@dimen/textSize_32"
tools:text="Extreme Sailing Series 2015 Act 1 Singapore"/>
</RelativeLayout>
@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/races_current"
android:textColor="?sap_light_gray"
android:textColor="?sap_yellow_1"
android:textSize="@dimen/textSize_20"
android:textStyle="bold"/>
@@ -49,10 +49,10 @@
android:dividerHeight="1dp"/>
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:id="@+id/progress"
android:visibility="gone"/>
</RelativeLayout>
</RelativeLayout>
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<RelativeLayout
android:id="@+id/race_content_header"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?sap_gray">
@@ -14,14 +15,14 @@
android:gravity="center"
android:textColor="?white"
android:textSize="@dimen/textSize_40"
tools:text="@string/time" />
tools:text="@string/time"/>
<ImageView
android:id="@+id/time_marker"
android:layout_width="match_parent"
android:layout_height="@dimen/thin_line"
android:layout_alignParentBottom="true"
android:src="?marker" />
android:src="?marker"/>
<LinearLayout
android:layout_width="match_parent"
@@ -43,19 +44,36 @@
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:textAppearance="?textSmall"
tools:text="@string/time_start" />
tools:text="@string/time_start"/>
<ImageView
android:id="@+id/time_start_lock"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="@dimen/default_margin_half"
android:layout_toLeftOf="@+id/time_start"
android:layout_toStartOf="@+id/time_start"
android:orientation="horizontal">
<ImageView
android:id="@+id/time_start_lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="?lock_outline"
android:visibility="gone" />
android:visibility="gone"
tools:visibility="visible"/>
<ImageView
android:id="@+id/linked_race"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="?link_24dp"
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout>
</RelativeLayout>
<RelativeLayout
@@ -76,7 +94,8 @@
android:gravity="center"
android:textAppearance="?textSmall"
android:visibility="invisible"
tools:text="@string/time" />
tools:text="@string/time"
tools:visibility="visible"/>
</RelativeLayout>
<RelativeLayout
@@ -93,7 +112,8 @@
android:layout_alignParentStart="true"
android:text="@string/time_finish"
android:textAppearance="?textSmall"
android:visibility="invisible" />
android:visibility="invisible"
tools:visibility="visible"/>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
@@ -82,8 +82,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="75dp"
android:scaleX="1.55"
android:scaleY="1.55" />
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale" />
</LinearLayout>
@@ -4,5 +4,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
@@ -80,8 +80,8 @@
android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/default_margin_twice"
android:layout_marginTop="@dimen/default_margin_twice"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
</LinearLayout>
</LinearLayout>
@@ -123,8 +123,8 @@
android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/default_margin_twice"
android:layout_marginTop="@dimen/default_margin_twice"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
</LinearLayout>
<TextView
@@ -34,12 +34,67 @@
android:text="@string/set_time"
android:textAppearance="?textNormal"/>
</LinearLayout>
<View
android:id="@+id/line_top"
android:layout_width="match_parent"
android:layout_height="@dimen/thick_line"
android:layout_alignParentBottom="true"
android:background="?black"/>
</RelativeLayout>
<LinearLayout
android:id="@+id/tab_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?black"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
<Button
android:id="@+id/absolute"
style="?android:attr/borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/dependent_races_absolute"
android:textAllCaps="true"
android:textColor="?sap_yellow_1"
android:textSize="@dimen/textSize_26"/>
<Button
android:id="@+id/relative"
style="?android:attr/borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/dependent_races_relative"
android:textAllCaps="true"
android:textColor="?sap_light_gray"
android:textSize="@dimen/textSize_26"/>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/tab_button"
android:layout_below="@id/header">
<RelativeLayout
android:id="@+id/time_absolute"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?sap_gray_black_30"
android:visibility="gone"
tools:visibility="visible">
<RelativeLayout
android:id="@+id/buttonBar"
android:layout_width="match_parent"
android:layout_height="@dimen/selector_header_height">
android:layout_height="@dimen/selector_header_height"
android:layout_alignParentTop="true">
<LinearLayout
android:layout_width="wrap_content"
@@ -85,16 +140,103 @@
</LinearLayout>
</RelativeLayout>
<View
android:id="@+id/line_top"
android:layout_width="match_parent"
android:layout_height="@dimen/thick_line"
android:layout_below="@id/buttonBar"
android:background="?black"/>
<LinearLayout
android:id="@+id/start_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale">
<NumberPicker
android:id="@+id/start_date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:descendantFocusability="blocksDescendants"/>
<include
layout="@layout/race_time_picker"
android:id="@+id/start_time_picker"
layout="@layout/time_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"/>
</LinearLayout>
<Button
android:id="@+id/set_start_time_absolute"
style="?attr/buttonSolidSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_margin="@dimen/default_margin_twice"
android:maxWidth="200dp"
android:text="@string/race_set_time"/>
<TextView
android:id="@+id/debug_time"
style="?textSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:visibility="gone"
tools:text="12:12:12"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/time_relative"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/buttonBar"/>
android:background="?sap_gray_black_30"
android:visibility="gone"
tools:visibility="visible">
<LinearLayout
android:id="@+id/relative_input"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<NumberPicker
android:id="@+id/time_offset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="@dimen/default_padding"
android:text="@string/minutes_after"
android:textAppearance="?textLarge"/>
<Spinner
android:id="@+id/dependent_race"
android:layout_width="470dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:dropDownWidth="wrap_content"
android:prompt="@string/dependent_races_prompt"
android:spinnerMode="dropdown"/>
</LinearLayout>
<Button
android:id="@+id/set_start_time_relative"
style="?attr/buttonSolidSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/relative_input"
android:layout_centerInParent="true"
android:maxWidth="200dp"
android:text="@string/race_set_time"/>
</RelativeLayout>
</FrameLayout>
</RelativeLayout>
@@ -10,7 +10,7 @@
android:orientation="vertical">
<RelativeLayout
android:id="@+id/header"
android:id="@+id/header_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/selector_header_height"
android:background="?sap_gray">
@@ -54,8 +54,9 @@
android:src="?wind_on"
android:visibility="gone"/>
<LinearLayout
<TextView
android:id="@+id/header_text"
android:drawableLeft="?chevron_left_24dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
@@ -65,28 +66,18 @@
android:layout_marginStart="@dimen/default_margin"
android:layout_toLeftOf="@id/wind_off"
android:layout_toStartOf="@id/wind_off"
android:orientation="horizontal">
<ImageView
android:id="@+id/header_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="?chevron_left_24dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal"
android:text="@string/wind"
android:textAppearance="?textNormal"/>
</LinearLayout>
android:textAppearance="?textNormal" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/content_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_below="@id/header"
android:layout_below="@id/header_layout"
android:background="?sap_gray_black_30"
android:orientation="vertical"
android:paddingLeft="@dimen/default_padding"
@@ -140,10 +131,11 @@
android:visibility="visible"/>
<com.sap.sailing.racecommittee.app.ui.views.CompassView
android:id="@+id/compassView"
android:id="@+id/compass_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>
<RelativeLayout
@@ -169,15 +161,20 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/layout_map"
android:id="@+id/layout_position"
android:layout_width="wrap_content"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/default_margin"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:orientation="vertical">
<TextView
android:id="@+id/position_header"
@@ -189,77 +186,119 @@
android:textAppearance="?textSmall"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/position_header">
<FrameLayout
android:id="@+id/windMap"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/welcome"/>
android:layout_height="wrap_content"
android:layout_below="@+id/position_header">
<RelativeLayout
android:id="@+id/address"
<TextView
android:id="@+id/position_latitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:background="?white"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
android:text="@string/wind_latitude"
android:textAppearance="?textSmall"/>
<ImageButton
android:id="@+id/address_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@android:color/transparent"
android:padding="@dimen/default_padding_half"
android:src="?search_24dp_invert"/>
<EditText
android:id="@+id/address_input"
<TextView
android:id="@+id/position_longitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/default_margin_half"
android:layout_marginStart="@dimen/default_margin_half"
android:layout_toLeftOf="@id/address_search"
android:layout_toStartOf="@id/address_search"
android:hint="@string/wind_address_hint"
android:padding="@dimen/default_padding_half"
android:singleLine="true"
android:textAppearance="?textNormal"
android:textColor="?white_invert"
android:textColorHint="?sap_light_gray"/>
</RelativeLayout>
android:layout_margin="@dimen/default_margin"
android:layout_below="@+id/position_latitude"
android:text="@string/wind_longitude"
android:textAppearance="?textSmall"/>
<RelativeLayout
android:id="@+id/dark_layer"
<TextView
android:id="@+id/position_accuracy"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/constant_black_transparent"/>
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:layout_below="@+id/position_longitude"
android:text="@string/wind_accuracy"
android:textAppearance="?textSmall"/>
<Button
android:id="@+id/enter_position"
android:id="@+id/position_show"
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/button_border_light"
android:padding="@dimen/default_padding"
android:text="@string/wind_enter_position"
android:textAllCaps="true"
android:textAppearance="?textNormal"
android:textColor="?white"/>
</RelativeLayout>
android:layout_below="@+id/position_accuracy"
android:layout_centerHorizontal="true"
android:text="@string/wind_map_button_show"/>
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:id="@+id/map_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/content_layout"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<!-- the following layout is for a future implementation -->
<!--<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:visibility="gone">
<Button
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Overall"/>
<Button
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#1"/>
<Button
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#2"/>
<Button
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#3"/>
</LinearLayout>-->
</LinearLayout>
<Button
android:id="@+id/position_hide"
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="@dimen/wind_hide_button_right_margin"
android:layout_marginEnd="@dimen/wind_hide_button_right_margin"
android:layout_marginTop="@dimen/wind_hide_button_top_margin"
android:text="@string/wind_map_button_hide"/>
</RelativeLayout>
</RelativeLayout>
@@ -39,6 +39,7 @@
<string name="label_login_position">Position</string>
<string name="login_type_viewer">Im Demo-Modus anmelden</string>
<string name="racelist_unscheduled">Noch nicht geplant</string>
<string name="racelist_prescheduled">Geplant für %s</string>
<string name="racelist_scheduled">Geplant für %s</string>
<string name="racelist_startphase">Start um %s läuft</string>
<string name="racelist_running">Gestartet um %s</string>
@@ -300,7 +301,7 @@
<string name="time_down">Runter in #TIME#</string>
<string name="time_up">Hoch in #TIME#</string>
<string name="time_finish">Ende N/A</string>
<string name="time_start">Start #TIME#</string>
<string name="time_start">Start %s</string>
<string name="time">#TIME#</string>
<string name="set_signal">Signal setzen</string>
<string name="flag_ap_desc">Rennen verschieben</string>
@@ -323,13 +324,13 @@
<string name="race_reset_cancel">Nein danke</string>
<string name="race_reset_message">Möchten Sie wirklich das Rennen inklusive aller Einstellungen und getätigten Aktionen zurücksetzen? Das kann nicht rückgängig gemacht werden!</string>
<string name="race_set_time">Zeit setzen</string>
<string name="race_start_time_in">-#TIME#</string>
<string name="race_start_time_ago">#TIME#</string>
<string name="race_start_time_in">-%s</string>
<string name="race_start_time_ago">%s</string>
<string name="tomorrow">Morgen</string>
<string name="race_begin_finish_header">Beginn des Zieleinlaufs</string>
<string name="race_begin_finish_current">Aktuelle Zeit verwenden</string>
<string name="race_begin_finish_custom">Angegebene Zeit verwenden</string>
<string name="wind_sensor"><![CDATA[um #AT# / von #FROM#° / #SPEED# kn]]></string>
<string name="wind_sensor"><![CDATA[um %1$s / von %2$.0f° / %3$.1f kn]]></string>
<string name="wind_no_data">Keine Daten</string>
<string name="wind_panel"><![CDATA[von %1$.0f°\n%2$.1f kn]]></string>
<string name="settings_category_developer">Entwickler Optionen</string>
@@ -341,7 +342,7 @@
<string name="gate_start_set_pathfinder">Pathfinder setzen</string>
<string name="gate_start_set">Gate Time setzen</string>
<string name="race_setup_confirm">Setup bestätigen</string>
<string name="race_end_finish_header">Ende des Zieleinlaufs</string>
<string name="race_end_finish_header">Ende des Zieleinlaufs (Beginn Zieleinlauf: %s)</string>
<string name="race_summary_finish_begin">Beginn des Zieleinlaufs</string>
<string name="race_summary_finish_duration">Dauer des Zieleinlaufs</string>
<string name="race_summary_finish_end">Ende des Zieleinlaufs</string>
@@ -372,7 +373,7 @@
<string name="delete_file_title">Datei löschen</string>
<string name="degrees">Grad</string>
<string name="start_time">Startzeit</string>
<string name="start_time_value">Um #TIME# in #COUNTDOWN#</string>
<string name="start_time_value">Um %1$s in %2$s</string>
<!--Extracted locales -->
<string name="selected_course_area_lost">Die ausgewählte Kursumgebung ist nicht mehr verfügbar.</string>
@@ -419,13 +420,27 @@
<string name="change_cancel">Abbrechen</string>
<string name="change_message">Nach den Wettfahrtregeln sind diese Daten im aktuellen Rennenstatus nicht veränderbar.\nSie könnten jedoch die falschen Eingaben korrigieren.</string>
<string name="change_title">Warnung</string>
<string name="send_mail">Sende Email...</string>
<string name="send_mail">Sende Email…</string>
<string name="data_reload_title">Daten wurden extern verändert</string>
<string name="data_reload_message">Daten müssen neu geladen werden. Eventueller Datenverlust ist möglich.</string>
<string name="preference_allow_dependent_races_header">Relative Startzeit</string>
<string name="preference_allow_dependent_races_description">Aktiviert relative Startzeiten zwischen Rennen</string>
<string name="preference_dependent_races_offset_header">Standard Offset der relativen Startzeit (Minuten)</string>
<string name="error_finished_time">Die ausgewählte Zielzeit liegt vor der Zeit, der ersten Schiffeinfahrt. Bitte überprüfe die Uhrzeit.</string>
<string name="error_finishing_time">Die ausgewählte Zeit is vor dem Rennstart. Bitte überprüfe die Uhrzeit.</string>
<string name="error_time_in_future">Die ausgewählte Zeit liegt in der Zukunft. Bitte überprüfe die Uhrzeit.</string>
<string name="error_wrong_race_state">Der RaceState hat nicht den korrekten Status (benötigt: %1$s - aktuell: %2$s)</string>
<string name="wind_map_button_show">Windkarte</string>
<string name="wind_map_button_hide">Schließen</string>
<string name="wind_latitude">Breite</string>
<string name="wind_longitude">Länge</string>
<string name="wind_accuracy">Genauigkeit</string>
<string name="dependent_races_absolute">Absolute Startzeit</string>
<string name="dependent_races_relative">Relative Startzeit</string>
<string name="dependent_races_prompt">Bitte Rennen auswählen</string>
<string name="minutes_after">Minuten nach</string>
<string name="minutes_after_long">%1$.0f Minuten nach %2$s</string>
<string name="wind_accuracy_ago">alt</string>
</resources>
@@ -19,6 +19,7 @@
<attr name="buttonSolidXSmall" format="reference"/>
<attr name="buttonSolidSmall" format="reference"/>
<attr name="buttonSolidNormal" format="reference"/>
<attr name="text" format="reference"/>
<attr name="textXXSmall" format="reference"/>
<attr name="textXSmall" format="reference"/>
<attr name="textSmall" format="reference"/>
@@ -53,6 +54,7 @@
<attr name="course_updown_64dp" format="reference"/>
<attr name="dns_48dp" format="reference"/>
<attr name="hearing_48dp" format="reference"/>
<attr name="link_24dp" format="reference"/>
<attr name="lock_outline" format="reference"/>
<attr name="marker" format="reference"/>
<attr name="marker_race" format="reference"/>
@@ -63,6 +65,7 @@
<attr name="swipe_idle" format="reference"/>
<attr name="swipe_left" format="reference"/>
<attr name="swipe_right" format="reference"/>
<attr name="tab_widget" format="reference"/>
<attr name="value_next" format="reference"/>
<attr name="value_prev" format="reference"/>
<attr name="wind_on" format="reference"/>
@@ -49,4 +49,9 @@
<dimen name="bigger_over_flow_width">64dp</dimen>
<dimen name="bigger_over_flow_height">32dp</dimen>
<dimen name="wind_hide_button_right_margin">76dp</dimen>
<dimen name="wind_hide_button_top_margin">20dp</dimen>
<item name="picker_scale" format="float" type="integer">1.55</item>
</resources>
@@ -81,6 +81,11 @@
<string name="app_folder" translatable="false">racecommittee</string>
<bool name="preference_isSendingActive_default">false</bool>
<string name="preference_allow_dependent_races_key" translatable="false">dependentRaces</string>
<bool name="preference_allow_dependent_races_default">true</bool>
<string name="preference_dependent_races_offset_key" translatable="false">dependentRacesOffset</string>
<integer name="preference_dependent_races_offset_default">5</integer>
<string name="preference_allow_demo_key" translatable="false">allow_demo</string>
<bool name="preference_allow_demo_default">false</bool>
@@ -51,6 +51,7 @@
<string name="login_type_shore_control">Login as shore control.</string>
<string name="login_type_viewer">Login as view-only (demo mode) user.</string>
<string name="racelist_unscheduled">Not yet scheduled</string>
<string name="racelist_prescheduled">Pre-scheduled for %s</string>
<string name="racelist_scheduled">Scheduled for %s</string>
<string name="racelist_startphase">Start at %s in progress</string>
<string name="racelist_running">Running since %s</string>
@@ -246,8 +247,8 @@
<string name="race_start_in_4_minutes">4 Minutes</string>
<string name="race_start_in_2_minutes">2 Minutes</string>
<string name="race_start_in_1_minute">1 Minute</string>
<string name="race_start_time_in">-#TIME#</string>
<string name="race_start_time_ago">#TIME#</string>
<string name="race_start_time_in">-%s</string>
<string name="race_start_time_ago">%s</string>
<string name="race_set_time">Set Time</string>
<string name="race_reset_course">Reset course</string>
<string name="no_course_active">No course active</string>
@@ -322,13 +323,13 @@
<string name="wind_enter_position">Set Your Position</string>
<string name="wind_kn">kn</string>
<string name="wind_address_hint">Enter Address</string>
<string name="wind_sensor"><![CDATA[at #AT# / from #FROM#° / #SPEED# kn]]></string>
<string name="wind_sensor"><![CDATA[at %1$s / from %2$.0f° / %3$.1f kn]]></string>
<string name="wind_panel"><![CDATA[from %1$.0f°\n%2$.1f kn]]></string>
<string name="wind_no_data">No Data</string>
<string name="time_up">Up in #TIME#</string>
<string name="time_down">Down in #TIME#</string>
<string name="time_start">Start #TIME#</string>
<string name="time_start">Start %s</string>
<string name="time_finish">Finish N/A</string>
<string name="time">#TIME#</string>
<string name="set_signal">Set Signal</string>
@@ -352,7 +353,7 @@
<string name="race_reset_cancel">No Thanks</string>
<string name="race_reset_message">Do you really want to reset this race including all settings and actions that have be made? This can not be undone.</string>
<string name="race_begin_finish_header">Begin Finish</string>
<string name="race_end_finish_header">End Finish</string>
<string name="race_end_finish_header">End Finish (Begin Finish: %s)</string>
<string name="race_begin_finish_current">Use Current Time</string>
<string name="race_begin_finish_custom">Use Custom Time</string>
<string name="race_setup_confirm">Confirm Setup</string>
@@ -393,7 +394,7 @@
<string name="delete_file_title">Delete file</string>
<string name="delete_file">This will delete the picture</string>
<string name="start_time">Start Time</string>
<string name="start_time_value">At #TIME# in #COUNTDOWN#</string>
<string name="start_time_value">At %1$s in %2$s</string>
<!--Extracted locales -->
<string name="selected_course_area_lost">The selected course area was lost.</string>
@@ -440,12 +441,26 @@
<string name="change_cancel">Cancel</string>
<string name="change_message">According to the racing rules this data is not changeable in the current\nrace state. You may proceed nevertheless to correct wrong inputs.</string>
<string name="change_title">Warning</string>
<string name="send_mail">Send mail...</string>
<string name="send_mail">Send mail…</string>
<string name="data_reload_title">Data has changed externally</string>
<string name="data_reload_message">Data have to be reloaded. Possible data loss is possible.</string>
<string name="preference_allow_dependent_races_header">Relative Start Time (Minutes)</string>
<string name="preference_allow_dependent_races_description">Enable Relative Start Time between Races</string>
<string name="preference_dependent_races_offset_header">Default Relative Start Time Offset</string>
<string name="error_finished_time">The given finish time is earlier than the first finisher time. Please recheck the time.</string>
<string name="error_finishing_time">The selected time is before the race start.</string>
<string name="error_time_in_future">The given time is in future. Please recheck the time.</string>
<string name="error_wrong_race_state">The RaceState isn\'t in the correct state (needed: %1$s - current: %2$s)</string>
<string name="wind_map_button_show">Wind Map</string>
<string name="wind_map_button_hide">Hide</string>
<string name="wind_latitude">Latitude</string>
<string name="wind_longitude">Longitude</string>
<string name="wind_accuracy">Accuracy</string>
<string name="dependent_races_absolute">Absolute Start Time</string>
<string name="dependent_races_relative">Relative Start Time</string>
<string name="dependent_races_prompt">Please select the race</string>
<string name="minutes_after">Minutes after</string>
<string name="minutes_after_long">%1$.0f Minutes after %2$s</string>
<string name="wind_accuracy_ago">ago</string>
</resources>
@@ -7,6 +7,7 @@
<style name="AppTheme.Dark.Base" parent="Theme.AppCompat.NoActionBar">
<item name="windowActionModeOverlay">true</item>
<item name="android:actionOverflowButtonStyle">@style/OverFlow</item>
<item name="android:tabWidgetStyle">@style/AppTheme.Dark.TabWidget</item>
<item name="colorPrimary">@color/dark_colorPrimary</item>
<item name="colorPrimaryDark">@color/dark_colorPrimaryDark</item>
@@ -34,6 +35,7 @@
<item name="buttonSolidXSmall">@style/AppTheme.Dark.Button.XSmall</item>
<item name="buttonSolidSmall">@style/AppTheme.Dark.Button.Small</item>
<item name="buttonSolidNormal">@style/AppTheme.Dark.Button.Normal</item>
<item name="text">@style/AppTheme.Dark.Text</item>
<item name="textXXSmall">@style/AppTheme.Dark.Text.XXSmall</item>
<item name="textXSmall">@style/AppTheme.Dark.Text.XSmall</item>
<item name="textSmall">@style/AppTheme.Dark.Text.Small</item>
@@ -67,6 +69,7 @@
<item name="course_updown_64dp">@drawable/course_updown_64dp_dark</item>
<item name="dns_48dp">@drawable/ic_dns_white_48dp</item>
<item name="hearing_48dp">@drawable/ic_hearing_white_48dp</item>
<item name="link_24dp">@drawable/ic_link_white_24dp</item>
<item name="lock_outline">@drawable/lock_outline_dark</item>
<item name="marker">@drawable/marker_dark</item>
<item name="marker_race">@drawable/marker_race_dark</item>
@@ -77,6 +80,7 @@
<item name="swipe_idle">@drawable/swipe_idle_dark</item>
<item name="swipe_left">@drawable/swipe_left_dark</item>
<item name="swipe_right">@drawable/swipe_right_dark</item>
<item name="tab_widget">@drawable/tabwidget_dark</item>
<item name="value_next">@drawable/value_next_dark</item>
<item name="value_prev">@drawable/value_prev_dark</item>
<item name="wind_off">@drawable/wind_off_dark</item>
@@ -171,4 +175,13 @@
<item name="android:gravity">center_horizontal</item>
</style>
<style name="AppTheme.Dark.TabWidget" parent="@android:style/Widget.TabWidget">
<item name="android:textAppearance">@style/AppTheme.Light.TabWidgetText</item>
</style>
<style name="AppTheme.Dark.TabWidgetText" parent="@android:style/TextAppearance.Widget.TabWidget">
<item name="android:textSize">@dimen/textSize_32</item>
<item name="android:textColor">?white</item>
</style>
</resources>
@@ -7,6 +7,7 @@
<style name="AppTheme.Light.Base" parent="Theme.AppCompat.NoActionBar">
<item name="windowActionModeOverlay">true</item>
<item name="android:actionOverflowButtonStyle">@style/OverFlow</item>
<item name="android:tabWidgetStyle">@style/AppTheme.Light.TabWidget</item>
<item name="colorPrimary">@color/light_colorPrimary</item>
<item name="colorPrimaryDark">@color/light_colorPrimaryDark</item>
@@ -37,6 +38,7 @@
<item name="buttonSolidXSmall">@style/AppTheme.Light.Button.XSmall</item>
<item name="buttonSolidSmall">@style/AppTheme.Light.Button.Small</item>
<item name="buttonSolidNormal">@style/AppTheme.Light.Button.Normal</item>
<item name="text">@style/AppTheme.Light.Text</item>
<item name="textXXSmall">@style/AppTheme.Light.Text.XXSmall</item>
<item name="textXSmall">@style/AppTheme.Light.Text.XSmall</item>
<item name="textSmall">@style/AppTheme.Light.Text.Small</item>
@@ -70,6 +72,7 @@
<item name="course_updown_64dp">@drawable/course_updown_64dp_light</item>
<item name="dns_48dp">@drawable/ic_dns_black_48dp</item>
<item name="hearing_48dp">@drawable/ic_hearing_black_48dp</item>
<item name="link_24dp">@drawable/ic_link_black_24dp</item>
<item name="lock_outline">@drawable/lock_outline_light</item>
<item name="marker">@drawable/marker_light</item>
<item name="marker_race">@drawable/marker_race_light</item>
@@ -80,6 +83,7 @@
<item name="swipe_idle">@drawable/swipe_idle_light</item>
<item name="swipe_left">@drawable/swipe_left_light</item>
<item name="swipe_right">@drawable/swipe_right_light</item>
<item name="tab_widget">@drawable/tabwidget_light</item>
<item name="value_next">@drawable/value_next_light</item>
<item name="value_prev">@drawable/value_prev_light</item>
<item name="wind_off">@drawable/wind_off_light</item>
@@ -181,4 +185,13 @@
<item name="android:windowBackground">@null</item>
</style>
<style name="AppTheme.Light.TabWidget" parent="@android:style/Widget.TabWidget">
<item name="android:textAppearance">@style/AppTheme.Light.TabWidgetText</item>
</style>
<style name="AppTheme.Light.TabWidgetText" parent="@android:style/TextAppearance.Widget.TabWidget">
<item name="android:textSize">@dimen/textSize_32</item>
<item name="android:textColor">?white</item>
</style>
</resources>
@@ -10,7 +10,7 @@
android:defaultValue="@string/empty"
android:key="@string/preference_identifier_key"
android:title="@string/device_identifier"/>
<EditTextPreference
<com.sap.sailing.racecommittee.app.ui.views.UrlEditTextPreference
android:defaultValue="@string/preference_server_url_default"
android:key="@string/preference_server_url_key"
android:title="@string/settings_advanced_service_header"/>
@@ -1,118 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/settings_category_general" >
<PreferenceCategory android:title="@string/settings_category_general">
<ListPreference
android:key="@string/preference_racing_procedure_override_key"
android:defaultValue="@string/preference_racing_procedure_override_default"
android:key="@string/preference_racing_procedure_override_key"
android:summary="@string/override_default_start_procedure_type_select_description"
android:title="@string/override_default_start_procedure_type_select_header" />
android:title="@string/override_default_start_procedure_type_select_header"/>
<ListPreference
android:key="@string/preference_course_designer_override_key"
android:defaultValue="@string/preference_course_designer_override_default"
android:summary="@string/empty"
android:title="@string/override_default_course_designer_type_select_header" />
android:key="@string/preference_course_designer_override_key"
android:title="@string/override_default_course_designer_type_select_header"/>
<CheckBoxPreference
android:defaultValue="@bool/preference_allow_dependent_races_default"
android:key="@string/preference_allow_dependent_races_key"
android:summary="@string/preference_allow_dependent_races_description"
android:title="@string/preference_allow_dependent_races_header"/>
<com.sap.sailing.racecommittee.app.ui.views.IntegerEditTextPreference
android:defaultValue="@integer/preference_dependent_races_offset_default"
android:key="@string/preference_dependent_races_offset_key"
android:numeric="integer"
android:title="@string/preference_dependent_races_offset_header"/>
</PreferenceCategory>
<PreferenceCategory android:title="RRS26" >
<PreferenceCategory android:title="RRS26">
<ListPreference
android:key="@string/preference_racing_procedure_rrs26_classflag_key"
android:defaultValue="@string/preference_racing_procedure_classflag_default"
android:title="@string/preference_racing_procedure_classflag_header"
android:summary="@string/empty" />
android:key="@string/preference_racing_procedure_rrs26_classflag_key"
android:summary="@string/empty"
android:title="@string/preference_racing_procedure_classflag_header"/>
<CheckBoxPreference
android:key="@string/preference_racing_procedure_rrs26_hasxray_key"
android:defaultValue="@bool/preference_racing_procedure_rrs26_hasxray_default"
android:title="@string/preference_racing_procedure_hasxray_header"
android:summary="@string/preference_racing_procedure_hasxray_description" />
android:key="@string/preference_racing_procedure_rrs26_hasxray_key"
android:summary="@string/preference_racing_procedure_hasxray_description"
android:title="@string/preference_racing_procedure_hasxray_header"/>
<com.sap.sailing.racecommittee.app.ui.views.FixedMultiSelectListPreference
android:key="@string/preference_racing_procedure_rrs26_startmode_flags_key"
android:defaultValue="@array/preference_racing_procedure_rrs26_startmode_flags_default"
android:title="@string/preference_racing_procedure_rrs26_startmode_flags_header"
android:summary="@string/empty" />
android:key="@string/preference_racing_procedure_rrs26_startmode_flags_key"
android:summary="@string/empty"
android:title="@string/preference_racing_procedure_rrs26_startmode_flags_header"/>
</PreferenceCategory>
<PreferenceCategory android:title="Gate Start" >
<PreferenceCategory android:title="Gate Start">
<ListPreference
android:defaultValue="@string/preference_racing_procedure_classflag_default"
android:key="@string/preference_racing_procedure_gatestart_classflag_key"
android:defaultValue="@string/preference_racing_procedure_classflag_default"
android:title="@string/preference_racing_procedure_classflag_header"
android:summary="@string/empty" />
android:summary="@string/empty"
android:title="@string/preference_racing_procedure_classflag_header"/>
<CheckBoxPreference
android:key="@string/preference_racing_procedure_gatestart_hasxray_key"
android:defaultValue="@bool/preference_racing_procedure_gatestart_hasxray_default"
android:title="@string/preference_racing_procedure_hasxray_header"
android:summary="@string/preference_racing_procedure_hasxray_description" />
android:key="@string/preference_racing_procedure_gatestart_hasxray_key"
android:summary="@string/preference_racing_procedure_hasxray_description"
android:title="@string/preference_racing_procedure_hasxray_header"/>
<CheckBoxPreference
android:key="@string/preference_racing_procedure_gatestart_haspathfinder_key"
android:defaultValue="@bool/preference_racing_procedure_gatestart_haspathfinder_default"
android:title="@string/preference_racing_procedure_gatestart_haspathfinder_header"
android:summary="@string/preference_racing_procedure_gatestart_haspathfinder_description" />
android:key="@string/preference_racing_procedure_gatestart_haspathfinder_key"
android:summary="@string/preference_racing_procedure_gatestart_haspathfinder_description"
android:title="@string/preference_racing_procedure_gatestart_haspathfinder_header"/>
<CheckBoxPreference
android:key="@string/preference_racing_procedure_gatestart_hasadditionalgolfdowntime_key"
android:defaultValue="@bool/preference_racing_procedure_gatestart_hasadditionalgolfdowntime_default"
android:title="@string/preference_racing_procedure_gatestart_hasadditionalgolfdowntime_header"
android:summary="@string/preference_racing_procedure_gatestart_hasadditionalgolfdowntime_description" />
android:key="@string/preference_racing_procedure_gatestart_hasadditionalgolfdowntime_key"
android:summary="@string/preference_racing_procedure_gatestart_hasadditionalgolfdowntime_description"
android:title="@string/preference_racing_procedure_gatestart_hasadditionalgolfdowntime_header"/>
</PreferenceCategory>
<PreferenceCategory android:title="ESS Start" >
<PreferenceCategory android:title="ESS Start">
<ListPreference
android:defaultValue="@string/preference_racing_procedure_classflag_default"
android:key="@string/preference_racing_procedure_ess_classflag_key"
android:defaultValue="@string/preference_racing_procedure_classflag_default"
android:title="@string/preference_racing_procedure_classflag_header"
android:summary="@string/empty" />
android:summary="@string/empty"
android:title="@string/preference_racing_procedure_classflag_header"/>
<CheckBoxPreference
android:key="@string/preference_racing_procedure_ess_hasxray_key"
android:defaultValue="@bool/preference_racing_procedure_ess_hasxray_default"
android:title="@string/preference_racing_procedure_hasxray_header"
android:summary="@string/preference_racing_procedure_hasxray_description" />
android:key="@string/preference_racing_procedure_ess_hasxray_key"
android:summary="@string/preference_racing_procedure_hasxray_description"
android:title="@string/preference_racing_procedure_hasxray_header"/>
</PreferenceCategory>
<PreferenceCategory android:title="Basic Start" >
<PreferenceCategory android:title="Basic Start">
<ListPreference
android:defaultValue="@string/preference_racing_procedure_classflag_default"
android:key="@string/preference_racing_procedure_basic_classflag_key"
android:defaultValue="@string/preference_racing_procedure_classflag_default"
android:title="@string/preference_racing_procedure_classflag_header"
android:summary="@string/empty" />
android:summary="@string/empty"
android:title="@string/preference_racing_procedure_classflag_header"/>
<CheckBoxPreference
android:key="@string/preference_racing_procedure_basic_hasxray_key"
android:defaultValue="@bool/preference_racing_procedure_basic_hasxray_default"
android:title="@string/preference_racing_procedure_hasxray_header"
android:summary="@string/preference_racing_procedure_hasxray_description" />
android:key="@string/preference_racing_procedure_basic_hasxray_key"
android:summary="@string/preference_racing_procedure_hasxray_description"
android:title="@string/preference_racing_procedure_hasxray_header"/>
</PreferenceCategory>
<PreferenceCategory android:title="League Start" >
<PreferenceCategory android:title="League Start">
<ListPreference
android:key="@string/preference_racing_procedure_league_classflag_key"
android:defaultValue="@string/preference_racing_procedure_classflag_default"
android:title="@string/preference_racing_procedure_classflag_header"
android:summary="@string/empty" />
android:key="@string/preference_racing_procedure_league_classflag_key"
android:summary="@string/empty"
android:title="@string/preference_racing_procedure_classflag_header"/>
<CheckBoxPreference
android:key="@string/preference_racing_procedure_league_hasxray_key"
android:defaultValue="@bool/preference_racing_procedure_league_hasxray_default"
android:title="@string/preference_racing_procedure_hasxray_header"
android:summary="@string/preference_racing_procedure_hasxray_description" />
android:key="@string/preference_racing_procedure_league_hasxray_key"
android:summary="@string/preference_racing_procedure_hasxray_description"
android:title="@string/preference_racing_procedure_hasxray_header"/>
</PreferenceCategory>
</PreferenceScreen>
@@ -7,8 +7,6 @@ public class AppConstants {
//TODO replace it later with BuildConfig.APPLICATION_ID (Eclipse didn't know it)
private final static String PACKAGE_NAME = "com.sap.sailing.racecommittee.app";
public final static String DEFAULT = "Default";
// Intent extra fields
public final static String COURSE_AREA_UUID_KEY = "courseUuid";
public final static String RACE_ID_KEY = "raceUuid";
@@ -72,4 +70,7 @@ public class AppConstants {
public final static String INTENT_ACTION_SHOW_SUMMARY_CONTENT = PACKAGE_NAME + ".action.show.summary";
public final static String INTENT_ACTION_SHOW_PROTEST = PACKAGE_NAME + ".action.show.protest";
public final static String INTENT_ACTION_IS_TRACKING = PACKAGE_NAME + "action.is.tracking";
public final static String INTENT_ACTION_IS_TRACKING_EXTRA = PACKAGE_NAME + "action.is.tracking.extra";
}
@@ -99,8 +99,7 @@ public class AppPreferences {
}
public List<String> getByNameCourseDesignerCourseNames() {
Set<String> values = preferences.getStringSet(
key(R.string.preference_course_designer_by_name_course_names_key), new HashSet<String>());
Set<String> values = preferences.getStringSet(key(R.string.preference_course_designer_by_name_course_names_key), new HashSet<String>());
return new ArrayList<String>(values);
}
@@ -251,6 +250,7 @@ public class AppPreferences {
if (value.equals("")) {
return "http://localhost:8889";
}
return value;
}
@@ -317,8 +317,7 @@ public class AppPreferences {
public void setGateStartHasAdditionalGolfDownTime(boolean hasAdditionalGolfDownTime) {
preferences
.edit()
.putBoolean(key(R.string.preference_racing_procedure_gatestart_hasadditionalgolfdowntime_key),
hasAdditionalGolfDownTime).commit();
.putBoolean(key(R.string.preference_racing_procedure_gatestart_hasadditionalgolfdowntime_key), hasAdditionalGolfDownTime).commit();
}
public void setGateStartHasPathfinder(boolean hasPathfinder) {
@@ -435,6 +434,16 @@ public class AppPreferences {
context.getResources().getBoolean(R.bool.preference_offline_default));
}
public boolean isDependentRacesAllowed() {
return preferences.getBoolean(context.getString(R.string.preference_allow_dependent_races_key),
context.getResources().getBoolean(R.bool.preference_allow_dependent_races_default));
}
public int getDependentRacesOffset() {
return preferences.getInt(context.getString(R.string.preference_dependent_races_offset_key),
context.getResources().getInteger(R.integer.preference_dependent_races_offset_default));
}
public String getTheme() {
return preferences.getString(context.getString(R.string.preference_theme_key),
context.getResources().getString(R.string.preference_theme_default));
@@ -24,6 +24,15 @@ public class ManagedRacesDataHandler extends DataHandler<Collection<ManagedRace>
@Override
public void onResult(Collection<ManagedRace> data) {
manager.addRaces(data);
calcRaceState(data);
}
private void calcRaceState(Collection<ManagedRace> data) {
for (ManagedRace race : data) {
race.calculateRaceState();
}
for (ManagedRace race : data) {
race.getState().forceUpdate();
}
}
}
@@ -10,8 +10,6 @@ import org.json.simple.JSONValue;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.RaceStateImpl;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.configuration.ConfigurationLoader;
@@ -20,7 +18,6 @@ import com.sap.sailing.domain.base.racegroup.RaceCell;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.RaceRow;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.racecommittee.app.data.AndroidRaceLogResolver;
import com.sap.sailing.racecommittee.app.domain.FleetIdentifier;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier;
@@ -28,6 +25,7 @@ import com.sap.sailing.racecommittee.app.domain.configuration.impl.MergingRegatt
import com.sap.sailing.racecommittee.app.domain.impl.FleetIdentifierImpl;
import com.sap.sailing.racecommittee.app.domain.impl.ManagedRaceIdentifierImpl;
import com.sap.sailing.racecommittee.app.domain.impl.ManagedRaceImpl;
import com.sap.sailing.racecommittee.app.utils.ManagedRaceCalculator;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
@@ -55,7 +53,6 @@ public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace
RaceGroup group = deserializer.deserialize(json);
addManagedRaces(managedRaces, group);
}
return managedRaces;
}
@@ -80,8 +77,7 @@ public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace
}
FleetIdentifier fleetIdentifier = new FleetIdentifierImpl(fleet, series, raceGroup);
ManagedRaceIdentifier identifier = new ManagedRaceIdentifierImpl(name, fleetIdentifier);
RaceState state = RaceStateImpl.create(new AndroidRaceLogResolver(), raceLog, author, configurationLoader);
return new ManagedRaceImpl(identifier, state);
return new ManagedRaceImpl(identifier, new ManagedRaceCalculator(raceLog, author, configurationLoader));
}
}
@@ -1,5 +1,8 @@
package com.sap.sailing.racecommittee.app.domain;
import java.util.Collection;
import java.util.List;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.base.Competitor;
@@ -10,9 +13,6 @@ import com.sap.sse.common.Named;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.WithID;
import java.util.Collection;
import java.util.List;
public interface ManagedRace extends ManagedRaceIdentifier, Named, WithID {
/**
@@ -73,7 +73,6 @@ public interface ManagedRace extends ManagedRaceIdentifier, Named, WithID {
*/
void setCompetitors(Collection<Competitor> competitors);
/**
* sets the list of mapItems ( buoys, other boats ) for a race. As the mapItems are retrieved later from the backend, the list of
* mapItems has to be settable.
@@ -83,6 +82,13 @@ public interface ManagedRace extends ManagedRaceIdentifier, Named, WithID {
*/
void setMapMarkers(List<MapMarker> markers);
/**
* Returns true if {@link RaceState} has been calculated and set
*
* @return true, if {@link RaceState} has been set
*/
boolean calculateRaceState();
/**
* sets the finished time, if the finished time is after the finishing time; check the {@link Result} for error message
*
@@ -1,5 +1,9 @@
package com.sap.sailing.racecommittee.app.domain.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishingTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
@@ -15,31 +19,33 @@ import com.sap.sailing.racecommittee.app.data.AndroidRaceLogResolver;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier;
import com.sap.sailing.racecommittee.app.domain.MapMarker;
import com.sap.sailing.racecommittee.app.utils.ManagedRaceCalculator;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class ManagedRaceImpl implements ManagedRace {
private static final long serialVersionUID = -4936566684992524001L;
// private static final String TAG = ManagedRace.class.getName();
private final RaceState state;
private final ManagedRaceIdentifier identifier;
private RaceState state;
private Collection<Competitor> competitors;
private List<MapMarker> mapMarkers;
private CourseBase courseOnServer;
private ManagedRaceCalculator calculator;
public ManagedRaceImpl(ManagedRaceIdentifier identifier, RaceState state) {
this.state = state;
this.identifier = identifier;
this.competitors = new ArrayList<Competitor>();
this.competitors = new ArrayList<>();
this.courseOnServer = null;
}
public ManagedRaceImpl(ManagedRaceIdentifier identifier, ManagedRaceCalculator calculator) {
this.identifier = identifier;
this.competitors = new ArrayList<>();
this.courseOnServer = null;
this.calculator = calculator;
}
@Override
public RaceState getState() {
return state;
@@ -82,7 +88,7 @@ public class ManagedRaceImpl implements ManagedRace {
@Override
public RaceLog getRaceLog() {
return state.getRaceLog();
return state == null ? null : state.getRaceLog();
}
@Override
@@ -101,10 +107,20 @@ public class ManagedRaceImpl implements ManagedRace {
}
@Override
public List<MapMarker> getMapMarkers(){
public void setCompetitors(Collection<Competitor> competitors) {
this.competitors = competitors;
}
@Override
public List<MapMarker> getMapMarkers() {
return mapMarkers;
}
@Override
public void setMapMarkers(List<MapMarker> markers) {
this.mapMarkers = markers;
}
@Override
public CourseBase getCourseOnServer() {
return courseOnServer;
@@ -116,13 +132,13 @@ public class ManagedRaceImpl implements ManagedRace {
}
@Override
public void setCompetitors(Collection<Competitor> competitors) {
this.competitors = competitors;
public boolean calculateRaceState() {
boolean calculated = false;
if (state == null && calculator != null) {
state = calculator.calculateRaceState();
calculated = true;
}
@Override
public void setMapMarkers(List<MapMarker> markers) {
this.mapMarkers = markers;
return calculated;
}
@Override
@@ -1,11 +1,10 @@
package com.sap.sailing.racecommittee.app.domain.impl;
import android.text.TextUtils;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sse.common.Util;
@@ -26,6 +25,10 @@ public class RaceGroupSeriesFleet {
fleetOrder = getFleetIndex(series.getFleets(), race.getFleet());
}
private static int getSeriesIndex(ManagedRace race, SeriesBase series) {
return Util.indexOf(race.getRaceGroup().getSeries(), series);
}
public RaceGroup getRaceGroup() {
return raceGroup;
}
@@ -59,10 +62,10 @@ public class RaceGroupSeriesFleet {
if (!useDisplayName || TextUtils.isEmpty(name)) {
name = raceGroup.getName();
}
if (series != null && !series.getName().equals(AppConstants.DEFAULT)) {
if (series != null && !series.getName().equals(LeaderboardNameConstants.DEFAULT_SERIES_NAME)) {
name += " - " + series.getName();
}
if (fleet != null && !fleet.getName().equals(AppConstants.DEFAULT)) {
if (fleet != null && !fleet.getName().equals(LeaderboardNameConstants.DEFAULT_FLEET_NAME)) {
name += " - " + fleet.getName();
}
return name;
@@ -130,8 +133,4 @@ public class RaceGroupSeriesFleet {
private int getFleetIndex(Iterable<? extends Fleet> fleets, Fleet fleet) {
return Util.indexOf(fleets, fleet);
}
private static int getSeriesIndex(ManagedRace race, SeriesBase series) {
return Util.indexOf(race.getRaceGroup().getSeries(), series);
}
}
@@ -9,12 +9,12 @@ import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.internal.widget.TintImageView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.*;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -46,6 +46,7 @@ import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.RaceFlagViewerFra
import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.RaceInfoListener;
import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.RaceSummaryFragment;
import com.sap.sailing.racecommittee.app.utils.BitmapHelper;
import com.sap.sailing.racecommittee.app.utils.RaceHelper;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -68,7 +69,6 @@ public class RacingActivity extends SessionActivity implements RaceInfoListener,
private Wind mWind;
private RaceListFragment mRaceList;
private ManagedRace mSelectedRace;
private CourseArea mCourseArea;
private TimePoint startTime;
private Serializable getCourseAreaIdFromIntent() {
@@ -152,7 +152,7 @@ public class RacingActivity extends SessionActivity implements RaceInfoListener,
throw new IllegalStateException("There was no course area id transmitted...");
}
ExLog.i(this, this.getClass().toString(), "trying to load courseArea via id: " + courseAreaId);
mCourseArea = dataManager.getDataStore().getCourseArea(courseAreaId);
CourseArea courseArea = dataManager.getDataStore().getCourseArea(courseAreaId);
Serializable eventId = getEventIdFromIntent();
if (eventId == null) {
@@ -166,14 +166,14 @@ public class RacingActivity extends SessionActivity implements RaceInfoListener,
startActivity(intent);
finish();
} else {
if (mCourseArea != null) {
loadRaces(mCourseArea);
if (courseArea != null) {
loadRaces(courseArea);
ExLog.i(this, this.getClass().toString(), "did load courseArea!");
} else {
ExLog.i(this, this.getClass().toString(), "courseArea == null :(");
Toast.makeText(this, getString(R.string.racing_course_area_missing), Toast.LENGTH_LONG).show();
}
loadNavDrawer(mCourseArea);
loadNavDrawer(courseArea);
loadWelcomeFragment();
}
}
@@ -253,7 +253,7 @@ public class RacingActivity extends SessionActivity implements RaceInfoListener,
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mWind = (Wind) savedInstanceState.getSerializable(WIND);
@@ -359,20 +359,8 @@ public class RacingActivity extends SessionActivity implements RaceInfoListener,
}
private void setupActionBar(ManagedRace race) {
String title = race.getRaceGroup().getDisplayName();
if (TextUtils.isEmpty(title)) {
title = race.getRaceGroup().getName();
}
if (race.getSeries() != null && !race.getSeries().getName().equals(AppConstants.DEFAULT)) {
title += " / " + race.getSeries().getName();
}
if (race.getFleet() != null && !race.getFleet().getName().equals(AppConstants.DEFAULT)) {
title += " / " + race.getFleet().getName();
}
title += " / " + race.getRaceName();
if (getSupportActionBar() != null) {
getSupportActionBar().setTitle(title);
getSupportActionBar().setTitle(RaceHelper.getRaceName(race, " / "));
}
}
@@ -384,7 +372,7 @@ public class RacingActivity extends SessionActivity implements RaceInfoListener,
builder.setPositiveButton(getString(R.string.race_reset_confirm), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ExLog.i(RacingActivity.this, LogEvent.RACE_RESET_YES, mSelectedRace.getId().toString());
ExLog.i(RacingActivity.this, LogEvent.RACE_RESET_YES, mSelectedRace.getId());
ExLog.w(RacingActivity.this, TAG, String.format("Race %s is selected for reset.", mSelectedRace.getId()));
mSelectedRace.getState().setAdvancePass(MillisecondsTimePoint.now());
}
@@ -393,7 +381,7 @@ public class RacingActivity extends SessionActivity implements RaceInfoListener,
@Override
public void onClick(DialogInterface dialog, int which) {
if (mSelectedRace != null) {
ExLog.i(RacingActivity.this, LogEvent.RACE_RESET_NO, mSelectedRace.getId().toString());
ExLog.i(RacingActivity.this, LogEvent.RACE_RESET_NO, mSelectedRace.getId());
}
dialog.cancel();
}
@@ -416,7 +404,7 @@ public class RacingActivity extends SessionActivity implements RaceInfoListener,
@Override
public void onGlobalLayout() {
// Find the overflow button
final ArrayList<View> outViews = new ArrayList<View>();
final ArrayList<View> outViews = new ArrayList<>();
decorView.findViewsWithText(outViews, overflowDescription, View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
if (outViews.isEmpty()) {
return;
@@ -493,7 +481,7 @@ public class RacingActivity extends SessionActivity implements RaceInfoListener,
setProgressSpinnerVisibility(false);
AlertDialog.Builder builder = new AlertDialog.Builder(RacingActivity.this, R.style.AppTheme_AlertDialog);
builder.setMessage(String.format(getString(R.string.generic_load_failure), ex.getMessage())).setTitle(getString(R.string.loading_failure))
.setIcon(R.drawable.ic_warning_grey600_36dp).setCancelable(true)
.setIcon(R.drawable.ic_warning_grey_600_36dp).setCancelable(true)
.setPositiveButton(getString(R.string.retry), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
setProgressSpinnerVisibility(true);
@@ -0,0 +1,190 @@
package com.sap.sailing.racecommittee.app.ui.adapters;
import java.util.ArrayList;
import android.content.Context;
import android.database.DataSetObserver;
import android.support.annotation.LayoutRes;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import com.sap.sailing.android.shared.util.ViewHolder;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.utils.RaceHelper;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
public class DependentRaceSpinnerAdapter implements SpinnerAdapter {
private static final int HEADER = 0;
private static final int DATA = 1;
private Context mContext;
private int mLayout;
private int mSelectedItem = -1;
private ArrayList<RaceData> mData;
public DependentRaceSpinnerAdapter(Context context, @LayoutRes int layout) {
mContext = context;
mLayout = layout;
mData = new ArrayList<>();
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View layout = convertView;
if (layout == null) {
layout = LayoutInflater.from(mContext).inflate(mLayout, parent, false);
}
TextView mainText = ViewHolder.get(layout, android.R.id.text1);
if (mainText != null) {
mainText.setText(mData.get(position).getMainText());
}
TextView subTextView = ViewHolder.get(layout, android.R.id.text2);
String subText = mData.get(position).getSubText();
if (subTextView != null) {
if (getItemViewType(position) == HEADER) {
layout.setBackgroundColor(ThemeHelper.getColor(mContext, R.attr.sap_gray_black_20));
if (subText != null) {
subTextView.setTextColor(ThemeHelper.getColor(mContext, R.attr.sap_light_gray));
subTextView.setText(subText);
subTextView.setVisibility(View.VISIBLE);
} else {
subTextView.setVisibility(View.GONE);
}
layout.setClickable(true);
if (mainText != null) {
mainText.setTextColor(ThemeHelper.getColor(mContext, R.attr.sap_light_gray));
}
} else {
if (mSelectedItem == position) {
layout.setBackgroundColor(ThemeHelper.getColor(mContext, R.attr.sap_light_gray));
} else {
layout.setBackgroundColor(ThemeHelper.getColor(mContext, R.attr.sap_gray_black_30));
}
subTextView.setVisibility(View.GONE);
layout.setClickable(false);
if (mainText != null) {
mainText.setTextColor(ThemeHelper.getColor(mContext, R.attr.white));
}
}
}
return layout;
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View layout = convertView;
if (layout == null) {
layout = LayoutInflater.from(mContext).inflate(mLayout, parent, false);
}
TextView text = ViewHolder.get(layout, android.R.id.text1);
if (text != null && position < mData.size() && mData.get(position).getRace() != null) {
String raceName = mData.get(position).getRace().getName();
String additional = RaceHelper.getFleetSeries(mData.get(position).getRace());
if (!TextUtils.isEmpty(additional)) {
additional += " - ";
}
additional += RaceHelper.getRaceGroupName(mData.get(position).getRace());
if (!TextUtils.isEmpty(additional)) {
raceName += " (" + additional + ")";
}
text.setText(raceName);
}
return layout;
}
@Override
public int getItemViewType(int position) {
if (mData.get(position).getRace() == null) {
return HEADER;
}
return DATA;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public boolean isEmpty() {
return (mData.size() == 0);
}
public void add(RaceData data) {
mData.add(data);
}
public void setSelected(int position) {
mSelectedItem = position;
}
public ManagedRace getRaceAtPosition(int position) {
return mData.get(position).getRace();
}
public static class RaceData {
private String mText1;
private String mText2;
private ManagedRace mRace;
public RaceData(String mainText, String subText, ManagedRace race) {
mText1 = mainText;
mText2 = subText;
mRace = race;
}
public String getMainText() {
return mText1;
}
public String getSubText() {
return mText2;
}
public ManagedRace getRace() {
return mRace;
}
}
}
@@ -1,5 +1,8 @@
package com.sap.sailing.racecommittee.app.ui.adapters.racelist;
import java.text.SimpleDateFormat;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -14,8 +17,12 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.ViewHolder;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.FlagPoleState;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure;
@@ -23,18 +30,19 @@ import com.sap.sailing.domain.common.racelog.FlagPole;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.data.AndroidRaceLogResolver;
import com.sap.sailing.racecommittee.app.data.DataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.ui.adapters.racelist.RaceFilter.FilterSubscriber;
import com.sap.sailing.racecommittee.app.ui.utils.FlagsResources;
import com.sap.sailing.racecommittee.app.utils.BitmapHelper;
import com.sap.sailing.racecommittee.app.utils.RaceHelper;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
import com.sap.sailing.racecommittee.app.utils.TimeUtils;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import java.text.SimpleDateFormat;
import java.util.List;
public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> implements FilterSubscriber {
private final static String TAG = ManagedRaceListAdapter.class.getName();
@@ -53,13 +61,14 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
private TextView race_finished;
private LinearLayout race_scheduled;
private TextView race_unscheduled;
private TextView depends_on;
private ImageView current_flag;
private TextView race_name;
private TextView flag_timer;
private ImageView arrow_direction;
private TextView boat_class;
private TextView fleet_series;
private ImageView protest_image;
private ImageView has_dependent_races;
private SimpleDateFormat dateFormat;
private RaceListDataType mSelectedRace;
@@ -70,7 +79,7 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
mShownViewItems = viewItems;
mInflater = LayoutInflater.from(getContext());
mResources = getContext().getResources();
dateFormat = new SimpleDateFormat("HH:mm", getContext().getResources().getConfiguration().locale);
dateFormat = new SimpleDateFormat("kk:mm", getContext().getResources().getConfiguration().locale);
}
@Override
@@ -129,17 +138,7 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
regatta = header.getRaceGroup().getName();
}
boat_class.setText(regatta);
String fleetSeries = "";
if (header.getFleet() != null && !header.getFleet().getName().equals(AppConstants.DEFAULT)) {
fleetSeries += header.getFleet().getName();
}
if (header.getSeries() != null && !header.getSeries().getName().equals(AppConstants.DEFAULT)) {
if (!TextUtils.isEmpty(fleetSeries)) {
fleetSeries += " - ";
}
fleetSeries += header.getSeries().getName();
}
fleet_series.setText(fleetSeries);
fleet_series.setText(RaceHelper.getFleetSeries(header.getFleet(), header.getSeries()));
protest_image.setImageDrawable(FlagsResources.getFlagDrawable(getContext(), Flags.BRAVO.name(), FLAG_SIZE));
protest_image.setOnClickListener(new View.OnClickListener() {
@Override
@@ -156,8 +155,16 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
if (mSelectedRace != null && mSelectedRace.equals(race)) {
setMarker(1 - getLevel());
convertView.setBackgroundColor(ThemeHelper.getColor(getContext(), R.attr.sap_gray_black_20));
if (race.isUpdateIndicatorVisible()) {
race.setUpdateIndicatorVisible(false);
}
} else {
convertView.setBackgroundColor(ThemeHelper.getColor(getContext(), R.attr.sap_gray));
if (race.isUpdateIndicatorVisible()) {
update_badge.setVisibility(View.VISIBLE);
}
}
}
@@ -182,6 +189,11 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
}
time.setTextSize(textSize);
}
StartTimeFinder stf = new StartTimeFinder(new AndroidRaceLogResolver(), race.getRace().getRaceLog());
StartTimeFinderResult result = stf.analyze();
if (result != null && result.isDependentStartTime()) {
has_dependent_races.setVisibility(View.VISIBLE);
}
}
if (state.getFinishedTime() != null) {
time.setVisibility(View.GONE);
@@ -189,8 +201,28 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
race_finished.setText(mResources.getString(R.string.race_finished, dateFormat.format(state.getFinishedTime().asDate())));
}
if (state.getStartTime() == null && state.getFinishedTime() == null) {
switch (race.getRace().getStatus()) {
case PRESCHEDULED:
race_scheduled.setVisibility(View.GONE);
race_unscheduled.setVisibility(View.GONE);
if (depends_on != null) {
StartTimeFinder stf = new StartTimeFinder(new AndroidRaceLogResolver(), race.getRace().getRaceLog());
StartTimeFinderResult result = stf.analyze();
if (result != null && result.isDependentStartTime()) {
SimpleRaceLogIdentifier identifier = Util.get(result.getRacesDependingOn(), 0);
ManagedRace tmp = DataManager.create(getContext()).getDataStore().getRace(identifier);
depends_on.setText(getContext()
.getString(R.string.minutes_after_long, result.getStartTimeDiff().asMinutes(), RaceHelper
.getRaceName(tmp, " / ")));
depends_on.setVisibility(View.VISIBLE);
}
}
break;
default:
race_scheduled.setVisibility(View.GONE);
race_unscheduled.setVisibility(View.VISIBLE);
}
} else {
if (race_name != null) {
race_name.setTextColor(ThemeHelper.getColor(getContext(), R.attr.white));
@@ -198,10 +230,6 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
}
}
if (race.isUpdateIndicatorVisible()) {
update_badge.setVisibility(View.VISIBLE);
}
updateFlag(race.getRace(), now);
}
return convertView;
@@ -229,26 +257,27 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
mSelectedRace = id;
}
private void findViews(View convertView) {
marker = ViewHolder.get(convertView, R.id.race_marker);
arrow_direction = ViewHolder.get(convertView, R.id.arrow_direction);
current_flag = ViewHolder.get(convertView, R.id.current_flag);
update_badge = ViewHolder.get(convertView, R.id.update_badge);
race_flag = ViewHolder.get(convertView, R.id.race_flag);
time = ViewHolder.get(convertView, R.id.time);
race_name = ViewHolder.get(convertView, R.id.race_name);
race_finished = ViewHolder.get(convertView, R.id.race_finshed);
race_started = ViewHolder.get(convertView, R.id.race_started);
race_scheduled = ViewHolder.get(convertView, R.id.race_scheduled);
race_unscheduled = ViewHolder.get(convertView, R.id.race_unscheduled);
flag_timer = ViewHolder.get(convertView, R.id.flag_timer);
protest_image = ViewHolder.get(convertView, R.id.protest_image);
boat_class = ViewHolder.get(convertView, R.id.boat_class);
fleet_series = ViewHolder.get(convertView, R.id.fleet_series);
private void findViews(View layout) {
marker = ViewHolder.get(layout, R.id.race_marker);
current_flag = ViewHolder.get(layout, R.id.current_flag);
update_badge = ViewHolder.get(layout, R.id.update_badge);
race_flag = ViewHolder.get(layout, R.id.race_flag);
time = ViewHolder.get(layout, R.id.time);
race_name = ViewHolder.get(layout, R.id.race_name);
race_finished = ViewHolder.get(layout, R.id.race_finshed);
race_started = ViewHolder.get(layout, R.id.race_started);
race_scheduled = ViewHolder.get(layout, R.id.race_scheduled);
race_unscheduled = ViewHolder.get(layout, R.id.race_unscheduled);
flag_timer = ViewHolder.get(layout, R.id.flag_timer);
protest_image = ViewHolder.get(layout, R.id.protest_image);
boat_class = ViewHolder.get(layout, R.id.boat_class);
fleet_series = ViewHolder.get(layout, R.id.fleet_series);
has_dependent_races = ViewHolder.get(layout, R.id.has_dependent_races);
depends_on = ViewHolder.get(layout, R.id.depends_on);
}
private void resetValues(View convertView) {
if (convertView != null) {
private void resetValues(View layout) {
if (layout != null) {
if (update_badge != null) {
update_badge.setVisibility(View.GONE);
}
@@ -273,6 +302,13 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
if (race_name != null) {
race_name.setTextColor(ThemeHelper.getColor(getContext(), R.attr.sap_light_gray));
}
if (has_dependent_races != null) {
has_dependent_races.setVisibility(View.GONE);
}
if (depends_on != null) {
depends_on.setTextColor(ThemeHelper.getColor(getContext(), R.attr.sap_light_gray));
depends_on.setVisibility(View.GONE);
}
setMarker(0);
}
}
@@ -354,8 +390,8 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
private void showFlag(LayerDrawable flag, Drawable arrow, String timer) {
if (flag != null && arrow != null && timer != null) {
current_flag.setImageDrawable(flag);
arrow_direction.setImageDrawable(arrow);
flag_timer.setText(timer);
flag_timer.setCompoundDrawablesWithIntrinsicBounds(arrow, null, null, null);
race_flag.setVisibility(View.VISIBLE);
}
}
@@ -37,7 +37,7 @@ public class RaceFilter extends Filter {
protected static FilterResults createResults(Collection<RaceListDataType> result) {
FilterResults results = new FilterResults();
results.values = new ArrayList<RaceListDataType>(result);
results.values = new ArrayList<>(result);
results.count = result.size();
return results;
}
@@ -58,7 +58,7 @@ public class RaceFilter extends Filter {
RaceListDataTypeHeader headerItem = (RaceListDataTypeHeader) item;
filteredItems.add(headerItem);
// new run for both types!
// new run for all types!
currentUnscheduledItem = null;
currentFinishedItem = null;
} else if (item instanceof RaceListDataTypeRace) {
@@ -76,7 +76,7 @@ public class RaceFilter extends Filter {
currentUnscheduledItem = null;
} else if (RaceLogRaceStatus.isActive(status)) {
filteredItems.add(raceItem);
// new run for both types!
// new run for all types!
currentUnscheduledItem = null;
currentFinishedItem = null;
}
@@ -14,6 +14,7 @@ import com.sap.sse.common.TimePoint;
public class RaceListDataTypeRace implements RaceListDataType {
private static String unscheduledTemplate;
private static String prescheduledTemplate;
private static String scheduldedTemplate;
private static String startPhaseTemplate;
private static String runningTemplate;
@@ -29,6 +30,7 @@ public class RaceListDataTypeRace implements RaceListDataType {
public static void initializeTemplates(Fragment fragment) {
unscheduledTemplate = fragment.getString(R.string.racelist_unscheduled);
prescheduledTemplate = fragment.getString(R.string.racelist_prescheduled);
scheduldedTemplate = fragment.getString(R.string.racelist_scheduled);
startPhaseTemplate = fragment.getString(R.string.racelist_startphase);
runningTemplate = fragment.getString(R.string.racelist_running);
@@ -77,6 +79,8 @@ public class RaceListDataTypeRace implements RaceListDataType {
switch (status) {
case UNSCHEDULED:
return unscheduledTemplate;
case PRESCHEDULED:
return String.format(prescheduledTemplate, formatStartTime());
case SCHEDULED:
return String.format(scheduldedTemplate, formatStartTime());
case STARTPHASE:
@@ -123,7 +123,7 @@ public class RaceInfoFragment extends RaceFragment implements RaceInfoListener {
prepareResetRaceView();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_AlertDialog);
builder.setView(resetRaceDialogView).setTitle(R.string.race_reset_confirmation_title)
.setIcon(R.drawable.ic_warning_grey600_36dp).setCancelable(true)
.setIcon(R.drawable.ic_warning_grey_600_36dp).setCancelable(true)
.setPositiveButton(getString(R.string.race_reset_reset_button), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
ExLog.i(getActivity(), LogEvent.RACE_RESET_YES, getRace().getId().toString());
@@ -59,15 +59,14 @@ import com.sap.sailing.racecommittee.app.ui.comparators.RaceListDataTypeComparat
import com.sap.sailing.racecommittee.app.ui.comparators.RegattaSeriesFleetComparator;
import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.ProtestTimeDialogFragment;
import com.sap.sailing.racecommittee.app.utils.BitmapHelper;
import com.sap.sailing.racecommittee.app.utils.RaceHelper;
import com.sap.sailing.racecommittee.app.utils.StringHelper;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
import com.sap.sailing.racecommittee.app.utils.TickListener;
import com.sap.sailing.racecommittee.app.utils.TickSingleton;
import com.sap.sse.common.TimePoint;
public class RaceListFragment
extends LoggableFragment
implements OnItemClickListener, OnItemSelectedListener, TickListener, OnScrollListener {
public class RaceListFragment extends LoggableFragment implements OnItemClickListener, OnItemSelectedListener, TickListener, OnScrollListener {
private final static String TAG = RaceListFragment.class.getName();
private final static String LAYOUT = "layout";
@@ -123,12 +122,8 @@ public class RaceListFragment
public static void showProtest(Context context, ManagedRace race) {
Intent intent = new Intent(AppConstants.INTENT_ACTION_SHOW_PROTEST);
String extra = race.getRaceGroup().getName();
if (!race.getSeries().getName().equals(AppConstants.DEFAULT)) {
extra += " - " + race.getSeries().getName();
}
if (!race.getFleet().getName().equals(AppConstants.DEFAULT)) {
extra += " - " + race.getFleet().getName();
}
extra += RaceHelper.getSeriesName(race.getSeries());
extra += RaceHelper.getFleetName(race.getFleet());
intent.putExtra(AppConstants.INTENT_ACTION_EXTRA, extra);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
@@ -472,42 +467,6 @@ public class RaceListFragment
}
}
public enum FilterMode {
ACTIVE(R.string.race_list_filter_show_active), ALL(R.string.race_list_filter_show_all);
private String displayName;
FilterMode(int resId) {
this.displayName = RaceApplication.getStringContext().getString(resId);
}
@Override
public String toString() {
return displayName;
}
}
public interface RaceListCallbacks {
void onRaceListItemSelected(RaceListDataType selectedItem);
}
private class IntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (AppConstants.INTENT_ACTION_SHOW_PROTEST.equals(intent.getAction())) {
String raceGroupName = intent.getExtras().getString(AppConstants.INTENT_ACTION_EXTRA);
if (raceGroupName != null) {
showProtestTimeDialog(raceGroupName);
} else {
ExLog.e(getActivity(), TAG, "INTENT_ACTION_SHOW_PROTEST does not carry an INTENT_ACTION_EXTRA with the race group name!");
}
}
}
}
private void showProtestTimeDialog(String raceGroupName) {
// Find the race group for which the
for (RaceGroupSeriesFleet raceGroupSeriesFleet : mRacesByGroup.keySet()) {
@@ -535,5 +494,38 @@ public class RaceListFragment
return false;
}
public enum FilterMode {
ACTIVE(R.string.race_list_filter_show_active), ALL(R.string.race_list_filter_show_all);
private String displayName;
FilterMode(int resId) {
this.displayName = RaceApplication.getStringContext().getString(resId);
}
@Override
public String toString() {
return displayName;
}
}
public interface RaceListCallbacks {
void onRaceListItemSelected(RaceListDataType selectedItem);
}
private class IntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (AppConstants.INTENT_ACTION_SHOW_PROTEST.equals(intent.getAction())) {
String raceGroupName = intent.getExtras().getString(AppConstants.INTENT_ACTION_EXTRA);
if (raceGroupName != null) {
showProtestTimeDialog(raceGroupName);
} else {
ExLog.e(getActivity(), TAG, "INTENT_ACTION_SHOW_PROTEST does not carry an INTENT_ACTION_EXTRA with the race group name!");
}
}
}
}
}
@@ -24,7 +24,8 @@ public abstract class RaceInfoFragmentChooser {
public RaceFragment choose(Context context, ManagedRace managedRace) {
switch (managedRace.getStatus()) {
case UNSCHEDULED:
return createInfoFragment(StartTimeFragment.newInstance(1), managedRace);
return createInfoFragment(StartTimeFragment.newInstance(StartTimeFragment.MODE_1), managedRace);
case PRESCHEDULED:
case SCHEDULED:
case STARTPHASE:
return createInfoFragment(context, getStartphaseFragment(), managedRace);
@@ -35,6 +35,6 @@ public class LoadFailedDialog extends FragmentAttachedDialogFragment {
getString(R.string.generic_load_failure),
getArguments().getString(ARGS_ERROR_MSG)))
.setTitle(getString(R.string.loading_failure))
.setIcon(R.drawable.ic_warning_grey600_36dp);
.setIcon(R.drawable.ic_warning_grey_600_36dp);
}
}
@@ -1,11 +1,16 @@
package com.sap.sailing.racecommittee.app.ui.fragments.panels;
import android.content.*;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.ViewHolder;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
@@ -131,9 +136,22 @@ public class FlagPanelFragment extends BasePanelFragment {
changeVisibility(mRecallFlags, View.VISIBLE);
changeVisibility(mCourseFlags, View.GONE);
changeVisibility(mMoreFlags, View.VISIBLE);
uncheckMarker(mCourseFlags);
break;
case PRESCHEDULED:
changeVisibility(mPostponeFlags, View.VISIBLE);
changeVisibility(mAbandonFlags, View.GONE);
changeVisibility(mRecallFlags, View.GONE);
changeVisibility(mCourseFlags, View.GONE);
changeVisibility(mMoreFlags, View.GONE);
uncheckMarker(mAbandonFlags);
uncheckMarker(mCourseFlags);
uncheckMarker(mMoreFlags);
break;
case SCHEDULED:
case STARTPHASE:
changeVisibility(mPostponeFlags, View.VISIBLE);
@@ -141,6 +159,7 @@ public class FlagPanelFragment extends BasePanelFragment {
changeVisibility(mRecallFlags, View.GONE);
changeVisibility(mCourseFlags, View.GONE);
changeVisibility(mMoreFlags, View.GONE);
uncheckMarker(mAbandonFlags);
uncheckMarker(mRecallFlags);
uncheckMarker(mMoreFlags);
@@ -154,6 +173,7 @@ public class FlagPanelFragment extends BasePanelFragment {
changeVisibility(mRecallFlags, View.VISIBLE);
changeVisibility(mCourseFlags, View.GONE);
changeVisibility(mMoreFlags, View.VISIBLE);
uncheckMarker(mCourseFlags);
break;
@@ -165,6 +185,7 @@ public class FlagPanelFragment extends BasePanelFragment {
changeVisibility(mRecallFlags, View.GONE);
changeVisibility(mCourseFlags, View.GONE);
changeVisibility(mMoreFlags, View.GONE);
uncheckMarker(mRecallFlags);
uncheckMarker(mCourseFlags);
uncheckMarker(mMoreFlags);
@@ -178,6 +199,7 @@ public class FlagPanelFragment extends BasePanelFragment {
changeVisibility(mRecallFlags, View.VISIBLE);
changeVisibility(mCourseFlags, View.GONE);
changeVisibility(mMoreFlags, View.VISIBLE);
uncheckMarker(mCourseFlags);
break;
@@ -187,6 +209,7 @@ public class FlagPanelFragment extends BasePanelFragment {
changeVisibility(mRecallFlags, View.GONE);
changeVisibility(mCourseFlags, View.GONE);
changeVisibility(mMoreFlags, View.GONE);
uncheckMarker(mPostponeFlags);
uncheckMarker(mAbandonFlags);
uncheckMarker(mRecallFlags);
@@ -1,6 +1,10 @@
package com.sap.sailing.racecommittee.app.ui.fragments.panels;
import android.content.*;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.view.LayoutInflater;
@@ -8,6 +12,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.ViewHolder;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
@@ -168,6 +173,13 @@ public class SetupPanelFragment extends BasePanelFragment {
changeVisibility(mWindLock, View.GONE);
break;
case PRESCHEDULED:
changeVisibility(mStartProcedureLock, View.GONE);
changeVisibility(mStartModeLock, View.GONE);
changeVisibility(mCourseLock, View.GONE);
changeVisibility(mWindLock, View.GONE);
break;
case SCHEDULED:
changeVisibility(mStartProcedureLock, View.GONE);
changeVisibility(mStartModeLock, View.GONE);
@@ -1,28 +1,41 @@
package com.sap.sailing.racecommittee.app.ui.fragments.panels;
import android.content.*;
import java.text.SimpleDateFormat;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.ViewHolder;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.BaseRaceStateChangedListener;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.data.AndroidRaceLogResolver;
import com.sap.sailing.racecommittee.app.data.DataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.StartTimeFragment;
import com.sap.sailing.racecommittee.app.utils.TickListener;
import com.sap.sailing.racecommittee.app.utils.RaceHelper;
import com.sap.sailing.racecommittee.app.utils.TickSingleton;
import com.sap.sailing.racecommittee.app.utils.TimeUtils;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import java.text.SimpleDateFormat;
public class TimePanelFragment extends BasePanelFragment implements TickListener {
public class TimePanelFragment extends BasePanelFragment {
public final static String TOGGLED = "toggled";
@@ -36,6 +49,8 @@ public class TimePanelFragment extends BasePanelFragment implements TickListener
private TextView mHeaderTime;
private TextView mTimeStart;
private TextView mTimeFinish;
private ImageView mLinkIcon;
private Boolean mLinkedRace = null;
public TimePanelFragment() {
mReceiver = new IntentReceiver();
@@ -64,6 +79,8 @@ public class TimePanelFragment extends BasePanelFragment implements TickListener
mTimeFinish = ViewHolder.get(layout, R.id.time_finish);
mHeaderTime = ViewHolder.get(layout, R.id.timer_text);
mTimeStart = ViewHolder.get(layout, R.id.time_start);
mLinkIcon = ViewHolder.get(layout, R.id.linked_race);
if (getArguments().getBoolean(TOGGLED, false)) {
toggleMarker(layout, R.id.time_marker);
}
@@ -75,7 +92,6 @@ public class TimePanelFragment extends BasePanelFragment implements TickListener
public void onResume() {
super.onResume();
TickSingleton.INSTANCE.registerListener(this);
notifyTick(MillisecondsTimePoint.now());
checkStatus();
@@ -111,8 +127,12 @@ public class TimePanelFragment extends BasePanelFragment implements TickListener
if (getRace() != null && getRace().getState() != null) {
TimePoint startTime = getRace().getState().getStartTime();
if (mTimeStart != null && startTime != null) {
mTimeStart.setText(getString(R.string.time_start).replace("#TIME#", dateFormat.format(startTime.asDate())));
if (mTimeStart != null) {
if (startTime != null) {
mTimeStart.setText(getString(R.string.time_start, dateFormat.format(startTime.asDate())));
} else {
mTimeStart.setText(getString(R.string.time_start, "N/A"));
}
}
if (mTimeFinish != null) {
@@ -129,9 +149,26 @@ public class TimePanelFragment extends BasePanelFragment implements TickListener
resId = R.string.race_start_time_ago;
time = TimeUtils.formatDurationSince(now.minus(startTime.asMillis()).asMillis());
}
mHeaderTime.setText(getString(resId).replace("#TIME#", time));
mHeaderTime.setText(getString(resId, time));
}
}
if (mLinkedRace == null) {
StartTimeFinder stf = new StartTimeFinder(new AndroidRaceLogResolver(), getRace().getRaceLog());
StartTimeFinderResult result = stf.analyze();
if (result != null) {
mLinkedRace = result.isDependentStartTime();
if (mLinkedRace && mHeaderTime != null) {
SimpleRaceLogIdentifier identifier = Util.get(result.getRacesDependingOn(), 0);
ManagedRace race = DataManager.create(getActivity()).getDataStore().getRace(identifier);
mHeaderTime.setText(getString(R.string.minutes_after_long, result.getStartTimeDiff().asMinutes(), RaceHelper.getRaceName(race, " / ")));
}
}
}
if (mLinkedRace != null && mLinkIcon != null) {
mLinkIcon.setVisibility(mLinkedRace ? View.VISIBLE : View.GONE);
}
}
private void uncheckMarker(View view) {
@@ -149,6 +186,10 @@ public class TimePanelFragment extends BasePanelFragment implements TickListener
changeVisibility(mTimeLock, View.GONE);
break;
case PRESCHEDULED:
changeVisibility(mTimeLock, View.GONE);
break;
case SCHEDULED:
changeVisibility(mTimeLock, View.GONE);
break;
@@ -204,7 +245,7 @@ public class TimePanelFragment extends BasePanelFragment implements TickListener
break;
case 1:
replaceFragment(StartTimeFragment.newInstance(2));
replaceFragment(StartTimeFragment.newInstance(StartTimeFragment.MODE_TIME_PANEL));
break;
default:
@@ -252,5 +293,12 @@ public class TimePanelFragment extends BasePanelFragment implements TickListener
checkStatus();
uncheckMarker(mView);
}
@Override
public void onStartTimeChanged(ReadonlyRaceState state) {
super.onStartTimeChanged(state);
mLinkedRace = null;
}
}
}
@@ -88,8 +88,6 @@ public class GeneralPreferenceFragment extends BasePreferenceFragment {
setupServerUrlBox();
setupSyncQRCodeButton();
setupForceUpdateButton();
bindPreferenceSummaryToValue(findPreference(R.string.preference_server_url_key));
}
private void setupIdentifierBox() {
@@ -105,6 +105,7 @@ public class RegattaPreferenceFragment extends BasePreferenceFragment {
private void setupGeneral() {
setupRacingProcedureTypePreference();
setupCourseDesignerTypePreference();
setupDependentRacesPreference();
}
private void setupRacingProcedureTypePreference() {
@@ -139,6 +140,10 @@ public class RegattaPreferenceFragment extends BasePreferenceFragment {
bindPreferenceSummaryToValue(preference);
}
private void setupDependentRacesPreference() {
bindPreferenceSummaryToInteger(findPreference(R.string.preference_dependent_races_offset_key));
}
private void setupRRS26StartmodeFlagsList() {
MultiSelectListPreference preference = findPreference(R.string.preference_racing_procedure_rrs26_startmode_flags_key);
List<String> flags = new ArrayList<>();
@@ -15,7 +15,6 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.NumberPicker;
import android.widget.TextView;
import com.sap.sailing.android.shared.util.ViewHolder;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.gate.GateStartRacingProcedure;
import com.sap.sailing.racecommittee.app.AppConstants;
@@ -70,8 +69,7 @@ public class GateStartFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View layout = LayoutInflater.from(getActivity()).inflate(
R.layout.race_schedule_procedure_gate_start_pathfinder, container, false);
final View layout = LayoutInflater.from(getActivity()).inflate(R.layout.race_schedule_procedure_gate_start_pathfinder, container, false);
mNat = ViewHolder.get(layout, R.id.pathfinder_nat);
mNum = ViewHolder.get(layout, R.id.pathfinder_num);
@@ -201,8 +199,7 @@ public class GateStartFragment {
private void enableSetButton(View view, View button) {
if (view != null && view.getTag(R.id.pathfinder_nat) != null && view.getTag(R.id.pathfinder_num) != null) {
button.setEnabled(
(Boolean) view.getTag(R.id.pathfinder_nat) && (Boolean) view.getTag(R.id.pathfinder_num));
button.setEnabled((Boolean) view.getTag(R.id.pathfinder_nat) && (Boolean) view.getTag(R.id.pathfinder_num));
}
}
}
@@ -235,8 +232,7 @@ public class GateStartFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = LayoutInflater.from(getActivity())
.inflate(R.layout.race_schedule_procedure_gate_start_timing, container, false);
View layout = LayoutInflater.from(getActivity()).inflate(R.layout.race_schedule_procedure_gate_start_timing, container, false);
View caption = ViewHolder.get(layout, R.id.header_text);
if (caption != null) {
@@ -244,8 +240,7 @@ public class GateStartFragment {
@Override
public void onClick(View v) {
replaceFragment(Pathfinder
.newInstance(getArguments().getString(NAT, null), getArguments().getString(NUM, null)));
replaceFragment(Pathfinder.newInstance(getArguments().getString(NAT, null), getArguments().getString(NUM, null)));
}
});
}
@@ -279,8 +274,10 @@ public class GateStartFragment {
mTimeLaunch = (NumberPicker) getActivity().findViewById(R.id.time_launch);
mTimeGolf = (NumberPicker) getActivity().findViewById(R.id.time_golf);
mProcedure = getRaceState().getTypedRacingProcedure();
ThemeHelper.setPickerTextColor(getActivity(), mTimeLaunch, ThemeHelper.getColor(getActivity(), R.attr.white));
ThemeHelper.setPickerTextColor(getActivity(), mTimeGolf, ThemeHelper.getColor(getActivity(), R.attr.white));
ThemeHelper.setPickerColor(getActivity(), mTimeLaunch, ThemeHelper.getColor(getActivity(), R.attr.white), ThemeHelper
.getColor(getActivity(), R.attr.sap_yellow_1));
ThemeHelper.setPickerColor(getActivity(), mTimeGolf, ThemeHelper.getColor(getActivity(), R.attr.white), ThemeHelper
.getColor(getActivity(), R.attr.sap_yellow_1));
setTimeLaunchWidget();
setTimeGolfWidget();
@@ -1,5 +1,9 @@
package com.sap.sailing.racecommittee.app.ui.fragments.raceinfo;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
@@ -8,8 +12,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.ViewHolder;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.impl.SimpleRaceLogIdentifierImpl;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.BaseRaceStateChangedListener;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.rrs26.RRS26RacingProcedure;
@@ -17,26 +24,33 @@ import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.data.DataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.impl.FleetIdentifierImpl;
import com.sap.sailing.racecommittee.app.ui.activities.RacingActivity;
import com.sap.sailing.racecommittee.app.ui.fragments.RaceFragment;
import com.sap.sailing.racecommittee.app.ui.utils.FlagsResources;
import com.sap.sailing.racecommittee.app.utils.BitmapHelper;
import com.sap.sailing.racecommittee.app.utils.RaceHelper;
import com.sap.sailing.racecommittee.app.utils.TimeUtils;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
public class MainScheduleFragment extends BaseFragment implements View.OnClickListener {
public static final String START_TIME = "startTime";
public static final String DEPENDENT_RACE = "dependentRace";
public static final String START_TIME_DIFF = "startTimeDiff";
private static final String TAG = MainScheduleFragment.class.getName();
private TextView mStartTimeTextView;
private String mStartTimeString;
private String mRaceId;
private TimePoint mStartTime;
private Duration mStartTimeDiff;
private TextView mWindValue;
private RacingProcedureType mRacingProcedureType;
@@ -162,8 +176,8 @@ public class MainScheduleFragment extends BaseFragment implements View.OnClickLi
}
private void initStartTime() {
TimePoint timePoint = (TimePoint) getArguments().getSerializable(START_TIME);
RacingActivity activity = (RacingActivity) getActivity();
TimePoint timePoint = (TimePoint) getArguments().getSerializable(START_TIME);
if (timePoint == null && activity != null) {
timePoint = activity.getStartTime();
}
@@ -174,6 +188,14 @@ public class MainScheduleFragment extends BaseFragment implements View.OnClickLi
mStartTime = timePoint;
mStartTimeString = mDateFormat.format(timePoint.asDate());
}
mStartTimeDiff = (Duration) getArguments().getSerializable(START_TIME_DIFF);
mRaceId = (String) getArguments().getSerializable(DEPENDENT_RACE);
if (mRaceId != null && mStartTimeDiff != null) {
ManagedRace race = DataManager.create(getActivity()).getDataStore().getRace(mRaceId);
mStartTimeString = getString(R.string.minutes_after_long, mStartTimeDiff.asMinutes(), RaceHelper.getRaceName(race, " / "));
}
}
@Override
@@ -205,7 +227,7 @@ public class MainScheduleFragment extends BaseFragment implements View.OnClickLi
break;
case R.id.start_time:
openFragment(StartTimeFragment.newInstance(getArguments().getSerializable(START_TIME)));
openFragment(StartTimeFragment.newInstance(getArguments()));
break;
case R.id.wind:
@@ -226,7 +248,13 @@ public class MainScheduleFragment extends BaseFragment implements View.OnClickLi
}
TimePoint now = MillisecondsTimePoint.now();
getRaceState().setRacingProcedure(now, mRacingProcedureType);
if (mStartTimeDiff == null && mRaceId == null) {
getRaceState().forceNewStartTime(now, mStartTime);
} else {
Util.Triple<String, String, String> triple = FleetIdentifierImpl.unescape(mRaceId);
SimpleRaceLogIdentifier identifier = new SimpleRaceLogIdentifierImpl(triple.getA(), triple.getB(), triple.getC());
getRaceState().forceNewDependentStartTime(now, mStartTimeDiff, identifier);
}
if (procedure != null) {
procedure.setStartModeFlag(MillisecondsTimePoint.now(), flag);
}
@@ -237,17 +265,18 @@ public class MainScheduleFragment extends BaseFragment implements View.OnClickLi
super.notifyTick(now);
if (mStartTimeTextView != null && !TextUtils.isEmpty(mStartTimeString)) {
String startTimeValue = getString(R.string.start_time_value).replace("#TIME#", mStartTimeString)
.replace("#COUNTDOWN#", calcCountdown(now));
if (mRaceId == null && mStartTimeDiff == null) {
String startTimeValue = getString(R.string.start_time_value, mStartTimeString, calcCountdown(now));
mStartTimeTextView.setText(startTimeValue);
} else {
mStartTimeTextView.setText(mStartTimeString);
}
}
if (mWindValue != null && getRace() != null && getRaceState() != null && getRaceState().getWindFix() != null) {
String sensorData = getString(R.string.wind_sensor);
Wind wind = getRaceState().getWindFix();
sensorData = sensorData.replace("#AT#", mDateFormat.format(wind.getTimePoint().asDate()));
sensorData = sensorData.replace("#FROM#", String.format("%.0f", wind.getFrom().getDegrees()));
sensorData = sensorData.replace("#SPEED#", String.format("%.1f", wind.getKnots()));
String sensorData = getString(R.string.wind_sensor, mDateFormat.format(wind.getTimePoint().asDate()), wind.getFrom().getDegrees(), wind
.getKnots());
mWindValue.setText(sensorData);
}
}
@@ -269,9 +298,12 @@ public class MainScheduleFragment extends BaseFragment implements View.OnClickLi
} else {
fragment.setArguments(getRecentArguments());
}
if (mStartTime != null) {
fragment.getArguments().putSerializable(START_TIME, mStartTime);
}
Bundle args = fragment.getArguments();
args.putSerializable(START_TIME, mStartTime);
args.putSerializable(START_TIME_DIFF, mStartTimeDiff);
args.putSerializable(DEPENDENT_RACE, mRaceId);
getFragmentManager().beginTransaction().replace(R.id.racing_view_container, fragment).commitAllowingStateLoss();
}
@@ -108,15 +108,11 @@ public class MoreFlagsFragment extends BaseFragment implements MoreFlagItemClick
mDateFormat = new SimpleDateFormat("HH:mm:ss", getResources().getConfiguration().locale);
View header = ViewHolder.get(layout, R.id.header_text);
if (header != null) {
// header.setOnClickListener(this);
}
mTimePicker = ViewHolder.get(layout, R.id.time_picker);
if (mTimePicker != null) {
Calendar calendar = Calendar.getInstance();
ThemeHelper.setPickerTextColor(getActivity(), mTimePicker, ThemeHelper.getColor(getActivity(), R.attr.white));
ThemeHelper.setPickerColor(getActivity(), mTimePicker, ThemeHelper.getColor(getActivity(), R.attr.white), ThemeHelper
.getColor(getActivity(), R.attr.sap_yellow_1));
mTimePicker.setIs24HourView(true);
mTimePicker.setCurrentHour(calendar.get(Calendar.HOUR_OF_DAY));
}
@@ -133,26 +129,41 @@ public class MoreFlagsFragment extends BaseFragment implements MoreFlagItemClick
finishCustom.setOnClickListener(this);
}
return layout;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
switch (getArguments().getInt(START_MODE, 0)) {
case 1: // Race-State: Finishing -> End Finishing
ImageView flag = ViewHolder.get(layout, R.id.header_flag);
View header = ViewHolder.get(getView(), R.id.header_text);
if (header != null) {
header.setOnClickListener(this);
}
View back = ViewHolder.get(getView(), R.id.header_back);
if (back != null) {
back.setVisibility(View.VISIBLE);
}
ImageView flag = ViewHolder.get(getView(), R.id.header_flag);
if (flag != null) {
int resId = R.drawable.flag_blue_48dp;
Drawable drawable = BitmapHelper.getDrawable(getActivity(), resId);
flag.setImageDrawable(drawable);
}
TextView headline = ViewHolder.get(layout, R.id.header_headline);
TextView headline = ViewHolder.get(getView(), R.id.header_headline);
if (headline != null) {
headline.setText(getString(R.string.race_end_finish_header));
headline.setText(getString(R.string.race_end_finish_header, mDateFormat.format(getRaceState().getFinishingTime().asMillis())));
}
break;
default: // Race-State: Running -> Start Finishing
break;
}
return layout;
}
@Override
@@ -181,7 +192,7 @@ public class MoreFlagsFragment extends BaseFragment implements MoreFlagItemClick
break;
default:
// currently unused, because of no other button
sendIntent(AppConstants.INTENT_ACTION_SHOW_MAIN_CONTENT);
break;
}
}
@@ -9,7 +9,6 @@ import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -18,13 +17,13 @@ import android.widget.ImageView;
import android.widget.Toast;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.ViewHolder;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.AppPreferences;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.ui.adapters.finishing.FinishListPhotoAdapter;
import com.sap.sailing.racecommittee.app.ui.views.DividerItemDecoration;
import com.sap.sailing.racecommittee.app.utils.CameraHelper;
import com.sap.sailing.racecommittee.app.utils.MailHelper;
import com.sap.sailing.racecommittee.app.utils.RaceHelper;
import com.sap.sailing.racecommittee.app.utils.StringHelper;
import java.io.File;
@@ -173,35 +172,16 @@ public class PhotoListFragment extends BaseFragment {
private String getSubject() {
String author = StringHelper.on(getActivity()).getAuthor(preferences.getAuthor().getName());
return getString(R.string.results_mail_subject, author, getRaceName());
return getString(R.string.results_mail_subject, author, RaceHelper.getRaceName(getRace()));
}
private String getBody() {
StringBuilder builder = new StringBuilder();
builder.append(getString(R.string.results_mail_body, getRaceName()));
builder.append(getString(R.string.results_mail_body, RaceHelper.getRaceName(getRace())));
builder.append(getString(R.string.results_mail_body_race_group, getRace().getRaceGroup().getName()));
builder.append(getString(R.string.results_mail_body_boat_class, getRace().getRaceGroup().getBoatClass().getName()));
builder.append(getString(R.string.results_mail_body_start, mDateFormat.format(getRaceState().getStartTime().asDate())));
builder.append(getString(R.string.results_mail_body_finish, mDateFormat.format(getRaceState().getFinishedTime().asDate())));
return builder.toString();
}
private String getRaceName() {
String name = getRace().getRaceGroup().getDisplayName();
if (TextUtils.isEmpty(name)) {
name = getRace().getRaceGroup().getName();
}
if (!getRace().getSeries().getName().equals(AppConstants.DEFAULT)) {
name += " - " + getRace().getSeries().getName();
}
if (!getRace().getFleet().getName().equals(AppConstants.DEFAULT)) {
name += " - " + getRace().getFleet().getName();
}
name += " - " + getRace().getName();
return name;
}
}
@@ -131,7 +131,7 @@ public class RaceFlagViewerFragment extends BaseFragment {
}
}
} else {
if (mLayout != null && mFlagCache.nextPole != null) {
if (mFlagCache.nextPole != null) {
for (int i = 0; i < mLayout.getChildCount(); i++) {
ImageView flagImage = ViewHolder.get(mLayout.getChildAt(i), R.id.flag);
Flags flags = (Flags) flagImage.getTag();
@@ -95,7 +95,7 @@ public class RaceTimeChangeFragment extends BaseFragment implements View.OnClick
case FINISHING_TIME_MODE:
start.setTime(getRaceState().getStartTime().asDate());
mPastDays = TimeUtils.daysBetween(calendar, start);
mPastDays = TimeUtils.daysBetween(start, calendar);
finished.setTime(getRaceState().getFinishedTime().asDate());
mFutureDays = TimeUtils.daysBetween(finished, calendar);
break;
@@ -108,7 +108,8 @@ public class RaceTimeChangeFragment extends BaseFragment implements View.OnClick
default:
// use default values
}
ThemeHelper.setPickerTextColor(getActivity(), mDatePicker, ThemeHelper.getColor(getActivity(), R.attr.white));
ThemeHelper.setPickerColor(getActivity(), mDatePicker, ThemeHelper.getColor(getActivity(), R.attr.white), ThemeHelper
.getColor(getActivity(), R.attr.sap_yellow_1));
TimeUtils.initDatePicker(getActivity(), mDatePicker, calendar, mPastDays, mFutureDays, false);
mDatePicker.setValue(Math.abs(mPastDays));
mDatePicker.setTag(calendar);
@@ -119,7 +120,8 @@ public class RaceTimeChangeFragment extends BaseFragment implements View.OnClick
mTimePicker = (TimePicker) layout.findViewById(R.id.time_picker);
if (mTimePicker != null) {
ThemeHelper.setPickerTextColor(getActivity(), mTimePicker, ThemeHelper.getColor(getActivity(), R.attr.white));
ThemeHelper.setPickerColor(getActivity(), mTimePicker, ThemeHelper.getColor(getActivity(), R.attr.white), ThemeHelper
.getColor(getActivity(), R.attr.sap_yellow_1));
mTimePicker.setIs24HourView(true);
mTimePicker.setCurrentHour(calendar.get(Calendar.HOUR_OF_DAY));
mTimePicker.setCurrentMinute(calendar.get(Calendar.MINUTE));
@@ -127,7 +129,8 @@ public class RaceTimeChangeFragment extends BaseFragment implements View.OnClick
mSecondPicker = (NumberPicker) layout.findViewById(R.id.second_picker);
if (mSecondPicker != null) {
ThemeHelper.setPickerTextColor(getActivity(), mTimePicker, ThemeHelper.getColor(getActivity(), R.attr.white));
ThemeHelper.setPickerColor(getActivity(), mTimePicker, ThemeHelper.getColor(getActivity(), R.attr.white), ThemeHelper
.getColor(getActivity(), R.attr.sap_yellow_1));
mSecondPicker.setVisibility(View.VISIBLE);
mSecondPicker.setMinValue(0);
mSecondPicker.setMaxValue(59);
@@ -1,39 +1,80 @@
package com.sap.sailing.racecommittee.app.ui.fragments.raceinfo;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.NumberPicker;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import com.sap.sailing.android.shared.util.ViewHolder;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.impl.SimpleRaceLogIdentifierImpl;
import com.sap.sailing.domain.abstractlog.race.state.RaceStateChangedListener;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.BaseRaceStateChangedListener;
import com.sap.sailing.domain.common.impl.NaturalComparator;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.AppPreferences;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.data.DataStore;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.impl.FleetIdentifierImpl;
import com.sap.sailing.racecommittee.app.domain.impl.RaceGroupSeriesFleet;
import com.sap.sailing.racecommittee.app.ui.adapters.DependentRaceSpinnerAdapter;
import com.sap.sailing.racecommittee.app.ui.adapters.DependentRaceSpinnerAdapter.RaceData;
import com.sap.sailing.racecommittee.app.ui.fragments.RaceFragment;
import com.sap.sailing.racecommittee.app.utils.BitmapHelper;
import com.sap.sailing.racecommittee.app.utils.RaceHelper;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
import com.sap.sailing.racecommittee.app.utils.TimeUtils;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import java.io.Serializable;
import java.util.Calendar;
public class StartTimeFragment extends BaseFragment
implements View.OnClickListener, NumberPicker.OnValueChangeListener, TimePicker.OnTimeChangedListener {
public static final int MODE_SETUP = 0;
public static final int MODE_1 = 1;
public static final int MODE_TIME_PANEL = 2;
private static final String START_MODE = "startMode";
private static final int FUTURE_DAYS = 25;
private static final int PAST_DAYS = -3;
private static final int MAX_DIFF_MIN = 60;
private static final String ZERO_TIME = "-00:00:00";
private static final int ABSOLUTE = 0;
private static final int RELATIVE = 1;
private View mAbsolute;
private View mRelative;
private Button mAbsoluteButton;
private Button mRelativeButton;
private NumberPicker mDatePicker;
private NumberPicker mTimeOffset;
private Spinner mDependentRace;
private TimePicker mTimePicker;
private TextView mCountdown;
private TextView mDebugTime;
@@ -43,7 +84,10 @@ public class StartTimeFragment extends BaseFragment
private Calendar mTimeLeft;
private Calendar mTimeRight;
private Calendar mCalendar;
private String mRaceId;
private Duration mStartTimeOffset;
private boolean mListenerIgnore = true;
private DependentRaceSpinnerAdapter mAdapter;
/**
* Listens for start time changes
@@ -62,11 +106,11 @@ public class StartTimeFragment extends BaseFragment
return fragment;
}
public static StartTimeFragment newInstance(Serializable timePoint) {
StartTimeFragment fragment = newInstance(0);
if (timePoint != null) {
public static StartTimeFragment newInstance(Bundle extraArgs) {
StartTimeFragment fragment = newInstance(MODE_SETUP);
Bundle args = fragment.getArguments();
args.putSerializable(MainScheduleFragment.START_TIME, timePoint);
if (extraArgs != null) {
args.putAll(extraArgs);
}
return fragment;
}
@@ -75,6 +119,36 @@ public class StartTimeFragment extends BaseFragment
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(R.layout.race_schedule_start_time, container, false);
mAbsolute = ViewHolder.get(layout, R.id.time_absolute);
mRelative = ViewHolder.get(layout, R.id.time_relative);
if (preferences.isDependentRacesAllowed()) {
View view = ViewHolder.get(layout, R.id.tab_button);
if (view != null) {
view.setVisibility(View.VISIBLE);
}
mAbsoluteButton = ViewHolder.get(layout, R.id.absolute);
if (mAbsoluteButton != null) {
mAbsoluteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showTab(ABSOLUTE);
}
});
}
mRelativeButton = ViewHolder.get(layout, R.id.relative);
if (mRelativeButton != null) {
mRelativeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showTab(RELATIVE);
}
});
}
}
mCountdown = ViewHolder.get(layout, R.id.start_countdown);
mMinuteInc = ViewHolder.get(layout, R.id.minute_inc);
if (mMinuteInc != null) {
@@ -86,13 +160,17 @@ public class StartTimeFragment extends BaseFragment
mMinuteDec.setOnClickListener(this);
}
Button setStart = ViewHolder.get(layout, R.id.set_date_time);
if (setStart != null) {
setStart.setOnClickListener(this);
View setStartAbsolute = ViewHolder.get(layout, R.id.set_start_time_absolute);
if (setStartAbsolute != null) {
setStartAbsolute.setOnClickListener(this);
}
View setStartRelative = ViewHolder.get(layout, R.id.set_start_time_relative);
if (setStartRelative != null) {
setStartRelative.setOnClickListener(this);
}
mDebugTime = ViewHolder.get(layout, R.id.debug_time);
return layout;
}
@@ -122,32 +200,10 @@ public class StartTimeFragment extends BaseFragment
}
};
Calendar time = Calendar.getInstance();
if (getArguments() != null) {
int startMode = getArguments().getInt(START_MODE, 0);
switch (startMode) {
case 0:
mStartTime = (TimePoint) getArguments().getSerializable(MainScheduleFragment.START_TIME);
if (mStartTime != null) {
time.setTime(mStartTime.asDate());
}
break;
case 2:
if (getRace() != null && getRaceState() != null) {
mStartTime = getRaceState().getStartTime();
time.setTime(mStartTime.asDate());
}
break;
default:
break;
}
}
if (getView() != null) {
if (getArguments() != null) {
switch (getArguments().getInt(START_MODE, 0)) {
case 1:
switch (getArguments().getInt(START_MODE, MODE_SETUP)) {
case MODE_1:
View header = ViewHolder.get(getView(), R.id.header_text);
if (header != null) {
header.setOnClickListener(this);
@@ -158,34 +214,132 @@ public class StartTimeFragment extends BaseFragment
}
break;
case 2:
case MODE_TIME_PANEL:
if (getRace() != null && getRaceState() != null) {
mStartTime = getRaceState().getStartTime();
if (mStartTime != null) {
time.setTime(mStartTime.asDate());
}
}
View frame = ViewHolder.get(getView(), R.id.header);
if (frame != null) {
frame.setVisibility(View.GONE);
}
break;
default:
default: // MODE_SETUP
mStartTime = (TimePoint) getArguments().getSerializable(MainScheduleFragment.START_TIME);
if (mStartTime != null) {
time.setTime(mStartTime.asDate());
}
mStartTimeOffset = (Duration) getArguments().getSerializable(MainScheduleFragment.START_TIME_DIFF);
mRaceId = (String) getArguments().getSerializable(MainScheduleFragment.DEPENDENT_RACE);
View syncButtons = ViewHolder.get(getView(), R.id.buttonBar);
if (syncButtons != null) {
syncButtons.setVisibility(View.GONE);
}
break;
}
}
mDatePicker = ViewHolder.get(getView(), R.id.date_picker);
if (mRaceId == null && mStartTimeOffset == null) {
showTab(ABSOLUTE);
} else {
showTab(RELATIVE);
}
initViewsAbsolute(time);
initViewsRelative();
}
}
private void initViewsRelative() {
mTimeOffset = ViewHolder.get(getView(), R.id.time_offset);
if (mTimeOffset != null) {
ThemeHelper.setPickerColor(getActivity(), mTimeOffset, ThemeHelper.getColor(getActivity(), R.attr.white), ThemeHelper
.getColor(getActivity(), R.attr.sap_yellow_1));
mTimeOffset.setMinValue(0);
mTimeOffset.setMaxValue(MAX_DIFF_MIN);
mTimeOffset.setWrapSelectorWheel(false);
mTimeOffset.setValue((mStartTimeOffset == null) ? preferences.getDependentRacesOffset() : (int) mStartTimeOffset.asMinutes());
}
mDependentRace = ViewHolder.get(getView(), R.id.dependent_race);
if (mDependentRace != null) {
int position = 1;
mAdapter = new DependentRaceSpinnerAdapter(getActivity(), R.layout.dependent_race_item);
DataStore manager = OnlineDataManager.create(getActivity()).getDataStore();
Map<RaceGroupSeriesFleet, List<ManagedRace>> mGroupHeaders = new LinkedHashMap<>();
List<ManagedRace> sortedRaces = new ArrayList<>(manager.getRaces());
Collections.sort(sortedRaces, new Comparator<ManagedRace>() {
@Override
public int compare(ManagedRace lhs, ManagedRace rhs) {
return new NaturalComparator().compare(lhs.getId(), rhs.getId());
}
});
for (ManagedRace race : sortedRaces) {
RaceGroupSeriesFleet container = new RaceGroupSeriesFleet(race);
if (!mGroupHeaders.containsKey(container)) {
mGroupHeaders.put(container, new LinkedList<ManagedRace>());
}
mGroupHeaders.get(container).add(race);
}
for (RaceGroupSeriesFleet races : mGroupHeaders.keySet()) {
RaceData header = new RaceData(RaceHelper.getRaceGroupName(races.getRaceGroup()), RaceHelper
.getFleetSeries(races.getFleet(), races.getSeries()), null);
mAdapter.add(header);
for (ManagedRace race : mGroupHeaders.get(races)) {
// not current race
if (getRace() != null && !race.getId().equals(getRace().getId())) {
if (mRaceId != null && race.getId().equals(mRaceId)) {
position = mAdapter.getCount();
}
RaceData items = new RaceData(race.getRaceName(), null, race);
mAdapter.add(items);
}
}
}
mDependentRace.setAdapter(mAdapter);
mDependentRace.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mAdapter.setSelected(position);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
mAdapter.setSelected(-1);
}
});
mDependentRace.setSelection(position);
}
}
private void initViewsAbsolute(Calendar time) {
mDatePicker = ViewHolder.get(getView(), R.id.start_date_picker);
if (mDatePicker != null) {
ThemeHelper.setPickerTextColor(getActivity(), mDatePicker, ThemeHelper.getColor(getActivity(), R.attr.white));
ThemeHelper.setPickerColor(getActivity(), mDatePicker, ThemeHelper.getColor(getActivity(), R.attr.white), ThemeHelper
.getColor(getActivity(), R.attr.sap_yellow_1));
mDatePicker.setOnValueChangedListener(this);
TimeUtils.initDatePicker(getActivity(), mDatePicker, time, PAST_DAYS, FUTURE_DAYS);
mDatePicker.setValue(Math.abs(PAST_DAYS));
}
mTimePicker = ViewHolder.get(getView(), R.id.time_picker);
mTimePicker = ViewHolder.get(getView(), R.id.start_time_picker);
if (mTimePicker != null) {
ThemeHelper.setPickerTextColor(getActivity(), mTimePicker, ThemeHelper.getColor(getActivity(), R.attr.white));
ThemeHelper.setPickerColor(getActivity(), mTimePicker, ThemeHelper.getColor(getActivity(), R.attr.white), ThemeHelper
.getColor(getActivity(), R.attr.sap_yellow_1));
mTimePicker.setOnTimeChangedListener(this);
mTimePicker.setIs24HourView(true);
int hours = time.get(Calendar.HOUR_OF_DAY);
int minutes = time.get(Calendar.MINUTE);
if (getArguments() != null && getArguments().getInt(START_MODE, 0) != 2
&& getArguments().getSerializable(MainScheduleFragment.START_TIME) == null) {
if (getArguments() != null && getArguments().getInt(START_MODE, MODE_SETUP) != MODE_TIME_PANEL && mRaceId == null
&& mStartTimeOffset == null) {
// In 10 minutes from now, but always a 5-minute-mark.
time.add(Calendar.MINUTE, 10);
hours = time.get(Calendar.HOUR_OF_DAY);
@@ -200,12 +354,12 @@ public class StartTimeFragment extends BaseFragment
mTimePicker.setTag(time.get(Calendar.SECOND));
}
}
}
@Override
public void notifyTick(TimePoint now) {
super.notifyTick(now);
if (mAbsolute.getVisibility() == View.VISIBLE) {
int resId;
TimePoint timePoint;
String time;
@@ -239,23 +393,23 @@ public class StartTimeFragment extends BaseFragment
}
if (mCountdown != null) {
String countdown = getString(resId).replace("#TIME#", time);
String countdown = getString(resId, time);
mCountdown.setText(countdown);
mCountdown.setTag(resId);
}
if (mMinuteDec != null) {
String countdown = getString(resId).replace("#TIME#", timeLeft);
if (countdown.equals("-00:00:00")) {
countdown = "00:00:00";
String countdown = getString(resId, timeLeft);
if (ZERO_TIME.equals(countdown)) {
countdown = countdown.substring(1);
}
mMinuteDec.setText(countdown);
}
if (mMinuteInc != null) {
String countdown = getString(resId).replace("#TIME#", timeRight);
if (countdown.equals("-00:00:00")) {
countdown = "00:00:00";
String countdown = getString(resId, timeRight);
if (ZERO_TIME.equals(countdown)) {
countdown = countdown.substring(1);
}
mMinuteInc.setText(countdown);
}
@@ -264,6 +418,56 @@ public class StartTimeFragment extends BaseFragment
mDebugTime.setText(mStartTime.asDate().toString());
}
}
}
private void showTab(int tab) {
int colorGrey = ThemeHelper.getColor(getActivity(), R.attr.sap_light_gray);
int colorOrange = ThemeHelper.getColor(getActivity(), R.attr.sap_yellow_1);
if (mAbsolute != null) {
mAbsolute.setVisibility(View.GONE);
}
if (mRelative != null) {
mRelative.setVisibility(View.GONE);
}
if (mAbsoluteButton != null) {
mAbsoluteButton.setTextColor(colorGrey);
BitmapHelper.setBackground(mAbsoluteButton, null);
}
if (mRelativeButton != null) {
mRelativeButton.setTextColor(colorGrey);
BitmapHelper.setBackground(mRelativeButton, null);
}
int id;
if (AppConstants.LIGHT_THEME.equals(AppPreferences.on(getActivity()).getTheme())) {
id = R.drawable.nav_drawer_tab_button_light;
} else {
id = R.drawable.nav_drawer_tab_button_dark;
}
Drawable drawable = BitmapHelper.getDrawable(getActivity(), id);
switch (tab) {
case RELATIVE:
if (mRelative != null) {
mRelative.setVisibility(View.VISIBLE);
}
if (mRelativeButton != null) {
mRelativeButton.setTextColor(colorOrange);
BitmapHelper.setBackground(mRelativeButton, drawable);
}
break;
default: // ABSOLUTE
if (mAbsolute != null) {
mAbsolute.setVisibility(View.VISIBLE);
}
if (mAbsoluteButton != null) {
mAbsoluteButton.setTextColor(colorOrange);
BitmapHelper.setBackground(mAbsoluteButton, drawable);
}
break;
}
}
private void setButtonTime(TimePoint timePoint, boolean reverse) {
mCalendar.setTime(timePoint.asDate());
@@ -315,10 +519,15 @@ public class StartTimeFragment extends BaseFragment
setPickerTime();
break;
case R.id.set_date_time:
case R.id.set_start_time_absolute:
changeFragment(mStartTime);
break;
case R.id.set_start_time_relative:
ManagedRace race = mAdapter.getRaceAtPosition(mDependentRace.getSelectedItemPosition());
changeFragment(MillisecondsTimePoint.now(), new MillisecondsDurationImpl(mTimeOffset.getValue() * 60 * 1000), race.getId());
break;
case R.id.header_text:
changeFragment();
break;
@@ -351,39 +560,62 @@ public class StartTimeFragment extends BaseFragment
int days = TimeUtils.daysBetween(today, newTime) + Math.abs(PAST_DAYS);
if (mDatePicker != null) {
mDatePicker.setValue(days);
}
if (mTimePicker != null) {
mTimePicker.setCurrentHour(newTime.get(Calendar.HOUR_OF_DAY));
mTimePicker.setCurrentMinute(newTime.get(Calendar.MINUTE));
}
}
private TimePoint getPickerTime() {
Calendar calendar = Calendar.getInstance();
if (mDatePicker != null) {
calendar.add(Calendar.DAY_OF_MONTH, mDatePicker.getValue() + PAST_DAYS);
}
if (mTimePicker != null) {
calendar.set(Calendar.HOUR_OF_DAY, mTimePicker.getCurrentHour());
calendar.set(Calendar.MINUTE, mTimePicker.getCurrentMinute());
}
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return new MillisecondsTimePoint(calendar.getTime());
}
private void changeFragment() {
changeFragment(null);
changeFragment(null, null, null);
}
private void changeFragment(TimePoint startTime) {
changeFragment(startTime, null, null);
}
private void changeFragment(TimePoint startTime, Duration startTimeDiff, String raceId) {
int viewId = R.id.racing_view_container;
TimePoint now = MillisecondsTimePoint.now();
RaceFragment fragment = MainScheduleFragment.newInstance();
Bundle args = getRecentArguments();
RacingProcedureType procedureType = getRaceState().getTypedRacingProcedure().getType();
getRaceState().setRacingProcedure(MillisecondsTimePoint.now(), procedureType);
getRaceState().setRacingProcedure(now, procedureType);
if (getArguments() != null && startTime != null) {
if (getArguments().getInt(START_MODE, 0) != 0) {
getRaceState().forceNewStartTime(MillisecondsTimePoint.now(), startTime);
if (getArguments().getInt(START_MODE, MODE_SETUP) != MODE_SETUP) {
if (startTimeDiff == null && raceId == null) {
// absolute start time
getRaceState().forceNewStartTime(now, startTime);
} else {
// relative start time
Util.Triple<String, String, String> triple = FleetIdentifierImpl.unescape(raceId);
SimpleRaceLogIdentifier identifier = new SimpleRaceLogIdentifierImpl(triple.getA(), triple.getB(), triple.getC());
getRaceState().forceNewDependentStartTime(now, startTimeDiff, identifier);
}
fragment = RaceFlagViewerFragment.newInstance();
viewId = R.id.race_frame;
}
args.putAll(getArguments());
args.putSerializable(MainScheduleFragment.START_TIME, startTime);
args.putSerializable(MainScheduleFragment.START_TIME_DIFF, startTimeDiff);
args.putSerializable(MainScheduleFragment.DEPENDENT_RACE, raceId);
}
fragment.setArguments(args);
getFragmentManager().beginTransaction().replace(viewId, fragment).commit();
@@ -1,33 +1,27 @@
package com.sap.sailing.racecommittee.app.ui.fragments.raceinfo;
import android.annotation.TargetApi;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.os.Handler;
import android.support.v4.content.LocalBroadcastManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.*;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.UiSettings;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
@@ -41,71 +35,61 @@ import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.services.polling.RacePositionsPoller;
import com.sap.sailing.racecommittee.app.ui.fragments.maps.WindMap;
import com.sap.sailing.racecommittee.app.ui.utils.OnRaceUpdatedListener;
import com.sap.sailing.racecommittee.app.ui.views.CompassView;
import com.sap.sailing.racecommittee.app.ui.views.CompassView.CompassDirectionListener;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
import com.sap.sailing.racecommittee.app.utils.TimeUtils;
import com.sap.sailing.racecommittee.app.utils.WindHelper;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
public class WindFragment extends BaseFragment
implements CompassDirectionListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener,
OnClickListener, OnMarkerDragListener, OnMapClickListener, OnRaceUpdatedListener, TextView.OnEditorActionListener, OnMapReadyCallback,
WindMap.OnResumeCallback {
implements CompassDirectionListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener,
OnRaceUpdatedListener {
private final static String TAG = WindFragment.class.getName();
private final static String START_MODE = "startMode";
private final static int FIVE_SEC = 5000;
private final static int EVERY_POSITION_CHANGE = 0;
private final static long ONE_SEC = 1000;
private final static long FIVE_SEC = 5000;
private final static long EVERY_POSITION_CHANGE = 0;
private final static int MIN_KTS = 3;
private final static int MAX_KTS = 30;
private final static float MAX_LOCATION_DRIFT_IN_METER = 25f; // 25 meter
private final static long MAX_LOCATION_DRIFT_IN_MILLIS = 8 * 60 * 60 * 1000; // 8 hours
private Context mContext;
private View mHeaderLayout;
private View mContentLayout;
private View mMapLayout;
private View mHeader;
private View mLayoutDirection;
private View mLayoutSpeed;
private View mLayoutAddress;
private View mPositionHeader;
private View mDarkLayer;
private View mWindOn;
private View mWindOff;
private View mAddressSearch;
private View mSetData;
private View mSetPosition;
private CompassView mCompassView;
private NumberPicker mWindSpeed;
private Button mEnterPosition;
private EditText mAddressInput;
private TextView mWindSensor;
private WindMap mWindMap;
private GoogleMap mGoogleMap;
private TextView mHeaderText;
private TextView mHeaderWindSensor;
// private View mWindOn;
// private View mWindOff;
private Button mContentSetData;
private CompassView mContentCompassView;
private NumberPicker mContentWindSpeed;
private TextView mContentLatitude;
private TextView mContentLongitude;
private TextView mContentAccuracy;
private Button mContentMapShow;
private WebView mMapWebView;
private Button mMapHide;
private GoogleApiClient apiClient;
private LocationRequest locationRequest;
private Location mCurrentLocation;
private Handler mRefreshUIHandler;
private Runnable mRefreshUIRunnable;
private RacePositionsPoller positionPoller;
// stuff to save during rotation/ ect
private boolean bigMap = false;
public WindFragment() {
}
public static WindFragment newInstance(int startMode) {
WindFragment fragment = new WindFragment();
Bundle args = new Bundle();
@@ -114,20 +98,13 @@ public class WindFragment extends BaseFragment
return fragment;
}
protected static float round(float number, int precision) {
BigDecimal decimal = new BigDecimal(number);
BigDecimal round = decimal.setScale(precision, BigDecimal.ROUND_UP);
return round.floatValue();
}
/**
* creates the string array that represents the numbers in the windspeed numberpicker
* creates the string array that represents the numbers in the wind speed number picker
*
* @return
* @return numbers for wind speed number picker
*/
private String[] generateNumbers() {
ArrayList<String> numbers = new ArrayList<>();
for (float i = MIN_KTS; i <= MAX_KTS; i += .5f) {
numbers.add(String.format("%.1f %s", i, getString(R.string.wind_kn)));
}
@@ -138,126 +115,196 @@ public class WindFragment extends BaseFragment
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
apiClient = new GoogleApiClient.Builder(getActivity()).addApi(LocationServices.API).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
// initialize the googleApiClient for location requests
apiClient = new GoogleApiClient.Builder(getActivity()).addApi(LocationServices.API).build();
// create runnable for ui refreshing
mRefreshUIRunnable = createUIRefreshRunnable();
//register receiver to be notified if race is tracked
IntentFilter filter = new IntentFilter(AppConstants.INTENT_ACTION_IS_TRACKING);
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(new IsTrackedReceiver(), filter);
}
private Runnable createUIRefreshRunnable() {
return new Runnable() {
@Override
public void run() {
refreshUI();
mRefreshUIHandler.postDelayed(this, ONE_SEC);
}
};
}
/**
* refresh location data labels and highlight missing or inaccuracy gps data
* disable mContentSetData button if gps data is missing or inaccurate
*/
private void refreshUI() {
if (mCurrentLocation != null) {
double latitude = mCurrentLocation.getLatitude();
double longitude = mCurrentLocation.getLongitude();
float accuracy = mCurrentLocation.getAccuracy();
long timeDifference = System.currentTimeMillis() - mCurrentLocation.getTime();
mContentLatitude.setText(String.format("%s: %.5f", getString(R.string.wind_latitude), latitude));
mContentLatitude.setTextColor(Color.BLACK);
mContentLongitude.setText(String.format("%s: %.5f", getString(R.string.wind_longitude), longitude));
mContentLongitude.setTextColor(Color.BLACK);
String timeFormatted = TimeUtils.formatTimeAgo(timeDifference) + " " + getString(R.string.wind_accuracy_ago);
mContentAccuracy.setText(String.format("%s: ~%.0fm\n(%s)", getString(R.string.wind_accuracy), mCurrentLocation.getAccuracy(), timeFormatted));
mContentSetData.setEnabled(timeDifference <= MAX_LOCATION_DRIFT_IN_MILLIS && accuracy <= MAX_LOCATION_DRIFT_IN_METER);
// highlight accuracy problem if location is invalid
mContentAccuracy.setTextColor(mContentSetData.isEnabled() ? Color.BLACK : Color.RED);
} else {
mContentSetData.setEnabled(false);
// highlight location problem if no location is available
mContentLatitude.setTextColor(Color.RED);
mContentLongitude.setTextColor(Color.RED);
mContentAccuracy.setTextColor(Color.RED);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(R.layout.wind_view, container, false);
mContext = inflater.getContext();
// the fragment is divided into three layouts
// header layout is optional and is used for back navigation
// content layout holds wind direction, wind speed and location controls
// map layout holds a lazy web view for map presentation
mHeaderLayout = layout.findViewById(R.id.header_layout);
mContentLayout = layout.findViewById(R.id.content_layout);
mMapLayout = layout.findViewById(R.id.map_layout);
mHeader = layout.findViewById(R.id.header_text);
mDarkLayer = layout.findViewById(R.id.dark_layer);
mLayoutDirection = layout.findViewById(R.id.layout_direction);
mLayoutSpeed = layout.findViewById(R.id.layout_speed);
mLayoutAddress = layout.findViewById(R.id.address);
mPositionHeader = layout.findViewById(R.id.position_header);
mHeaderText = (TextView) layout.findViewById(R.id.header_text);
mHeaderWindSensor = (TextView) layout.findViewById(R.id.wind_sensor);
// disabled, because of bug #2871
// mWindOff = layout.findViewById(R.id.wind_off);
// mWindOn = layout.findViewById(R.id.wind_on);
mAddressSearch = layout.findViewById(R.id.address_search);
mSetData = layout.findViewById(R.id.set_data);
mSetPosition = layout.findViewById(R.id.set_position);
mCompassView = (CompassView) layout.findViewById(R.id.compassView);
mWindSpeed = (NumberPicker) layout.findViewById(R.id.wind_speed);
mAddressInput = (EditText) layout.findViewById(R.id.address_input);
mWindSensor = (TextView) layout.findViewById(R.id.wind_sensor);
mEnterPosition = (Button) layout.findViewById(R.id.enter_position);
// mWindOff = layout.findViewById(R.id.wind_off);
// mWindOn = layout.findViewById(R.id.wind_on);
mContentSetData = (Button) layout.findViewById(R.id.set_data);
mContentCompassView = (CompassView) layout.findViewById(R.id.compass_view);
mContentWindSpeed = (NumberPicker) layout.findViewById(R.id.wind_speed);
mContentLatitude = (TextView) layout.findViewById(R.id.position_latitude);
mContentLongitude = (TextView) layout.findViewById(R.id.position_longitude);
mContentAccuracy = (TextView) layout.findViewById(R.id.position_accuracy);
mContentMapShow = (Button) layout.findViewById(R.id.position_show);
mMapWebView = (WebView) layout.findViewById(R.id.web_view);
mMapHide = (Button) layout.findViewById(R.id.position_hide);
return layout;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initialMapFragment();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getArguments() != null) {
switch (getArguments().getInt(START_MODE, 0)) {
case 1:
if (getView() != null) {
View header = getView().findViewById(R.id.header);
header.setVisibility(View.GONE);
}
break;
default:
break;
}
}
if (mWindSensor != null && getRace() != null && getRaceState() != null && getRaceState().getWindFix() != null) {
if (mHeaderWindSensor != null && getRace() != null && getRaceState() != null && getRaceState().getWindFix() != null) {
String sensorData = getString(R.string.wind_sensor);
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm", getResources().getConfiguration().locale);
Wind wind = getRaceState().getWindFix();
sensorData = sensorData.replace("#AT#", dateFormat.format(wind.getTimePoint().asDate()));
sensorData = sensorData.replace("#FROM#", String.format("%.0f", wind.getFrom().getDegrees()));
sensorData = sensorData.replace("#SPEED#", String.format("%.1f", wind.getKnots()));
mWindSensor.setText(sensorData);
mHeaderWindSensor.setText(sensorData);
}
setupButtons();
setupWindSpeedPicker();
showElements(true);
setupLayouts(false);
}
setInstanceState(savedInstanceState);
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mRefreshUIHandler = new Handler();
}
/**
* starts the googleApiClient to get location updates
*/
public void setupLocationClient() {
private void tearUpApiClient() {
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(FIVE_SEC);
locationRequest.setFastestInterval(EVERY_POSITION_CHANGE);
apiClient.registerConnectionCallbacks(this);
apiClient.registerConnectionFailedListener(this);
apiClient.connect();
}
private void tearDownApiClient() {
apiClient.unregisterConnectionFailedListener(this);
apiClient.unregisterConnectionCallbacks(this);
apiClient.disconnect();
}
/**
* adds the polling for buoy data to the polled races, also registers a callback
*/
private void tearUpPositionPoller() {
positionPoller = new RacePositionsPoller(getActivity());
positionPoller.register(getRace(), this);
ExLog.i(getActivity(), TAG, "registering race " + getRace().getRaceName());
}
private void tearDownPositionPoller() {
positionPoller.unregisterAllAndStop();
}
/**
* configures all the buttons in the view
*/
public void setupButtons() {
if (mHeader != null) {
mHeader.setOnClickListener(this);
if (mHeaderText != null) {
mHeaderText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openMainScheduleFragment();
}
if (mEnterPosition != null) {
mEnterPosition.setOnClickListener(this);
});
}
if (mAddressInput != null) {
mAddressInput.setOnEditorActionListener(this);
if (mContentSetData != null) {
mContentSetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onSendClick();
}
if (mAddressSearch != null) {
mAddressSearch.setOnClickListener(this);
});
}
if (mSetData != null) {
mSetData.setOnClickListener(this);
if (mContentMapShow != null) {
// disable functionality at first. will be enabled after contacting the server if race is tracked
mContentMapShow.setEnabled(false);
mContentMapShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setupLayouts(true);
}
if (mSetPosition != null) {
mSetPosition.setOnClickListener(this);
});
}
if (mMapHide != null) {
mMapHide.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setupLayouts(false);
}
});
}
}
/**
* configures the windspeedpicker views and attaches all relevant listener functions to them
* configures the wind speed picker views and attaches all relevant listener functions to them
*/
public void setupWindSpeedPicker() {
String nums[] = generateNumbers();
ThemeHelper.setPickerTextColor(getActivity(), mWindSpeed, ThemeHelper.getColor(getActivity(), R.attr.white));
mWindSpeed.setMaxValue(nums.length - 1);
mWindSpeed.setMinValue(0);
mWindSpeed.setWrapSelectorWheel(false);
mWindSpeed.setDisplayedValues(nums);
mWindSpeed.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
ThemeHelper.setPickerTextColor(getActivity(), mContentWindSpeed, ThemeHelper.getColor(getActivity(), R.attr.white));
mContentWindSpeed.setMaxValue(nums.length - 1);
mContentWindSpeed.setMinValue(0);
mContentWindSpeed.setWrapSelectorWheel(false);
mContentWindSpeed.setDisplayedValues(nums);
mContentWindSpeed.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
double enteredWindSpeed = preferences.getWindSpeed();
double enteredWindBearingFrom = preferences.getWindBearingFromDirection();
@@ -270,67 +317,66 @@ public class WindFragment extends BaseFragment
}
}
mCompassView.setDirection((float) enteredWindBearingFrom);
mWindSpeed.setValue(((int) ((enteredWindSpeed - MIN_KTS) * 2)));
mContentCompassView.setDirection((float) enteredWindBearingFrom);
mContentWindSpeed.setValue(((int) ((enteredWindSpeed - MIN_KTS) * 2)));
}
/**
* adds the polling for buoy data to the polled races, also registers a callback
*/
public void setupPositionPoller() {
positionPoller = new RacePositionsPoller(getActivity());
positionPoller.register(getRace(), this);
ExLog.i(getActivity(), TAG, "registering race " + getRace().getRaceName());
// the map view needs java script
@SuppressLint("SetJavaScriptEnabled")
private void setupLayouts(boolean showMap) {
if (mHeaderLayout != null) {
if (getArguments() != null && getArguments().getInt(START_MODE, 0) == 1) {
mHeaderLayout.setVisibility(View.GONE);
} else {
mHeaderLayout.setVisibility(showMap ? View.GONE : View.VISIBLE);
}
}
if (mContentLayout != null) {
mContentLayout.setVisibility(showMap ? View.GONE : View.VISIBLE);
}
if (mMapLayout != null) {
WebSettings settings = mMapWebView.getSettings();
if (showMap) {
settings.setJavaScriptEnabled(true);
loadRaceMap(true, false, false, true);
mMapLayout.setVisibility(View.VISIBLE);
} else {
settings.setJavaScriptEnabled(false);
mMapLayout.setVisibility(View.GONE);
}
}
}
/**
* function to restore the instanceState via savedInstanceState bundle
*
* @param savedInstanceState said bundle
*/
public void setInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
bigMap = savedInstanceState.getBoolean("bigMap", false);
double markerLat = savedInstanceState.getDouble("markerLat", -1);
if (bigMap) {
showBigMap();
if (mWindMap != null && savedInstanceState.getDouble("lat", -1) != -1) {
mWindMap.centerMap(savedInstanceState.getDouble("lat"), savedInstanceState.getDouble("lng"), savedInstanceState.getFloat("zoom"));
if (markerLat != -1) {
mWindMap.movePositionMarker(new LatLng(markerLat, savedInstanceState.getDouble("markerLng")));
}
}
}
private boolean loadRaceMap(boolean showWindCharts, boolean showStreamlets, boolean showSimulation, boolean showMapControls) {
ManagedRace race = getRace();
if (race != null) {
// build complete race map url
String mapUrl = WindHelper.generateMapURL(getActivity(), race, showWindCharts, showStreamlets, showSimulation, showMapControls);
mMapWebView.loadUrl(mapUrl);
return true;
}
return false;
}
@Override
public void onStart() {
super.onStart();
mCompassView.setDirectionListener(this);
mContentCompassView.setDirectionListener(this);
// start ui refreshing
if (mRefreshUIHandler != null) {
mRefreshUIHandler.postDelayed(mRefreshUIRunnable, ONE_SEC);
}
}
@Override
public void onPause() {
super.onPause();
if (apiClient.isConnected()) {
apiClient.unregisterConnectionFailedListener(this);
apiClient.unregisterConnectionCallbacks(this);
}
apiClient.disconnect();
positionPoller.unregisterAllAndStop();
Fragment fragment = getFragmentManager().findFragmentById(R.id.windMap);
if (fragment != null) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.remove(fragment);
transaction.commit();
}
// disconnect googleApiClient and unregister position poller
tearDownApiClient();
tearDownPositionPoller();
sendIntent(AppConstants.INTENT_ACTION_TIME_SHOW);
}
@@ -339,63 +385,23 @@ public class WindFragment extends BaseFragment
public void onResume() {
super.onResume();
setupPositionPoller();
setupLocationClient();
initialMapFragment();
// connect googleApiClient and register position poller
tearUpApiClient();
tearUpPositionPoller();
// Contact server and ask if race is tracked and map is allowed to show.
WindHelper.isTrackedRace(getActivity(), getRace());
sendIntent(AppConstants.INTENT_ACTION_TIME_HIDE);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
public void onStop() {
super.onStop();
outState.putBoolean("bigMap", bigMap);
if (mWindMap != null) {
if (mGoogleMap != null) {
outState.putDouble("lat", mGoogleMap.getCameraPosition().target.latitude);
outState.putDouble("lng", mGoogleMap.getCameraPosition().target.longitude);
outState.putFloat("zoom", mGoogleMap.getCameraPosition().zoom);
}
if (mWindMap.windMarker != null) {
outState.putDouble("markerLat", mWindMap.windMarker.getPosition().latitude);
outState.putDouble("markerLng", mWindMap.windMarker.getPosition().longitude);
}
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.header_text:
if (bigMap) {
showElements(true);
bigMap = false;
} else {
openMainScheduleFragment();
}
break;
case R.id.enter_position:
showBigMap();
break;
case R.id.address_search:
if (mAddressInput != null) {
new GeoCodeTask().execute("" + mAddressInput.getText());
}
break;
case R.id.set_data: {
onSendClick();
break;
}
case R.id.set_position: {
onPositionSetClick();
break;
}
default: {
break;
}
// stop ui refreshing
if (mRefreshUIHandler != null) {
mRefreshUIHandler.removeCallbacks(mRefreshUIRunnable);
}
}
@@ -417,160 +423,18 @@ public class WindFragment extends BaseFragment
}
}
/**
* saves the boat position for later sendage of the entered windData
*/
private void onPositionSetClick() {
bigMap = false;
if (mWindMap != null && mWindMap.windMarker != null && mGoogleMap != null) {
mGoogleMap.getUiSettings().setAllGesturesEnabled(false);
mCurrentLocation = new Location("manual");
mCurrentLocation.setLatitude(mWindMap.windMarker.getPosition().latitude);
mCurrentLocation.setLongitude(mWindMap.windMarker.getPosition().longitude);
preferences.setWindPosition(mWindMap.windMarker.getPosition());
mWindMap.windMarker.setDraggable(false);
mWindMap.centerMap(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
}
showElements(true);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void initialMapFragment() {
mWindMap = WindMap.newInstance(mContext, this);
FragmentManager manager;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
manager = getChildFragmentManager();
} else {
manager = getFragmentManager();
}
if (manager.findFragmentByTag("googleMap") == null) {
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.windMap, mWindMap, "googleMap").commit();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
manager.executePendingTransactions();
mWindMap.getMapAsync(this);
}
}
}
/**
* expands the windMap , hides views where the map is supposed to go
*/
private void showBigMap() {
showElements(false);
if (mWindMap != null && mGoogleMap != null) {
mGoogleMap.getUiSettings().setAllGesturesEnabled(true);
if (mWindMap.windMarker != null) {
mWindMap.windMarker.setDraggable(true);
}
}
bigMap = true;
}
private void showElements(boolean show) {
if (mSetData != null) {
mSetData.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
mSetData
.setEnabled(mWindMap != null && mWindMap.windMarker != null && mWindMap.windMarker.getPosition() != null && mCurrentLocation != null);
}
if (mSetPosition != null) {
mSetPosition.setVisibility(show ? View.INVISIBLE : View.VISIBLE);
}
if (mLayoutDirection != null) {
mLayoutDirection.setVisibility(show ? View.VISIBLE : View.GONE);
}
if (mLayoutSpeed != null) {
mLayoutSpeed.setVisibility(show ? View.VISIBLE : View.GONE);
}
if (mPositionHeader != null) {
mPositionHeader.setVisibility(show ? View.VISIBLE : View.GONE);
}
if (mDarkLayer != null) {
mDarkLayer.setVisibility(show ? View.VISIBLE : View.GONE);
}
if (mLayoutAddress != null) {
mLayoutAddress.setVisibility(show ? View.GONE : View.VISIBLE);
}
}
private void setWind(boolean on) {
if (mWindOn != null) {
mWindOn.setVisibility(on ? View.VISIBLE : View.GONE);
}
if (mWindOff != null) {
mWindOff.setVisibility(!on ? View.VISIBLE : View.GONE);
}
}
// private void setWind(boolean on) {
// if (mWindOn != null) {
// mWindOn.setVisibility(on ? View.VISIBLE : View.GONE);
// }
// if (mWindOff != null) {
// mWindOff.setVisibility(!on ? View.VISIBLE : View.GONE);
// }
// }
@Override
public void onLocationChanged(Location location) {
mCurrentLocation = location;
if (mWindMap != null) {
mWindMap.centerMap(location.getLatitude(), location.getLongitude());
if (mGoogleMap != null) {
UiSettings uiSettings = mGoogleMap.getUiSettings();
if (uiSettings != null) {
uiSettings.setAllGesturesEnabled(false);
}
}
mWindMap.addAccuracyCircle(location);
if (mWindMap.windMarker != null) {
mWindMap.windMarker.setDraggable(false);
}
}
if (mDarkLayer != null) {
mDarkLayer.setVisibility(View.GONE);
}
setWind(true);
showElements(true);
if (mDarkLayer != null) {
mDarkLayer.setVisibility(View.GONE);
}
}
/**
* function gets called after inputting the location where you want to the map to zoom to
*
* @param v the view where the location was entered
*/
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
switch (actionId) {
case EditorInfo.IME_ACTION_DONE:
new GeoCodeTask().execute("" + v.getText());
return true;
default:
return false;
}
}
@Override
public void onMarkerDragStart(Marker arg0) {
}
@Override
public void onMarkerDragEnd(Marker marker) {
mCurrentLocation = new Location("manual");
mCurrentLocation.setLatitude(marker.getPosition().latitude);
mCurrentLocation.setLongitude(marker.getPosition().longitude);
preferences.setWindPosition(marker.getPosition());
}
@Override
public void onMarkerDrag(Marker arg0) {
}
@Override
public void onMapClick(LatLng arg0) {
if (bigMap && mWindMap != null) {
mWindMap.movePositionMarker(arg0);
}
}
@Override
@@ -585,18 +449,21 @@ public class WindFragment extends BaseFragment
@Override
public void onConnectionSuspended(int i) {
LocationServices.FusedLocationApi.removeLocationUpdates(apiClient, this);
}
@Override
public void onDirectionChanged(float degree) {
}
@Override
public void OnRaceUpdated(ManagedRace race) {
}
private Wind getResultingWindFix() throws NumberFormatException {
Position currentPosition = new DegreePosition(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
double windSpeed = mWindSpeed.getValue() / 2 + MIN_KTS;
double windBearing = mCompassView.getDirection();
double windSpeed = mContentWindSpeed.getValue() / 2 + MIN_KTS;
double windBearing = mContentCompassView.getDirection();
Bearing bearing_from = new DegreeBearingImpl(windBearing);
SpeedWithBearing speedBearing = new KnotSpeedWithBearingImpl(windSpeed, bearing_from.reverse());
return new WindImpl(currentPosition, MillisecondsTimePoint.now(), speedBearing);
@@ -613,90 +480,13 @@ public class WindFragment extends BaseFragment
preferences.setWindSpeed(wind.getKnots());
}
@Override
public void OnRaceUpdated(ManagedRace race) {
if (mWindMap != null) {
mWindMap.onMapDataUpdated(race.getMapMarkers());
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onMapReady(GoogleMap map) {
mGoogleMap = map;
if (mWindMap != null && mGoogleMap != null) {
mWindMap.setMap(mGoogleMap);
mGoogleMap.setOnMapClickListener(this);
mGoogleMap.setOnMarkerDragListener(this);
UiSettings uiSettings = mGoogleMap.getUiSettings();
uiSettings.setAllGesturesEnabled(false);
uiSettings.setCompassEnabled(false);
uiSettings.setMapToolbarEnabled(false);
// mWindMap.setData(mapItems);
// center the map
LatLng enteredWindLocation = preferences.getWindPosition();
if (enteredWindLocation.latitude != 0 && enteredWindLocation.longitude != 0) {
mWindMap.movePositionMarker(enteredWindLocation);
mWindMap.centerMap(enteredWindLocation);
}
}
}
private class IsTrackedReceiver extends BroadcastReceiver {
@Override
public void afterResumed() {
mWindMap.getMapAsync(this);
public void onReceive(Context context, Intent intent) {
mContentMapShow.setEnabled(intent.getBooleanExtra(AppConstants.INTENT_ACTION_IS_TRACKING_EXTRA, false));
}
/**
* Async call to googles geocoder web api
*/
private class GeoCodeTask extends AsyncTask<String, String, JSONObject> {
String location = "";
protected JSONObject doInBackground(String... urls) {
location = urls[0];
StringBuilder responseBody = new StringBuilder();
JSONObject locJSON = null;
try {
URL website = new URL(getString(R.string.url_google_geocoder) + URLEncoder.encode(location, "UTF-8")
+ getString(R.string.urlpart_google_geocoder_sensor));
ExLog.i(getActivity(), TAG, website.toString());
HttpURLConnection urlConnection = (HttpURLConnection) website.openConnection();
BufferedReader in;
try {
in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
responseBody.append(line);
}
JSONObject jObject = new JSONObject(responseBody + "");
locJSON = jObject.getJSONArray("results").getJSONObject(0).getJSONObject("geometry").getJSONObject("location");
} finally {
urlConnection.disconnect();
}
} catch (Exception e) {
ExLog.ex(WindFragment.this.getActivity(), TAG, e);
}
return (locJSON);
}
protected void onPostExecute(JSONObject locJSON) {
if (locJSON == null) {
ExLog.i(getActivity(), TAG, "No Location found for " + location);
Toast.makeText(getActivity(), R.string.no_location_found, Toast.LENGTH_SHORT).show();
return;
}
try {
ExLog.i(getActivity(), TAG, "Location found for " + location + ": " + locJSON.getDouble("lat") + "," + locJSON.getDouble("lng"));
if (mWindMap != null) {
mWindMap.centerMap(locJSON.getDouble("lat"), locJSON.getDouble("lng"));
}
} catch (JSONException e) {
ExLog.ex(WindFragment.this.getActivity(), TAG, e);
}
}
}
}
Loaded 100 of 109 files, more files were not shown because too many files have changed in this diff. Show more