Merge branch 'master' into competitor_filtering

This commit is contained in:
Axel Uhl committed 2013-05-01 15:35:23 +02:00
commit d270e7ff81
50 files changed
+803 -464

No files matched your search

+16 -13
View File
@@ -53,6 +53,7 @@ MAVEN_SETTINGS_PROXY=$PROJECT_HOME/configuration/maven-settings-proxy.xml
p2PluginRepository=$PROJECT_HOME/java/com.sap.sailing.feature.p2build/bin/products/raceanalysis.product.id/linux/gtk/$ARCH
HAS_OVERWRITTEN_TARGET=0
TARGET_SERVER_NAME=$active_branch
gwtcompile=1
@@ -104,7 +105,8 @@ do
m) MAVEN_SETTINGS=$OPTARG;;
n) OSGI_BUNDLE_NAME=$OPTARG;;
l) OSGI_TELNET_PORT=$OPTARG;;
s) TARGET_SERVER_NAME=$OPTARG;;
s) TARGET_SERVER_NAME=$OPTARG
HAS_OVERWRITTEN_TARGET=1;;
\?) echo "Invalid option"
exit 2;;
esac
@@ -331,27 +333,28 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
rm -rf $ACDIR/org.eclipse.*
rm -rf $ACDIR/configuration/org.eclipse.*
rm -rf $ACDIR/start
rm -rf $ACDIR/stop
if [[ i$HAS_OVERWRITTEN_TARGET -eq 0 ]]; then
rm -rf $ACDIR/start
rm -rf $ACDIR/stop
cp -v $p2PluginRepository/configuration/config.ini configuration/
mkdir -p configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty.xml configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/realm.properties configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/monitoring.properties configuration/
cp -v $PROJECT_HOME/configuration/mongodb.cfg $ACDIR/
cp -v $PROJECT_HOME/java/target/start $ACDIR/
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
fi
cp -v $p2PluginRepository/configuration/config.ini configuration/
cp -r -v $p2PluginRepository/configuration/org.eclipse.equinox.simpleconfigurator configuration/
cp -v $p2PluginRepository/plugins/*.jar plugins/
mkdir -p configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty.xml configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/realm.properties configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/monitoring.properties configuration/
# Make sure mongodb configuration is active
cp -v $PROJECT_HOME/configuration/mongodb.cfg $ACDIR/
cp -rv $PROJECT_HOME/configuration/native-libraries $ACDIR/
# Make sure this script is up2date at least for the next run
cp -v $PROJECT_HOME/configuration/buildAndUpdateProduct.sh $ACDIR/
cp -v $PROJECT_HOME/java/target/start $ACDIR/
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
cp -v $PROJECT_HOME/java/target/udpmirror $ACDIR/
echo "Installation complete. You may now start the server using ./start"
@@ -1,8 +1,18 @@
package com.sap.sailing.domain.common.racelog;
public enum StartProcedureType {
UNKNOWN,
RRS26,
GateStart,
ESS
//RRS26("Fix-Line-Start (RRS26)"),
GateStart("Gate-Line-Start"),
ESS("\"Extreme Sailing Series\"-Start");
private String displayName;
private StartProcedureType(String displayName){
this.displayName = displayName;
}
@Override
public String toString(){
return displayName;
}
}
@@ -30,22 +30,38 @@ public class RaceLogEventComparatorTest {
@Test
public void testEqualsOnSame() {
when(eventOne.getTimePoint()).thenReturn(new MillisecondsTimePoint(1));
when(eventOne.getId()).thenReturn("a");
int result = comparator.compare(eventOne, eventOne);
assertEquals(0, result);
}
@Test
public void testSamePassAndSameTime() {
public void testSamePassAndSameTimeSameId() {
when(eventOne.getPassId()).thenReturn(0);
when(eventOne.getTimePoint()).thenReturn(new MillisecondsTimePoint(0));
when(eventOne.getId()).thenReturn("a");
when(eventTwo.getPassId()).thenReturn(0);
when(eventTwo.getTimePoint()).thenReturn(new MillisecondsTimePoint(0));
when(eventTwo.getId()).thenReturn("a");
int result = comparator.compare(eventOne, eventTwo);
assertEquals(0, result);
}
@Test
public void testSamePassAndSameTimeDifferentId() {
when(eventOne.getPassId()).thenReturn(0);
when(eventOne.getTimePoint()).thenReturn(new MillisecondsTimePoint(0));
when(eventOne.getId()).thenReturn("a");
when(eventTwo.getPassId()).thenReturn(0);
when(eventTwo.getTimePoint()).thenReturn(new MillisecondsTimePoint(0));
when(eventTwo.getId()).thenReturn("b");
int result = comparator.compare(eventOne, eventTwo);
assertTrue(result < 0);
}
@Test
public void testSamePassLowerHigherTime() {
when(eventOne.getPassId()).thenReturn(0);
@@ -61,8 +61,10 @@ public class RaceLogTest {
when(eventOne.getPassId()).thenReturn(0);
when(eventOne.getTimePoint()).thenReturn(new MillisecondsTimePoint(0));
when(eventOne.getId()).thenReturn("a");
when(eventTwo.getPassId()).thenReturn(1);
when(eventTwo.getTimePoint()).thenReturn(new MillisecondsTimePoint(0));
when(eventTwo.getId()).thenReturn("b");
assertTrue(raceLog.add(eventOne));
assertTrue(raceLog.add(eventTwo));
@@ -74,14 +76,37 @@ public class RaceLogTest {
}
@Test
public void testWontAddEventSamePassSameTimePoint() {
public void testAddEventSamePassSameTimePointDifferentId() {
RaceLogEvent eventOne = mock(RaceLogEvent.class);
RaceLogEvent eventTwo = mock(RaceLogEvent.class);
when(eventOne.getPassId()).thenReturn(0);
when(eventOne.getTimePoint()).thenReturn(new MillisecondsTimePoint(0));
when(eventOne.getId()).thenReturn("a");
when(eventTwo.getPassId()).thenReturn(0);
when(eventTwo.getTimePoint()).thenReturn(new MillisecondsTimePoint(0));
when(eventTwo.getId()).thenReturn("b");
assertTrue(raceLog.add(eventOne));
assertTrue(raceLog.add(eventTwo));
raceLog.lockForRead();
assertTrue(Util.contains(raceLog.getRawFixes(), eventOne));
assertTrue(Util.contains(raceLog.getRawFixes(), eventTwo));
raceLog.unlockAfterRead();
}
@Test
public void testWontAddEventSamePassSameTimePointSameId() {
RaceLogEvent eventOne = mock(RaceLogEvent.class);
RaceLogEvent eventTwo = mock(RaceLogEvent.class);
when(eventOne.getPassId()).thenReturn(0);
when(eventOne.getTimePoint()).thenReturn(new MillisecondsTimePoint(0));
when(eventOne.getId()).thenReturn("a");
when(eventTwo.getPassId()).thenReturn(0);
when(eventTwo.getTimePoint()).thenReturn(new MillisecondsTimePoint(0));
when(eventTwo.getId()).thenReturn("a");
assertTrue(raceLog.add(eventOne));
assertFalse(raceLog.add(eventTwo));
@@ -0,0 +1,36 @@
package com.sap.sailing.domain.racelog.analyzing.impl;
import com.sap.sailing.domain.common.racelog.StartProcedureType;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogStartProcedureChangedEvent;
public class StartProcedureTypeAnalyzer extends RaceLogAnalyzer {
public StartProcedureTypeAnalyzer(RaceLog raceLog) {
super(raceLog);
}
public StartProcedureType getActiveStartProcedureType() {
StartProcedureType result = null;
raceLog.lockForRead();
try {
result = searchLog();
} finally {
raceLog.unlockAfterRead();
}
return result;
}
private StartProcedureType searchLog() {
StartProcedureType result = null;
for (RaceLogEvent event : getAllEvents()) {
if (event instanceof RaceLogStartProcedureChangedEvent) {
RaceLogStartProcedureChangedEvent startProcedureEvent = (RaceLogStartProcedureChangedEvent) event;
result = startProcedureEvent.getStartProcedureType();
}
}
return result;
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.racelog.impl;
import java.util.NavigableSet;
import java.util.logging.Logger;
import com.sap.sailing.domain.racelog.PassAwareRaceLog;
import com.sap.sailing.domain.racelog.RaceLog;
@@ -8,27 +9,27 @@ import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.tracking.impl.PartialNavigableSetView;
public class PassAwareRaceLogImpl extends RaceLogImpl implements PassAwareRaceLog {
// private static final String TAG = PassAwareRaceLogImpl.class.getName();
private static final Logger logger = Logger.getLogger(PassAwareRaceLogImpl.class.getName());
private static final long serialVersionUID = -1252381252528365834L;
private static final String ReadWriteLockName = PassAwareRaceLogImpl.class.getName() + ".lock";
public static final int DefaultPassId = 0;
private int currentPassId;
public PassAwareRaceLogImpl(RaceLog raceLog) {
this();
public static PassAwareRaceLog copy(RaceLog raceLog) {
PassAwareRaceLog newRaceLog = new PassAwareRaceLogImpl();
raceLog.lockForRead();
try {
for (RaceLogEvent event : raceLog.getRawFixes()) {
this.add(event);
newRaceLog.add(event);
}
} finally {
raceLog.unlockAfterRead();
}
return newRaceLog;
}
private int currentPassId;
public PassAwareRaceLogImpl() {
this(DefaultPassId);
}
@@ -44,7 +45,7 @@ public class PassAwareRaceLogImpl extends RaceLogImpl implements PassAwareRaceLo
public void setCurrentPassId(int newPassId) {
if (newPassId != this.currentPassId) {
// ExLog.i(TAG, String.format("Changing pass id to %d", newPassId));
logger.info(String.format("Changing pass id to %d", newPassId));
this.currentPassId = newPassId;
}
}
@@ -79,11 +80,11 @@ public class PassAwareRaceLogImpl extends RaceLogImpl implements PassAwareRaceLo
unlockAfterWrite();
}
if (isAdded) {
// ExLog.i(TAG, String.format("%s (%s) was added to log.", event, event.getClass().getName()));
logger.finer(String.format("%s (%s) was added to log.", event, event.getClass().getName()));
setCurrentPassId(Math.max(event.getPassId(), this.currentPassId));
notifyListenersAboutReceive(event);
} else {
// ExLog.w(TAG, String.format("%s (%s) was not added to log. Ignoring", event, event.getClass().getName()));
logger.warning(String.format("%s (%s) was not added to log. Ignoring", event, event.getClass().getName()));
}
return isAdded;
}
@@ -28,11 +28,20 @@ public enum RaceLogEventComparator implements Comparator<Timed>, Serializable {
private int compareEvents(RaceLogEvent e1, RaceLogEvent e2) {
int result = comparePasses(e1, e2);
return result == 0 ? timedComparator.compare(e1, e2) : result;
return result == 0 ? compareTimes(e1, e2) : result;
}
private int comparePasses(RaceLogEvent e1, RaceLogEvent e2) {
return e1.getPassId() - e2.getPassId();
}
private int compareTimes(RaceLogEvent e1, RaceLogEvent e2) {
int result = timedComparator.compare(e1, e2);
return result == 0 ? compareIds(e1, e2) : result;
}
private int compareIds(RaceLogEvent e1, RaceLogEvent e2) {
return e1.getId().toString().compareTo(e2.getId().toString());
}
}
@@ -15,12 +15,10 @@ import com.sap.sailing.domain.tracking.impl.TrackImpl;
import com.sap.sailing.util.impl.ArrayListNavigableSet;
public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
private static final long serialVersionUID = -176745401321893502L;
private transient Set<RaceLogEventVisitor> listeners;
private final static Logger logger = Logger.getLogger(RaceLogImpl.class.getName());
private transient Set<RaceLogEventVisitor> listeners;
public RaceLogImpl(String nameForReadWriteLock) {
super(new ArrayListNavigableSet<Timed>(RaceLogEventComparator.INSTANCE), nameForReadWriteLock);
@@ -45,7 +43,10 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
unlockAfterWrite();
}
if (isAdded) {
logger.finer(String.format("%s (%s) was added to log.", event, event.getClass().getName()));
notifyListenersAboutReceive(event);
} else {
logger.warning(String.format("%s (%s) was not added to log. Ignoring", event, event.getClass().getName()));
}
return isAdded;
}
@@ -29,7 +29,7 @@ public class PassawareRaceLogTest {
RaceLogFlagEvent rcEvent1 = RaceLogEventFactory.INSTANCE.createFlagEvent(t1, UUID.randomUUID(), new ArrayList<Competitor>(), passId, Flags.CLASS, Flags.NONE, false);
RaceLogFlagEvent rcEvent2 = RaceLogEventFactory.INSTANCE.createFlagEvent(t1, UUID.randomUUID(), new ArrayList<Competitor>(), ++passId, Flags.CLASS, Flags.NONE, false);
RaceLog passAwareRaceLog = new PassAwareRaceLogImpl(raceLog);
RaceLog passAwareRaceLog = PassAwareRaceLogImpl.copy(raceLog);
passAwareRaceLog.add(rcEvent1);
passAwareRaceLog.add(rcEvent2);
@@ -125,6 +125,7 @@ import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
import com.sap.sailing.domain.persistence.MongoWindStoreFactory;
import com.sap.sailing.domain.polarsheets.BoatAndWindSpeed;
import com.sap.sailing.domain.polarsheets.PolarSheetGenerationWorker;
import com.sap.sailing.domain.racelog.PassAwareRaceLog;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogFlagEvent;
import com.sap.sailing.domain.racelog.analyzing.impl.GateLineOpeningTimeFinder;
@@ -1159,7 +1160,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
RaceLog raceLog = raceColumn.getRaceLog(fleet);
if (raceLog != null) {
PassAwareRaceLogImpl passAwareRaceLog = new PassAwareRaceLogImpl(raceLog);
PassAwareRaceLog passAwareRaceLog = PassAwareRaceLogImpl.copy(raceLog);
StartTimeFinder startTimeFinder = new StartTimeFinder(passAwareRaceLog);
if(startTimeFinder.getStartTime()!=null){
raceInfoDTO.startTime = startTimeFinder.getStartTime().asDate();
@@ -122,7 +122,7 @@ public class TestStoringAndRetrievingRaceLogInRegatta extends RaceLogMongoDBTest
@Test
public void testStoreAndRetrieveSimpleLeaderboardWithRaceLogStartProcedureChangedEvent() {
RaceLogStartProcedureChangedEvent expectedEvent = RaceLogEventFactory.INSTANCE.createStartProcedureChangedEvent(now, 0, StartProcedureType.RRS26);
RaceLogStartProcedureChangedEvent expectedEvent = RaceLogEventFactory.INSTANCE.createStartProcedureChangedEvent(now, 0, StartProcedureType.ESS);
addAndStoreRaceLogEvent(regatta, raceColumnName, expectedEvent);
@@ -25,7 +25,7 @@ public class RaceLogStartProcedureChangedEventSerializer extends BaseRaceLogEven
public JSONObject serialize(RaceLogEvent object) {
RaceLogStartProcedureChangedEvent event = (RaceLogStartProcedureChangedEvent) object;
JSONObject result = super.serialize(event);
result.put(FIELD_START_PROCEDURE_TYPE, event.getStartProcedureType().toString());
result.put(FIELD_START_PROCEDURE_TYPE, event.getStartProcedureType().name());
return result;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+1 -1
View File
@@ -16,7 +16,7 @@
</div>
<div id='contentWrapper'>
<div id='keyVisual'>
<img alt='Sailer' src='images/key-visual.png' />
<img alt='Sailer' src='images/key-visual.jpg' />
<div id='infoBox'>
<h1 id='pageHeadline'>SAP Sailing Analytics</h1>
<p>
@@ -217,7 +217,7 @@ public class RegattaDataPerLegAction extends HttpAction {
int posGL = 0;
if (previousLeg != null) {
posGL = rankAtWaypoint.get(competitor).get(trackedLeg.getLeg().getTo()) -
- rankAtWaypoint.get(competitor).get(trackedLeg.getLeg().getFrom());
rankAtWaypoint.get(competitor).get(trackedLeg.getLeg().getFrom());
}
final Element competitor_node = addNamedElement(competitor_data_node, "competitor");
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -2,129 +2,135 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:gravity="center_horizontal"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="left"
android:scaleY="1"
android:text="@string/please_pick_a_time"
android:textAppearance="@style/TextAppearance.Welter.AppDescriptionText" />
<ImageView
android:layout_width="500dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingBottom="2dp"
android:scaleType="fitXY"
android:contentDescription="@string/empty"
android:src="@android:drawable/divider_horizontal_textfield" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="30dp" >
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="200dp"
android:layout_gravity="center_vertical|center_horizontal" >
<LinearLayout
android:layout_width="400dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:scaleY="1.55" >
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="2dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:scaleType="fitXY"
android:contentDescription="@string/empty"
android:src="@android:drawable/divider_horizontal_textfield" />
<TimePicker
android:id="@+id/timePicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:scaleX="1.55" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="30dp" >
</LinearLayout>
<TextView
android:id="@+id/time_below_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="left"
android:scaleY="1"
android:text="@string/time_until_start"
android:textAppearance="@style/TextAppearance.Welter.AppDescriptionText" />
<ImageView
android:layout_width="500dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingBottom="2dp"
android:scaleType="fitXY"
android:contentDescription="@string/empty"
android:src="@android:drawable/divider_horizontal_textfield" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="30dp" >
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:layout_marginBottom="18dp">
<TextView
android:id="@+id/race_reset_time_txt_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Welter.ExtraBigTitleLabel"
android:text="Schedule race" />
<TextView
android:id="@+id/race_reset_time_text_subheader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Welter.BigTitleLabel"
android:text="Select start mode and start time" />
<ImageView
android:layout_width="500dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingBottom="2dp"
android:scaleType="fitXY"
android:contentDescription="@string/app_name"
android:src="@android:drawable/divider_horizontal_textfield" />
</LinearLayout>
␍
<RelativeLayout
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="200dp"
android:orientation="horizontal">
<Button
android:id="@+id/btnRescheduleTime"
android:layout_width="400dp"
android:layout_height="200dp"
android:layout_gravity="center_vertical|center_horizontal"
android:text="@string/reset_time"
android:layout_centerHorizontal="true" />
<LinearLayout
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:layout_marginBottom="18dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_margin="18dp">
<Spinner
android:id="@+id/race_reset_time_spinner_procedure"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal" />
</LinearLayout>
<LinearLayout
android:layout_width="400dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_margin="18dp">
<Button
android:id="@+id/race_reset_time_btn_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Today" />
<TimePicker
android:id="@+id/race_reset_time_picker_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="75dp"
android:scaleX="1.55"
android:scaleY="1.55" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_marginRight="30dp">
<ImageButton
android:id="@+id/resetTimeAPButton"
android:layout_width="200dp"
android:layout_height="110dp"
android:scaleType="fitCenter"
android:contentDescription="@string/flag_ap"
android:src="@drawable/ap_flag" />
<TextView
android:id="@+id/abortingFlagButtonLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_marginTop="2dp"
android:text="@string/postpone_race"
android:layout_gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.Welter.AppDescriptionText" />
</LinearLayout>
android:paddingLeft="18dp" >
<Button
android:id="@+id/race_reset_time_btn_time"
android:layout_width="180dp"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:layout_marginTop="18dp"
android:drawableLeft="@drawable/ic_menu_recent_history"
android:text="Start" />
<Button
android:id="@+id/race_reset_time_btn_postpone"
android:layout_width="180dp"
android:layout_height="0dp"
android:layout_weight="0.2"
android:drawableLeft="@drawable/ic_menu_block"
android:text="Postpone" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<TextView
android:id="@+id/race_reset_time_text_infotext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Welter.LargeFontLabel"
android:text="calculating..." />
</LinearLayout>
</LinearLayout>
@@ -4,9 +4,9 @@
<PreferenceCategory android:title="@string/settings_category_general" >
<EditTextPreference
android:key="courseAreasPref"
android:name="@string/settings_general_manageareas_header"
android:defaultValue="@string/settings_general_manageareas_default"
android:key="courseAreasPref"
android:summary="@string/settings_general_manageareas_description"
android:title="@string/settings_general_manageareas_header" />
</PreferenceCategory>
@@ -19,6 +19,19 @@
android:summary="Check the box if the Golf flag should be automatically removed in a gate start race"
android:title="Remove Golf Automatically" />
-->
<CheckBoxPreference
android:key="overrideDefaultStartProcedureType"
android:defaultValue="false"
android:summary="Check the box if races should use the start procedure type defined below as a default"
android:title="Override default start procedure type" />
<ListPreference
android:key="defaultStartProcedureType"
android:title="Default start procedure type"
android:summary="Races will use selected start procedure type as a default. For this option to be active override checkbox has to be enabled."
/>
<EditTextPreference
android:defaultValue="30"
android:inputType="numberDecimal"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="settings_advanced_service_default">http://localhost:8888</string>
<string name="settings_advanced_service_default">http://192.168.56.1:8888</string>
<string name="intentActionRegisterRace">com.sap.sailing.racecommittee.app.action.registerRace</string>
<!-- <string name="intentActionSaveIntent">com.sap.sailing.racecommittee.app.action.saveIntent</string> -->
@@ -18,8 +18,8 @@
<string name="service_info">SAP Race Committee Service</string>
<string name="service_title">Monitoring all races on selected event course.</string>
<string name="app_name">SAP Race Committee Cockpit</string>
<string name="set_time">Set start time</string>
<string name="reset_time">Reset start time</string>
<string name="set_time">Start</string>
<string name="reset_time">Restart</string>
<string name="choose">Choose</string>
<string name="current_time">Current time</string>
<string name="race_finished_template">%s is finished</string>
@@ -17,7 +17,7 @@ public class AppConstants {
public final static String RACE_ID_KEY = "raceUuid";
public final static String SERVICE_UNIQUE_ID = "serviceUID";
public final static String RACING_EVENT_TIME = "racingEventTime";
public final static String EXTRAS_JSON_KEY = "json";
public final static String EXTRAS_SERIALIZED_EVENT = "json";
public final static String EXTRAS_URL = "url";
public final static String FLAG_KEY = "raceFlag";
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.domain.base.racegroup.impl.RaceGroupImpl;
import com.sap.sailing.domain.base.racegroup.impl.SeriesWithRowsImpl;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.common.racelog.StartProcedureType;
import com.sap.sailing.domain.racelog.PassAwareRaceLog;
import com.sap.sailing.domain.racelog.RaceLogEventFactory;
import com.sap.sailing.domain.racelog.impl.PassAwareRaceLogImpl;
@@ -88,7 +89,8 @@ public class OfflineDataManager extends DataManager {
"Q1",
new FleetImpl("Default"),
qualifying,
raceGroup),
raceGroup),
StartProcedureType.ESS,
log);
log = new PassAwareRaceLogImpl();
@@ -105,6 +107,7 @@ public class OfflineDataManager extends DataManager {
new FleetImpl("Default"),
qualifying,
raceGroup),
StartProcedureType.ESS,
log);
log = new PassAwareRaceLogImpl();
@@ -118,6 +121,7 @@ public class OfflineDataManager extends DataManager {
new FleetImpl("Default"),
qualifying,
raceGroup),
StartProcedureType.ESS,
log);
/*ManagedRace m1 = new ManagedRaceImpl(
new ManagedRaceIdentifierImpl(
@@ -13,6 +13,7 @@ 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.domain.common.racelog.StartProcedureType;
import com.sap.sailing.domain.racelog.PassAwareRaceLog;
import com.sap.sailing.domain.racelog.impl.PassAwareRaceLogImpl;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
@@ -53,7 +54,7 @@ public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace
for (RaceCell cell : raceRow.getCells()) {
PassAwareRaceLog log = cell.getRaceLog() instanceof PassAwareRaceLog ?
(PassAwareRaceLog) cell.getRaceLog() :
new PassAwareRaceLogImpl(cell.getRaceLog());
PassAwareRaceLogImpl.copy(cell.getRaceLog());
ManagedRace race = createManagedRace(
raceGroup,
series,
@@ -71,6 +72,7 @@ public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace
return new ManagedRaceImpl(
new ManagedRaceIdentifierImpl(name,
new FleetIdentifierImpl(fleet, series, raceGroup)),
StartProcedureType.ESS,
raceLog);
}
@@ -41,7 +41,6 @@ public interface ManagedRace extends ManagedRaceIdentifier, Named, WithID {
*/
public CourseBase getCourseDesign();
/**
* returns the list of competitors for this race
* @return list of competitors
@@ -10,32 +10,30 @@ 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.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.common.racelog.StartProcedureType;
import com.sap.sailing.domain.racelog.PassAwareRaceLog;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier;
import com.sap.sailing.racecommittee.app.domain.startprocedure.impl.GateStartProcedure;
import com.sap.sailing.racecommittee.app.domain.state.RaceState;
import com.sap.sailing.racecommittee.app.domain.state.impl.RaceStateImpl;
public class ManagedRaceImpl implements ManagedRace {
private static final long serialVersionUID = -4936566684992524001L;
//private static final String TAG = ManagedRace.class.getName();
// private static final String TAG = ManagedRace.class.getName();
private ManagedRaceIdentifier identifier;
private ManagedRaceIdentifier identifier;
private RaceState state;
private Collection<Competitor> competitors;
private CourseBase courseOnServer;
public ManagedRaceImpl(ManagedRaceIdentifier identifier, PassAwareRaceLog raceLog) {
//TODO To be changed when the start procedure for a regatta/leaderboard can be selected in the GWT Admin Console on backend side
this(identifier, new RaceStateImpl(raceLog, new GateStartProcedure(raceLog)));
public ManagedRaceImpl(ManagedRaceIdentifier identifier, StartProcedureType defaultStartProcedureType,
PassAwareRaceLog raceLog) {
this(identifier, new RaceStateImpl(defaultStartProcedureType, raceLog));
}
public ManagedRaceImpl(
ManagedRaceIdentifier identifier,
RaceState state) {
public ManagedRaceImpl(ManagedRaceIdentifier identifier, RaceState state) {
this.identifier = identifier;
this.state = state;
this.competitors = new ArrayList<Competitor>();
@@ -86,7 +84,7 @@ public class ManagedRaceImpl implements ManagedRace {
public CourseBase getCourseDesign() {
return state.getCourseDesign();
}
@Override
public Collection<Competitor> getCompetitors() {
return competitors;
@@ -26,7 +26,7 @@ public interface StartProcedure {
void dispatchFiredEventTimePoint(TimePoint startTime, TimePoint eventTime);
void setRaceStateChangedListener(StartProcedureRaceStateChangedListener raceStateChangedListener);
void setChangeListener(StartProcedureListener listener);
void setFinishing(TimePoint eventTime);
@@ -1,21 +1,21 @@
package com.sap.sailing.racecommittee.app.domain.startprocedure;
import com.sap.sailing.domain.common.TimePoint;
public interface StartProcedureRaceStateChangedListener {
void onRaceAborted(TimePoint eventTime);
void onRaceStartphaseEntered(TimePoint eventTime);
void onRaceStarted(TimePoint eventTime);
void onRaceFinishing(TimePoint now);
void onRaceFinishing(TimePoint now, TimePoint automaticRaceEnd);
void onRaceFinished(TimePoint now);
void onIndividualRecall(TimePoint eventTime);
void onIndividualRecallRemoval();
}
package com.sap.sailing.racecommittee.app.domain.startprocedure;
import com.sap.sailing.domain.common.TimePoint;
public interface StartProcedureListener {
void onRaceAborted(TimePoint eventTime);
void onRaceStartphaseEntered(TimePoint eventTime);
void onRaceStarted(TimePoint eventTime);
void onRaceFinishing(TimePoint now);
void onRaceFinishing(TimePoint now, TimePoint automaticRaceEnd);
void onRaceFinished(TimePoint now);
void onIndividualRecall(TimePoint eventTime);
void onIndividualRecallRemoval();
}
@@ -22,7 +22,7 @@ import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.domain.startprocedure.RunningRaceEventListener;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartPhaseEventListener;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedure;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedureRaceStateChangedListener;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedureListener;
import com.sap.sailing.racecommittee.app.ui.fragments.RaceFragment;
import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.RunningRaceFragment;
import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.startphase.EssStartPhaseFragment;
@@ -41,7 +41,7 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
private List<Long> startProcedureEventIntervals;
private PassAwareRaceLog raceLog;
private StartProcedureRaceStateChangedListener raceStateChangedListener;
private StartProcedureListener raceStateChangedListener;
private EssStartPhaseEventListener startPhaseEventListener;
public ExtremeSailingSeriesStartProcedure(PassAwareRaceLog raceLog) {
@@ -90,10 +90,9 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
}
private void handleAPDown(TimePoint eventTime) {
TimePoint startPhaseTimePoint = eventTime.minus(1);
if (raceStateChangedListener != null) {
raceStateChangedListener.onRaceStartphaseEntered(startPhaseTimePoint);
raceStateChangedListener.onRaceStartphaseEntered(eventTime);
}
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
@@ -116,9 +115,8 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
}
private void handleEssTwoUpAndEssThreeDown(TimePoint eventTime) {
TimePoint essThreeDownTimePoint = eventTime.minus(1);
RaceLogEvent essThreeDownEvent = RaceLogEventFactory.INSTANCE.createFlagEvent(essThreeDownTimePoint, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent essThreeDownEvent = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.ESSTHREE, Flags.NONE, /*isDisplayed*/false);
raceLog.add(essThreeDownEvent);
@@ -132,9 +130,8 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
}
private void handleEssOneUpAndEssTwoDown(TimePoint eventTime) {
TimePoint essTwoDownTimePoint = eventTime.minus(1);
RaceLogEvent essTwoDownEvent = RaceLogEventFactory.INSTANCE.createFlagEvent(essTwoDownTimePoint, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent essTwoDownEvent = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.ESSTWO, Flags.NONE, /*isDisplayed*/false);
raceLog.add(essTwoDownEvent);
@@ -148,8 +145,7 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
}
private void handleEssOneDown(TimePoint eventTime) {
TimePoint essOneDownTimePoint = eventTime.minus(1);
RaceLogEvent essOneDownEvent = RaceLogEventFactory.INSTANCE.createFlagEvent(essOneDownTimePoint, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent essOneDownEvent = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.ESSONE, Flags.NONE, /*isDisplayed*/false);
raceLog.add(essOneDownEvent);
@@ -159,7 +155,7 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
}
@Override
public void setRaceStateChangedListener(StartProcedureRaceStateChangedListener raceStateChangedListener) {
public void setChangeListener(StartProcedureListener raceStateChangedListener) {
this.raceStateChangedListener = raceStateChangedListener;
}
@@ -169,6 +165,8 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
TimePoint startPhaseStartTime = getStartPhaseStartTime(newEnteredStartTime);
TimePoint resultTime;
if (now.after(startPhaseStartTime)) {
// We do not want RaceStatusEvents with the same timestamp to ensure a reasonable ordering
// Therefore we guarantee that SCHEDULDED happens before STARTPHASE
resultTime = startPhaseStartTime.minus(1);
} else {
resultTime = now;
@@ -179,8 +177,7 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
@Override
public void setFinishing(TimePoint eventTime) {
TimePoint blueFlagDisplayEventTime = eventTime.minus(1);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(blueFlagDisplayEventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.BLUE, Flags.NONE, /*isDisplayed*/true);
raceLog.add(event);
@@ -204,8 +201,7 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
@Override
public void setFinished(TimePoint eventTime) {
TimePoint blueFlagRemoveEventTime = eventTime.minus(1);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(blueFlagRemoveEventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.BLUE, Flags.NONE, /*isDisplayed*/false);
raceLog.add(event);
@@ -228,8 +224,7 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
}
private void handleAPUp(TimePoint eventTime, Flags lowerFlag) {
TimePoint apFlagDisplayEventTime = eventTime.minus(1);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(apFlagDisplayEventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.AP, lowerFlag, /*isDisplayed*/true);
raceLog.add(event);
@@ -252,8 +247,7 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
}
private void handleNovemberUp(TimePoint eventTime, Flags lowerFlag) {
TimePoint novemberFlagDisplayEventTime = eventTime.minus(1);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(novemberFlagDisplayEventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.NOVEMBER, lowerFlag, /*isDisplayed*/true);
raceLog.add(event);
@@ -264,8 +258,7 @@ public class ExtremeSailingSeriesStartProcedure implements StartProcedure {
@Override
public void setGeneralRecall(TimePoint eventTime) {
TimePoint recallDisplayEventTime = eventTime.minus(1);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(recallDisplayEventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.FIRSTSUBSTITUTE, Flags.NONE, /*isDisplayed*/true);
raceLog.add(event);
@@ -19,7 +19,7 @@ import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.domain.startprocedure.RunningRaceEventListener;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartPhaseEventListener;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedure;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedureRaceStateChangedListener;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedureListener;
import com.sap.sailing.racecommittee.app.ui.fragments.RaceFragment;
import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.GateStartRunningRaceFragment;
import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.startphase.GateStartPhaseFragment;
@@ -35,7 +35,7 @@ public class GateStartProcedure implements StartProcedure {
private List<Long> startProcedureEventIntervals;
private PassAwareRaceLog raceLog;
private StartProcedureRaceStateChangedListener raceStateChangedListener;
private StartProcedureListener raceStateChangedListener;
private GateStartPhaseEventListener startPhaseEventListener;
private GateStartRunningRaceEventListener runningRaceEventListener;
@@ -83,13 +83,13 @@ public class GateStartProcedure implements StartProcedure {
}
private void handleClassOverGolfUp(TimePoint eventTime) {
TimePoint startPhaseTimePoint = eventTime.minus(1);
TimePoint startPhaseTimePoint = eventTime;
if (raceStateChangedListener != null) {
raceStateChangedListener.onRaceStartphaseEntered(startPhaseTimePoint);
}
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(startPhaseTimePoint, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.CLASS, Flags.GOLF, /*isDisplayed*/true);
raceLog.add(event);
@@ -109,7 +109,7 @@ public class GateStartProcedure implements StartProcedure {
}
private void handlePapaDown(TimePoint eventTime) {
TimePoint papaDownTimepoint = eventTime.minus(1);
TimePoint papaDownTimepoint = eventTime;
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(papaDownTimepoint, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.PAPA, Flags.NONE, /*isDisplayed*/false);
@@ -121,7 +121,8 @@ public class GateStartProcedure implements StartProcedure {
}
private void handleClassOverGolfDown(TimePoint eventTime) {
TimePoint essOneDownTimePoint = eventTime.minus(1);
TimePoint essOneDownTimePoint = eventTime;
RaceLogEvent essOneDownEvent = RaceLogEventFactory.INSTANCE.createFlagEvent(essOneDownTimePoint, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.CLASS, Flags.GOLF, /*isDisplayed*/false);
raceLog.add(essOneDownEvent);
@@ -132,7 +133,7 @@ public class GateStartProcedure implements StartProcedure {
}
@Override
public void setRaceStateChangedListener(StartProcedureRaceStateChangedListener raceStateChangedListener) {
public void setChangeListener(StartProcedureListener raceStateChangedListener) {
this.raceStateChangedListener = raceStateChangedListener;
}
@@ -142,6 +143,8 @@ public class GateStartProcedure implements StartProcedure {
TimePoint startPhaseStartTime = getStartPhaseStartTime(newEnteredStartTime);
TimePoint resultTime;
if (now.after(startPhaseStartTime)) {
// We do not want RaceStatusEvents with the same timestamp to ensure a reasonable ordering
// Therefore we guarantee that SCHEDULDED happens before STARTPHASE
resultTime = startPhaseStartTime.minus(1);
} else {
resultTime = now;
@@ -152,8 +155,7 @@ public class GateStartProcedure implements StartProcedure {
@Override
public void setFinishing(TimePoint eventTime) {
TimePoint blueFlagDisplayEventTime = eventTime.minus(1);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(blueFlagDisplayEventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.BLUE, Flags.NONE, /*isDisplayed*/true);
raceLog.add(event);
@@ -169,8 +171,7 @@ public class GateStartProcedure implements StartProcedure {
@Override
public void setFinished(TimePoint eventTime) {
TimePoint blueFlagRemoveEventTime = eventTime.minus(1);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(blueFlagRemoveEventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.BLUE, Flags.NONE, /*isDisplayed*/false);
raceLog.add(event);
@@ -192,9 +193,8 @@ public class GateStartProcedure implements StartProcedure {
}
}
private void handleAPUp(TimePoint eventTime, Flags lowerFlag) {
TimePoint apFlagDisplayEventTime = eventTime.minus(1);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(apFlagDisplayEventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
private void handleAPUp(TimePoint eventTime, Flags lowerFlag) {
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.AP, lowerFlag, /*isDisplayed*/true);
raceLog.add(event);
@@ -217,8 +217,7 @@ public class GateStartProcedure implements StartProcedure {
}
private void handleNovemberUp(TimePoint eventTime, Flags lowerFlag) {
TimePoint novemberFlagDisplayEventTime = eventTime.minus(1);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(novemberFlagDisplayEventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.NOVEMBER, lowerFlag, /*isDisplayed*/true);
raceLog.add(event);
@@ -229,8 +228,7 @@ public class GateStartProcedure implements StartProcedure {
@Override
public void setGeneralRecall(TimePoint eventTime) {
TimePoint recallDisplayEventTime = eventTime.minus(1);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(recallDisplayEventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createFlagEvent(eventTime, UUID.randomUUID(), Collections.<Competitor>emptyList(),
raceLog.getCurrentPassId(), Flags.FIRSTSUBSTITUTE, Flags.NONE, /*isDisplayed*/true);
raceLog.add(event);
@@ -0,0 +1,19 @@
package com.sap.sailing.racecommittee.app.domain.startprocedure.impl;
import com.sap.sailing.domain.common.racelog.StartProcedureType;
import com.sap.sailing.domain.racelog.PassAwareRaceLog;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedure;
public class StartProcedureFactory {
public static StartProcedure create(StartProcedureType type, PassAwareRaceLog raceLog) {
switch (type) {
case ESS:
return new ExtremeSailingSeriesStartProcedure(raceLog);
case GateStart:
return new GateStartProcedure(raceLog);
default:
throw new UnsupportedOperationException("Not yet implemented start procedure.");
}
}
}
@@ -8,11 +8,12 @@ import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.common.racelog.StartProcedureType;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedure;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedureRaceStateChangedListener;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedureListener;
public interface RaceState extends StartProcedureRaceStateChangedListener {
public interface RaceState extends StartProcedureListener {
/**
* Register a new listener on state changes.
@@ -34,6 +35,16 @@ public interface RaceState extends StartProcedureRaceStateChangedListener {
* @return the log of the race.
*/
RaceLog getRaceLog();
/**
* @return the active or default start procedure type.
*/
StartProcedureType getStartProcedureType();
/**
* @return the start procedure of the race
*/
StartProcedure getStartProcedure();
/**
* Gets the current race's start time.
@@ -76,7 +87,7 @@ public interface RaceState extends StartProcedureRaceStateChangedListener {
* @param newStartTime
* to be set.
*/
void setStartTime(TimePoint newStartTime);
void setStartTime(TimePoint newStartTime, StartProcedureType startProcedureType);
/**
* Sets the current race's course design
@@ -109,11 +120,6 @@ public interface RaceState extends StartProcedureRaceStateChangedListener {
* @return the status of the race.
*/
RaceLogRaceStatus getStatus();
/**
* @return the start procedure of the race
*/
StartProcedure getStartProcedure();
/**
* @return the timepoint at which the individual recall was displayed or null when the individual recall is already removed
@@ -14,6 +14,7 @@ import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.common.racelog.StartProcedureType;
import com.sap.sailing.domain.racelog.PassAwareRaceLog;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogEvent;
@@ -26,17 +27,21 @@ import com.sap.sailing.domain.racelog.analyzing.impl.IndividualRecallFinder;
import com.sap.sailing.domain.racelog.analyzing.impl.LastPublishedCourseDesignFinder;
import com.sap.sailing.domain.racelog.analyzing.impl.PathfinderFinder;
import com.sap.sailing.domain.racelog.analyzing.impl.RaceStatusAnalyzer;
import com.sap.sailing.domain.racelog.analyzing.impl.StartProcedureTypeAnalyzer;
import com.sap.sailing.domain.racelog.analyzing.impl.StartTimeFinder;
import com.sap.sailing.racecommittee.app.domain.racelog.RaceLogChangedListener;
import com.sap.sailing.racecommittee.app.domain.racelog.impl.RaceLogChangedVisitor;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedure;
import com.sap.sailing.racecommittee.app.domain.startprocedure.impl.GateStartProcedure;
import com.sap.sailing.racecommittee.app.domain.startprocedure.impl.StartProcedureFactory;
import com.sap.sailing.racecommittee.app.domain.state.RaceState;
import com.sap.sailing.racecommittee.app.domain.state.RaceStateChangedListener;
import com.sap.sailing.racecommittee.app.logging.ExLog;
public class RaceStateImpl implements RaceState, RaceLogChangedListener {
// private static final String TAG = RaceStateImpl.class.getName();
private static final String TAG = RaceStateImpl.class.getSimpleName();
protected StartProcedureType defaultStartProcedureType;
protected PassAwareRaceLog raceLog;
protected StartProcedure startProcedure;
@@ -44,6 +49,7 @@ public class RaceStateImpl implements RaceState, RaceLogChangedListener {
protected Set<RaceStateChangedListener> stateChangedListeners;
private RaceLogChangedVisitor raceLogListener;
private RaceStatusAnalyzer statusAnalyzer;
private StartTimeFinder startTimeFinder;
private FinishedTimeFinder finishedTimeFinder;
@@ -53,11 +59,11 @@ public class RaceStateImpl implements RaceState, RaceLogChangedListener {
private IndividualRecallFinder individualRecallFinder;
private PathfinderFinder pathfinderFinder;
private GateLineOpeningTimeFinder gateLineOpeningTimeFinder;
private StartProcedureTypeAnalyzer startProcedureTypeAnalyzer;
public RaceStateImpl(PassAwareRaceLog raceLog, StartProcedure procedure) {
public RaceStateImpl(StartProcedureType defaultStartProcedureType, PassAwareRaceLog raceLog) {
this.defaultStartProcedureType = defaultStartProcedureType;
this.raceLog = raceLog;
this.startProcedure = procedure;
this.startProcedure.setRaceStateChangedListener(this);
this.status = RaceLogRaceStatus.UNKNOWN;
this.stateChangedListeners = new HashSet<RaceStateChangedListener>();
@@ -74,13 +80,18 @@ public class RaceStateImpl implements RaceState, RaceLogChangedListener {
this.individualRecallFinder = new IndividualRecallFinder(raceLog);
this.pathfinderFinder = new PathfinderFinder(raceLog);
this.gateLineOpeningTimeFinder = new GateLineOpeningTimeFinder(raceLog);
this.startProcedureTypeAnalyzer = new StartProcedureTypeAnalyzer(raceLog);
registerStartProcedure();
updateStatus();
}
@Override
public RaceLog getRaceLog() {
return raceLog;
}
@Override
public RaceLogRaceStatus getStatus() {
return status;
}
@@ -90,6 +101,21 @@ public class RaceStateImpl implements RaceState, RaceLogChangedListener {
return startProcedure;
}
@Override
public StartProcedureType getStartProcedureType() {
StartProcedureType type = startProcedureTypeAnalyzer.getActiveStartProcedureType();
return type == null ? defaultStartProcedureType : type;
}
private void registerStartProcedure() {
if (startProcedure != null) {
startProcedure.setChangeListener(null);
}
StartProcedureType type = getStartProcedureType();
startProcedure = StartProcedureFactory.create(type, raceLog);
startProcedure.setChangeListener(this);
}
private void setStatus(RaceLogRaceStatus newStatus) {
RaceLogRaceStatus oldStatus = this.status;
this.status = newStatus;
@@ -128,15 +154,16 @@ public class RaceStateImpl implements RaceState, RaceLogChangedListener {
return startTimeFinder.getStartTime();
}
public void setStartTime(TimePoint newStartTime) {
TimePoint eventTime = startProcedure.getLogicalStartTimeEventTime(newStartTime);
public void setStartTime(TimePoint newStartTime, StartProcedureType type) {
RaceLogRaceStatus status = getStatus();
if (status != RaceLogRaceStatus.UNSCHEDULED) {
//substract minus 2, because a PassChangeEvent (minus 2) AND a RaceStatusEvent (minus 1) will be written to log
onRaceAborted(eventTime.minus(2));
onRaceAborted(MillisecondsTimePoint.now());
}
switchStartProcedure(type);
TimePoint eventTime = startProcedure.getLogicalStartTimeEventTime(newStartTime);
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createStartTimeEvent(eventTime, UUID.randomUUID(),
Collections.<Competitor> emptyList(), raceLog.getCurrentPassId(), newStartTime);
this.raceLog.add(event);
@@ -144,6 +171,18 @@ public class RaceStateImpl implements RaceState, RaceLogChangedListener {
notifyListenersAboutStartTimeChange(newStartTime);
}
private void switchStartProcedure(StartProcedureType type) {
if (!type.equals(startProcedureTypeAnalyzer.getActiveStartProcedureType())) {
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createStartProcedureChangedEvent(MillisecondsTimePoint.now(), raceLog.getCurrentPassId(), type);
this.raceLog.add(event);
registerStartProcedure();
ExLog.i(TAG, String.format("Switch start procedure to %s", this.startProcedure.getClass().getSimpleName()));
}
}
public TimePoint getFinishedTime() {
return finishedTimeFinder.getFinishedTime();
}
@@ -152,8 +191,7 @@ public class RaceStateImpl implements RaceState, RaceLogChangedListener {
public CourseBase getCourseDesign() {
return lastCourseDesignFinder.getLastCourseDesign();
}
public void setCourseDesign(CourseBase newCourseData) {
TimePoint eventTime = MillisecondsTimePoint.now();
@@ -164,6 +202,9 @@ public class RaceStateImpl implements RaceState, RaceLogChangedListener {
@Override
public void onRaceAborted(TimePoint eventTime) {
/*RaceLogEvent abortEvent = RaceLogEventFactory.INSTANCE.createRaceStatusEvent(eventTime, raceLog.getCurrentPassId(), RaceLogRaceStatus.UNSCHEDULED);
this.raceLog.add(abortEvent);*/
RaceLogEvent passChangeEvent = RaceLogEventFactory.INSTANCE.createPassChangeEvent(eventTime, raceLog.getCurrentPassId() + 1);
this.raceLog.add(passChangeEvent);
@@ -23,101 +23,95 @@ public class EventPersistenceManager {
private final static String delayedEventFileName = "delayedEvents.txt";
private Context context;
private String fileContent;
private List<String> persistedEvents;
public EventPersistenceManager(Context context) {
this.context = context;
fileContent = "";
initializeFile();
persistedEvents = new ArrayList<String>();
initializeFileAndPersistedEvents();
}
public boolean areIntentsDelayed() {
return !fileContent.isEmpty();
return !persistedEvents.isEmpty();
}
public void persistIntent(Intent intent) {
Bundle extras = intent.getExtras();
Serializable serializedEvent = extras.getSerializable(AppConstants.EXTRAS_JSON_KEY);
String raceUuid = extras.getSerializable(AppConstants.RACE_ID_KEY).toString();
persistEvent(raceUuid, serializedEvent);
String raceId = extras.getString(AppConstants.RACE_ID_KEY);
String url = extras.getString(AppConstants.EXTRAS_URL);
Serializable serializedEvent = extras.getSerializable(AppConstants.EXTRAS_SERIALIZED_EVENT);
persistEvent(url, raceId, serializedEvent);
}
private void persistEvent(String raceUuid, Serializable serializedEvent) {
String eventLine = String.format("%s;%s\n", raceUuid, serializedEvent);
ExLog.i(TAG, "Called to persist following event " + eventLine);
private void persistEvent(String url, String raceId, Serializable serializedEvent) {
String eventLine = getSerializedIntentForPersistence(url, raceId, serializedEvent);
ExLog.i(TAG, String.format("Persisting event \"%s\" for race %s.", eventLine, raceId));
if (!fileContent.isEmpty()) {
if (fileContent.contains(eventLine)) {
ExLog.i(TAG, "The event to persist exists already in file");
return;
}
if (persistedEvents.contains(eventLine)) {
ExLog.i(TAG, "The event already exists. Ignoring.");
return;
}
writeEventToFile(eventLine);
saveEvent(eventLine);
}
private String getSerializedIntentForPersistence(String url, String raceId, Serializable serializedEvent) {
String eventLine = String.format("%s;%s;%s", raceId, serializedEvent, url);
return eventLine;
}
public void removeIntent(Intent intent) {
Bundle extras = intent.getExtras();
String serializedEvent = extras.getString(AppConstants.EXTRAS_JSON_KEY);
String raceUuid = extras.getString(AppConstants.RACE_ID_KEY);
String url = extras.getString(AppConstants.EXTRAS_URL);
String raceId = extras.getString(AppConstants.RACE_ID_KEY);
String serializedEvent = extras.getString(AppConstants.EXTRAS_SERIALIZED_EVENT);
removeEvent(raceUuid, serializedEvent);
removeEvent(url, raceId, serializedEvent);
}
private void removeEvent(String raceUuid, String serializedEvent) {
if (fileContent.isEmpty())
private void removeEvent(String url, String raceId, String serializedEvent) {
if (persistedEvents.isEmpty())
return;
ExLog.i(TAG, "removeEvent is called for " + raceUuid + ", serializedEvent " + serializedEvent);
String eventLine = String.format("%s;%s\n", raceUuid, serializedEvent);
removeEntryInFileContent(eventLine);
ExLog.i(TAG, String.format("Removing event \"%s\" for race %s.", serializedEvent, raceId));
String eventLine = getSerializedIntentForPersistence(url, raceId, serializedEvent);
removePersistedEvent(eventLine);
}
/**
* @param eventLine
*/
private void removeEntryInFileContent(String eventLine) {
if (fileContent.contains(eventLine)) {
fileContent = fileContent.replace(eventLine, "");
writeFileContentToFile();
ExLog.i(TAG, "Entry " + eventLine + " is removed from fileContent");
private void removePersistedEvent(String eventLine) {
if (persistedEvents.contains(eventLine)) {
persistedEvents.remove(eventLine);
writePersistedEventsToFile();
ExLog.i(TAG, "Event removed.");
}
}
public int getEventCount() {
if (fileContent.isEmpty()) {
return 0;
}
return fileContent.split("\n").length;
return persistedEvents.size();
}
public List<Intent> restoreEvents() {
List<Intent> delayedIntents = new ArrayList<Intent>();
List<String> fileEntriesToBeRemoved = new ArrayList<String>(); //These file entries may be too old. No appropriate race id has been found for this event.
if (!fileContent.isEmpty()) {
String[] eventLines = fileContent.split("\n");
for (String line : eventLines) {
if (!line.isEmpty()) {
String[] lineParts = line.split(";");
for (String persistedEvent : persistedEvents) {
Intent eventIntent = EventSendingService.createEventIntent(context, lineParts[0], lineParts[1]);
if (eventIntent != null) {
delayedIntents.add(eventIntent);
} else {
fileEntriesToBeRemoved.add(line);
}
}
String[] lineParts = persistedEvent.split(";");
String url = lineParts[2];
String raceId = lineParts[0];
String serializedEvent = lineParts[1];
Intent eventIntent = EventSendingService.createEventIntent(context, url, raceId, serializedEvent);
if (eventIntent != null) {
delayedIntents.add(eventIntent);
}
}
for (String lineToBeRemoved : fileEntriesToBeRemoved) {
removeEntryInFileContent(lineToBeRemoved);
}
ExLog.i(TAG, "Restored " + delayedIntents.size() + " events");
return delayedIntents;
@@ -141,30 +135,40 @@ public class EventPersistenceManager {
return fileContent;
}
private void writeEventToFile(String eventLine) {
fileContent += eventLine;
writeFileContentToFile();
private void saveEvent(String eventLine) {
persistedEvents.add(eventLine);
writePersistedEventsToFile();
ExLog.i(TAG, "Wrote event to file: " + eventLine);
}
private void writeFileContentToFile() {
writeToFile(fileContent, Context.MODE_PRIVATE);
ExLog.i(TAG, "Wrote file content to file: " + fileContent);
private void writePersistedEventsToFile() {
String newFileContent = "";
for (String persistedEvent : persistedEvents) {
newFileContent += persistedEvent + "\n";
}
writeToFile(newFileContent, Context.MODE_PRIVATE);
ExLog.i(TAG, "Wrote file content to file: " + newFileContent);
}
private void initializeFile() {
private void initializeFileAndPersistedEvents() {
try {
fileContent = getFileContent();
String fileContent = getFileContent();
String[] eventLines = fileContent.split("\n");
for (String eventLine : eventLines) {
if (!eventLine.isEmpty()) {
persistedEvents.add(eventLine);
}
}
} catch (FileNotFoundException e) {
ExLog.w(TAG, "persistence file not found in internal storage. The file will be created.");
clearFileContent();
clearPersistedEvents();
}
ExLog.i(TAG, "Initialized file");
}
private void clearFileContent() {
fileContent = "";
writeFileContentToFile();
private void clearPersistedEvents() {
persistedEvents.clear();
writePersistedEventsToFile();
}
private void writeToFile(String content, int mode) {
@@ -34,7 +34,7 @@ public class EventSenderTask extends AsyncTask<Intent, Void, Pair<Intent, Intege
}
Bundle extras = intent.getExtras();
Serializable serializedEvent = extras.getSerializable(AppConstants.EXTRAS_JSON_KEY);
Serializable serializedEvent = extras.getSerializable(AppConstants.EXTRAS_SERIALIZED_EVENT);
String url = extras.getString(AppConstants.EXTRAS_URL);
if (serializedEvent == null || url == null) {
return Pair.create(intent, -1);
@@ -47,7 +47,7 @@ public class EventSenderTask extends AsyncTask<Intent, Void, Pair<Intent, Intege
} finally {
post.disconnect();
}
ExLog.i(getClass().getName(), "Post request issued for the following Event: " + serializedEvent);
ExLog.i(getClass().getName(), "Post successful for the following event: " + serializedEvent);
} catch (Exception e) {
ExLog.e(getClass().getName(), e.toString());
return Pair.create(intent, -1);
@@ -17,8 +17,6 @@ import android.os.IBinder;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.data.DataManager;
import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.logging.ExLog;
import com.sap.sailing.racecommittee.app.receiver.ConnectivityChangedReceiver;
@@ -43,6 +41,17 @@ public class EventSendingService extends Service implements EventSendingListener
private final IBinder mBinder = new EventSendingBinder();
private EventPersistenceManager persistenceManager;
private boolean isHandlerSet;
private EventSendingServiceLogger serviceLogger;
public interface EventSendingServiceLogger {
public void onEventSentSuccessful();
public void onEventSentFailed();
}
public void setEventSendingServiceLogger(EventSendingServiceLogger logger) {
serviceLogger = logger;
}
public class EventSendingBinder extends Binder {
public EventSendingService getService() {
@@ -84,23 +93,17 @@ public class EventSendingService extends Service implements EventSendingListener
URLEncoder.encode(race.getName()),
URLEncoder.encode(race.getFleet().getName()));
return createEventIntent(context, url, race.getId(), serializedEvent);
}
public static Intent createEventIntent(Context context, String url, Serializable raceId, Serializable serializedEvent) {
Intent eventIntent = new Intent(context.getString(R.string.intentActionSendEvent));
eventIntent.putExtra(AppConstants.RACE_ID_KEY, race.getId());
eventIntent.putExtra(AppConstants.EXTRAS_JSON_KEY, serializedEvent);
eventIntent.putExtra(AppConstants.RACE_ID_KEY, raceId);
eventIntent.putExtra(AppConstants.EXTRAS_SERIALIZED_EVENT, serializedEvent);
eventIntent.putExtra(AppConstants.EXTRAS_URL, url);
ExLog.i(TAG, "Created event " + eventIntent + " for sending to backend");
return eventIntent;
}
public static Intent createEventIntent(Context context, Serializable raceId, Serializable serializedEvent) {
ReadonlyDataManager data = DataManager.create(context);
ManagedRace race = data.getDataStore().getRace(raceId);
if (race == null) {
ExLog.e(TAG, "There is no race with id " + raceId);
return null;
}
return createEventIntent(context, race, serializedEvent);
}
/*
* (non-Javadoc)
@@ -144,7 +147,9 @@ public class EventSendingService extends Service implements EventSendingListener
}
private void handleSendEvents(Intent intent) {
ExLog.i(TAG, String.format("Trying to send an event..."));
if (!isConnected()) {
ExLog.i(TAG, String.format("Send aborted because there is no connection."));
persistenceManager.persistIntent(intent);
ConnectivityChangedReceiver.enable(this);
} else {
@@ -153,16 +158,21 @@ public class EventSendingService extends Service implements EventSendingListener
}
private void handleDelayedEvents() {
ExLog.i(TAG, String.format("Trying to resend stored events..."));
isHandlerSet = false;
if (!isConnected())
if (!isConnected()) {
ExLog.i(TAG, String.format("Resend aborted because there is no connection."));
return;
}
sendDelayedEvents();
}
private void sendDelayedEvents() {
List<Intent> delayedIntents = persistenceManager.restoreEvents();
ExLog.i(TAG, String.format("Trying to resend %d waiting events", delayedIntents.size()));
ExLog.i(TAG, String.format("Resending %d events...", delayedIntents.size()));
for (Intent intent : delayedIntents)
sendEvent(intent);
}
@@ -178,17 +188,23 @@ public class EventSendingService extends Service implements EventSendingListener
public void onResult(Intent intent, boolean success) {
if (!success) {
ExLog.w(TAG, "Could not POST intent to server.");
ExLog.w(TAG, "Error while posting intent to server. Will persist intent...");
persistenceManager.persistIntent(intent);
if (!isHandlerSet) {
SendDelayedEventsCaller delayedCaller = new SendDelayedEventsCaller(this);
handler.postDelayed(delayedCaller, 1000 * 30); //after 30 sec, try the sending again
isHandlerSet = true;
}
serviceLogger.onEventSentFailed();
} else {
ExLog.i(TAG, "Event successfully send. " + intent.getStringExtra(AppConstants.EXTRAS_JSON_KEY));
persistenceManager.removeIntent(intent);
ExLog.i(TAG, "Event successfully send.");
if (persistenceManager.areIntentsDelayed()) {
persistenceManager.removeIntent(intent);
}
lastSuccessfulSend = Calendar.getInstance().getTime();
serviceLogger.onEventSentSuccessful();
}
}
@@ -1,30 +1,75 @@
package com.sap.sailing.racecommittee.app.ui.activities;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.logging.ExLog;
import java.util.Date;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.os.IBinder;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.services.sending.EventSendingService;
import com.sap.sailing.racecommittee.app.services.sending.EventSendingService.EventSendingBinder;
import com.sap.sailing.racecommittee.app.services.sending.EventSendingService.EventSendingServiceLogger;
/**
* Base activity for all race committee cockpit activities enabling basic menu functionality.
*/
public abstract class BaseActivity extends Activity {
private class EventSendingServiceConnection implements ServiceConnection, EventSendingServiceLogger {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
EventSendingBinder binder = (EventSendingBinder) service;
sendingService = binder.getService();
boundSendingService = true;
sendingService.setEventSendingServiceLogger(this);
updateLiveIcon();
}
@Override
public void onServiceDisconnected(ComponentName arg) {
boundSendingService = false;
}
@Override
public void onEventSentSuccessful() {
updateLiveIcon();
}
@Override
public void onEventSentFailed() {
updateLiveIcon();
}
}
protected MenuItem menuItemLive;
boolean boundSendingService = false;
EventSendingService sendingService;
EventSendingServiceConnection sendingServiceConnection;
String sendingServiceStatus = "";
public BaseActivity() {
this.sendingServiceConnection = new EventSendingServiceConnection();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
menuItemLive = menu.findItem(R.id.LiveIcon);
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
return true;
}
@@ -39,7 +84,7 @@ public abstract class BaseActivity extends Activity {
fadeActivity(SettingsActivity.class);
return true;
case R.id.LiveIcon:
Toast.makeText(this, AppConstants.getServerBaseURL(this), Toast.LENGTH_SHORT).show();
Toast.makeText(this, getLiveIconText(), Toast.LENGTH_LONG).show();
return true;
case R.id.WindLog: // fadeActivity(WindActivity.class); return false;
case android.R.id.home:
@@ -48,37 +93,58 @@ public abstract class BaseActivity extends Activity {
return super.onOptionsItemSelected(item);
}
}
protected boolean onHomeClicked() {
return false;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
updateLiveIcon();
return super.onPrepareOptionsMenu(menu);
}
protected boolean onHomeClicked() {
return false;
}
@Override
protected void onResume() {
super.onResume();
updateLiveIcon();
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, EventSendingService.class);
bindService(intent, sendingServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (boundSendingService) {
unbindService(sendingServiceConnection);
boundSendingService = false;
}
}
private void updateLiveIcon() {
if (menuItemLive == null)
return;
ExLog.i("OptionsMenu", "Querying remote status.");
if (AppConstants.getServerBaseURL(this).indexOf("sapsailing.com") != -1) {
ExLog.i("OptionsMenu", "It's remote.");
menuItemLive.setIcon(R.drawable.ic_menu_share_sapsailing);
if (!boundSendingService)
return;
int errorCount = this.sendingService.getDelayedIntentsCount();
if (errorCount > 0) {
menuItemLive.setIcon(R.drawable.ic_menu_share_red);
Date lastSuccessfulSend = this.sendingService.getLastSuccessfulSend();
sendingServiceStatus = String.format("Currently %d events waiting to be sent.\nLast successful sent was at %s",
errorCount, lastSuccessfulSend == null ? "never" : lastSuccessfulSend);
} else {
ExLog.i("OptionsMenu", "It's local.");
menuItemLive.setIcon(R.drawable.ic_menu_share_local);
menuItemLive.setIcon(R.drawable.ic_menu_share_holo_light);
sendingServiceStatus = String.format("Currently no event waiting to be sent.", errorCount);
}
}
private String getLiveIconText() {
return String.format("Connected to: %s\n%s", AppConstants.getServerBaseURL(this), sendingServiceStatus);
}
protected void fadeActivity(Class<?> activity) {
Intent intent = new Intent(getBaseContext(), activity);
fadeActivity(intent);
@@ -177,8 +177,10 @@ public class RacingActivity extends TwoPaneActivity implements RaceInfoListener,
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
if (!isFinishing()) {
AlertDialog alert = builder.create();
alert.show();
}
}
public void onRaceItemClicked(ManagedRace managedRace) {
@@ -1,8 +1,13 @@
package com.sap.sailing.racecommittee.app.ui.activities;
import java.util.ArrayList;
import java.util.List;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.PreferenceActivity;
import com.sap.sailing.domain.common.racelog.StartProcedureType;
import com.sap.sailing.racecommittee.app.R;
/**
@@ -15,5 +20,18 @@ public class SettingsActivity extends PreferenceActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.layout.settings_view);
ListPreference startProcedurePreference = (ListPreference) findPreference("defaultStartProcedureType");
List<CharSequence> entries = new ArrayList<CharSequence>();
List<CharSequence> entryValues = new ArrayList<CharSequence>();
for (StartProcedureType type : StartProcedureType.values()) {
entries.add(type.toString());
entryValues.add(type.name());
}
startProcedurePreference.setEntries(entries.toArray(new CharSequence[0]));
startProcedurePreference.setEntryValues(entryValues.toArray(new CharSequence[0]));
}
}
@@ -19,6 +19,17 @@ public abstract class RaceFragment extends Fragment implements TickListener {
return arguments;
}
/**
* Creates a bundle that contains the race id as parameter for the next fragment
*
* @return a bundle containing the race id
*/
protected Bundle getRecentArguments() {
Bundle args = new Bundle();
args.putSerializable(AppConstants.RACE_ID_KEY, managedRace.getId());
return args;
}
private ManagedRace managedRace;
@Override
@@ -49,16 +60,10 @@ public abstract class RaceFragment extends Fragment implements TickListener {
public ManagedRace getRace() {
return managedRace;
}
/**
* Creates a bundle that contains the race id as parameter for the next fragment
*
* @return a bundle containing the race id
*/
protected Bundle getParameterBundle() {
Bundle args = new Bundle();
args.putSerializable(AppConstants.RACE_ID_KEY, managedRace.getId());
return args;
@Override
public void notifyTick() {
// may be overriden in subclasses.
}
}
@@ -147,7 +147,7 @@ public class RaceInfoFragment extends RaceFragment implements RaceStateChangedLi
RaceDialogFragment fragment = new CourseDesignDialogFragment();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
fragment.setArguments(args);
fragment.show(fragmentManager, "courseDesignDialogFragment");
@@ -119,7 +119,7 @@ public class FinishingRaceFragment extends RaceFragment {
RaceDialogFragment fragment = new AbortModeSelectionDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
args.putString(AppConstants.FLAG_KEY, Flags.NOVEMBER.name());
fragment.setArguments(args);
@@ -171,7 +171,7 @@ public class GateStartRunningRaceFragment extends RaceFragment implements GateSt
RaceDialogFragment fragment = new AbortTypeSelectionDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
fragment.setArguments(args);
fragment.show(fragmentManager, "dialogAPNovemberMode");
@@ -185,7 +185,7 @@ public class GateStartRunningRaceFragment extends RaceFragment implements GateSt
private void showPathFinderDialog() {
FragmentManager fragmentManager = getFragmentManager();
RaceDialogFragment fragment = new RaceChoosePathFinderDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
fragment.setArguments(args);
fragment.show(fragmentManager, null);
}
@@ -193,7 +193,7 @@ public class GateStartRunningRaceFragment extends RaceFragment implements GateSt
private void showLineOpeningTimeDialog() {
FragmentManager fragmentManager = getFragmentManager();
RaceDialogFragment fragment = new RaceChooseLineOpeningTimeDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
fragment.setArguments(args);
fragment.show(fragmentManager, null);
}
@@ -156,7 +156,7 @@ public class RunningRaceFragment extends RaceFragment implements IndividualRecal
RaceDialogFragment fragment = new AbortTypeSelectionDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
fragment.setArguments(args);
fragment.show(fragmentManager, "dialogAPNovemberMode");
@@ -5,17 +5,23 @@ import java.util.Date;
import android.app.FragmentManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.racelog.StartProcedureType;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
@@ -33,13 +39,14 @@ public class SetStartTimeRaceFragment extends RaceFragment {
}
protected boolean isReset;
private StartProcedureType selectedStartProcedureType;
protected TimePicker startTimePicker;
protected Button setStartTimeButton;
protected TextView countdownView;
protected ImageButton abortRaceButton;
protected Date scheduledTime;
protected Spinner spinnerStartProcedure;
protected TimePicker pickerTime;
protected Button btSetDate;
protected Button btSetTime;
protected Button btPostpone;
protected TextView textInfoText;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@@ -49,100 +56,144 @@ public class SetStartTimeRaceFragment extends RaceFragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isReset = getArguments().getBoolean(AppConstants.RESET_TIME_FRAGMENT_IS_RESET);
startTimePicker = (TimePicker) getView().findViewById(R.id.timePicker);
setStartTimeButton = (Button) getView().findViewById(R.id.btnRescheduleTime);
countdownView = (TextView) getView().findViewById(R.id.time_below_picker);
abortRaceButton = (ImageButton) getView().findViewById(R.id.resetTimeAPButton);
spinnerStartProcedure = (Spinner) getView().findViewById(R.id.race_reset_time_spinner_procedure);
setupStartProcedureSpinner();
pickerTime = (TimePicker) getView().findViewById(R.id.race_reset_time_picker_time);
setupTimePicker();
btSetDate = (Button) getView().findViewById(R.id.race_reset_time_btn_date);
btSetTime = (Button) getView().findViewById(R.id.race_reset_time_btn_time);
btPostpone = (Button) getView().findViewById(R.id.race_reset_time_btn_postpone);
startTimePicker.setIs24HourView(true);
startTimePicker.setOnClickListener(new OnClickListener() {
btSetDate.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
refreshDifferenceTime();
Toast.makeText(getActivity(), "Not implemented yet.", Toast.LENGTH_SHORT).show();
}
});
setStartTimeButton.setText(isReset ? R.string.reset_time : R.string.set_time);
setStartTimeButton.setOnClickListener(new OnClickListener() {
btSetTime.setText(isReset ? R.string.reset_time : R.string.set_time);
btSetTime.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
setStartTime();
}
});
abortRaceButton.setOnClickListener(new OnClickListener() {
btPostpone.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
ExLog.i(ExLog.RACE_SET_TIME_BUTTON_AP, getRace().getId().toString(), getActivity());
showAPModeDialog();
}
});
// / TODO: click-listener for abort button
refreshTimePickerTime();
textInfoText = (TextView) getView().findViewById(R.id.race_reset_time_text_infotext);
}
private void setupTimePicker() {
pickerTime.setIs24HourView(true);
// In 10 minutes from now, but always a 5-minute-mark.
Calendar now = Calendar.getInstance();
now.add(Calendar.MINUTE, 10);
int hours = now.get(Calendar.HOUR_OF_DAY);
int minutes = now.get(Calendar.MINUTE);
minutes = (int) (Math.ceil((minutes / 5.0)) * 5.0);
if (minutes >= 60) {
hours++;
}
pickerTime.setCurrentHour(hours);
pickerTime.setCurrentMinute(minutes);
}
private void setupStartProcedureSpinner() {
ArrayAdapter<StartProcedureType> adapter = new ArrayAdapter<StartProcedureType>(getActivity(),
android.R.layout.simple_spinner_dropdown_item, StartProcedureType.values());
spinnerStartProcedure.setAdapter(adapter);
spinnerStartProcedure.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
selectedStartProcedureType = (StartProcedureType) adapterView.getItemAtPosition(position);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
StartProcedureType type = StartProcedureType.ESS;
boolean overrideStartProcedureType = PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean(
"overrideDefaultStartProcedureType", false);
if (overrideStartProcedureType) {
type = StartProcedureType.valueOf(PreferenceManager.getDefaultSharedPreferences(getActivity()).getString(
"defaultStartProcedureType", StartProcedureType.ESS.name()));
} else {
type = getRace().getState().getStartProcedureType();
}
spinnerStartProcedure.setSelection(adapter.getPosition(type));
}
@Override
public void onStart() {
super.onStart();
ExLog.w(SetStartTimeRaceFragment.class.getName(), String.format("Fragment %s is now shown", SetStartTimeRaceFragment.class.getName()));
ExLog.i(SetStartTimeRaceFragment.class.getName(),
String.format("Fragment %s is now shown", SetStartTimeRaceFragment.class.getName()));
}
@Override
public void notifyTick() {
refreshDifferenceTime();
super.notifyTick();
textInfoText.setText(String.format("Start %s", getTimeStringToStart()));
}
private void refreshTimePickerTime() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, AppConstants.DefaultStartTimeMinuteOffset);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
startTimePicker.setCurrentHour(hour);
startTimePicker.setCurrentMinute(minute);
refreshDifferenceTime();
private Date getStartTime() {
Calendar newStartTimeCal = Calendar.getInstance();
// newStartTimeCal.set(Calendar.YEAR, 0);
// newStartTimeCal.set(Calendar.MONTH, 0);
// newStartTimeCal.set(Calendar.DAY_OF_MONTH, 0);
newStartTimeCal.set(Calendar.HOUR_OF_DAY, pickerTime.getCurrentHour());
newStartTimeCal.set(Calendar.MINUTE, pickerTime.getCurrentMinute());
newStartTimeCal.set(Calendar.SECOND, 0);
newStartTimeCal.set(Calendar.MILLISECOND, 0);
Date newStartTime = newStartTimeCal.getTime();
return newStartTime;
}
private void refreshDifferenceTime() {
// This method might be called by the ticker before initialization happens...
if (startTimePicker == null || countdownView == null) {
return;
private String getTimeStringToStart() {
Date startTime = getStartTime();
Date now = new Date();
long differenceInSeconds = (startTime.getTime() - now.getTime()) / 1000;
boolean isInPast = Math.signum(differenceInSeconds) < 0;
if (isInPast) {
differenceInSeconds = Math.abs(differenceInSeconds);
}
// / TODO: Why is this method synchronized?
synchronized (this) {
int hourOfDay = startTimePicker.getCurrentHour();
int minute = startTimePicker.getCurrentMinute();
long diff[] = new long[] { 0, 0, 0, 0 };
/* sec */diff[3] = (differenceInSeconds >= 60 ? differenceInSeconds % 60 : differenceInSeconds);
/* min */diff[2] = (differenceInSeconds = (differenceInSeconds / 60)) >= 60 ? differenceInSeconds % 60
: differenceInSeconds;
/* hours */diff[1] = (differenceInSeconds = (differenceInSeconds / 60)) >= 24 ? differenceInSeconds % 24
: differenceInSeconds;
/* days */diff[0] = (differenceInSeconds = (differenceInSeconds / 24));
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
Date nowDate = calendar.getTime();
Date pickedDate = (Date) nowDate.clone();
pickedDate.setHours(hourOfDay);
pickedDate.setMinutes(minute);
pickedDate.setSeconds(0);
long nowTime = nowDate.getTime();
long pickedTime = pickedDate.getTime();
long diffTime = pickedTime - nowTime;
if (diffTime >= 0 && diffTime > 12 * 60 * 60 * 1000) {
diffTime -= (24 * 60 * 60 * 1000);
} else if (diffTime < 0 && diffTime < -(12 * 60 * 60 * 1000)) {
diffTime += (24 * 60 * 60 * 1000);
}
long diffHours = diffTime / 1000 / 60 / 60;
long diffMins = (diffTime / 1000 / 60) % 60;
long diffSecs = (diffTime / 1000) % 60;
String minusInd = (diffHours < 0 || diffMins < 0 || diffSecs < 0 ? "-" : "");
countdownView.setText(getString(R.string.time_until_start) + ": " + minusInd + Math.abs(diffHours) + "h "
+ Math.abs(diffMins) + "min " + Math.abs(diffSecs) + "sec");
String timeText = "";
if (diff[0] > 0) {
timeText += String.format("%d day%s, ", diff[0], diff[0] > 1 ? "s" : "");
}
if (diff[1] > 0) {
timeText += String.format("%d hours%s, ", diff[1], diff[1] > 1 ? "s" : "");
}
if (diff[2] > 0) {
timeText += String.format("%d minute%s, ", diff[2], diff[2] > 1 ? "s" : "");
}
timeText += String.format("%d second%s", diff[3], diff[3] > 1 ? "s" : "");
return isInPast ? String.format("%s %s", timeText, "ago") : String.format("%s %s", "in", timeText);
}
protected void setStartTime() {
@@ -151,20 +202,13 @@ public class SetStartTimeRaceFragment extends RaceFragment {
} else {
ExLog.i(ExLog.RACE_SET_TIME, getRace().getId().toString(), getActivity());
}
Calendar newStartTime = Calendar.getInstance();
newStartTime.set(Calendar.HOUR_OF_DAY, startTimePicker.getCurrentHour());
newStartTime.set(Calendar.MINUTE, startTimePicker.getCurrentMinute());
newStartTime.set(Calendar.SECOND, 0);
newStartTime.set(Calendar.MILLISECOND, 0);
setStartTime(newStartTime.getTime());
setStartTime(getStartTime());
// TODO: start course designer activity!
}
private void setStartTime(Date newStartTime) {
getRace().getState().setStartTime(new MillisecondsTimePoint(newStartTime));
getRace().getState().setStartTime(new MillisecondsTimePoint(newStartTime), selectedStartProcedureType);
}
protected void showAPModeDialog() {
@@ -172,7 +216,7 @@ public class SetStartTimeRaceFragment extends RaceFragment {
RaceDialogFragment fragment = new AbortModeSelectionDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
args.putString(AppConstants.FLAG_KEY, Flags.AP.name());
fragment.setArguments(args);
@@ -94,6 +94,7 @@ public class EssStartPhaseFragment extends RaceFragment implements EssStartPhase
log.lockForRead();
try {
RaceLogEvent lastEvent = log.getLastFixAtOrBefore(MillisecondsTimePoint.now());
// TODO: better analysis of state. only flagevents... sort by timestamp...
if (lastEvent instanceof RaceLogFlagEvent) {
RaceLogFlagEvent flagEvent = (RaceLogFlagEvent) lastEvent;
Flags flag = flagEvent.getUpperFlag();
@@ -102,9 +103,11 @@ public class EssStartPhaseFragment extends RaceFragment implements EssStartPhase
onAPDown();
} else if (flagEvent.isDisplayed() && flag.equals(Flags.ESSTHREE)) {
onEssThreeUp();
} else if (flagEvent.isDisplayed() && flag.equals(Flags.ESSTWO)) {
} else if ((!flagEvent.isDisplayed() && flag.equals(Flags.ESSTHREE)) ||
(flagEvent.isDisplayed() && flag.equals(Flags.ESSTWO))) {
onEssTwoUp();
} else if (flagEvent.isDisplayed() && flag.equals(Flags.ESSONE)) {
} else if ((!flagEvent.isDisplayed() && flag.equals(Flags.ESSTWO)) ||
(flagEvent.isDisplayed() && flag.equals(Flags.ESSONE))) {
onEssOneUp();
}
}
@@ -159,7 +162,7 @@ public class EssStartPhaseFragment extends RaceFragment implements EssStartPhase
RaceDialogFragment fragment = new AbortModeSelectionDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
args.putString(AppConstants.FLAG_KEY, Flags.AP.name());
fragment.setArguments(args);
@@ -122,7 +122,7 @@ public class GateStartPhaseFragment extends RaceFragment implements GateStartPha
private void showPathFinderDialog() {
FragmentManager fragmentManager = getFragmentManager();
RaceDialogFragment fragment = new RaceChoosePathFinderDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
fragment.setArguments(args);
fragment.show(fragmentManager, null);
}
@@ -130,7 +130,7 @@ public class GateStartPhaseFragment extends RaceFragment implements GateStartPha
private void showLineOpeningTimeDialog() {
FragmentManager fragmentManager = getFragmentManager();
RaceDialogFragment fragment = new RaceChooseLineOpeningTimeDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
fragment.setArguments(args);
fragment.show(fragmentManager, null);
}
@@ -204,7 +204,7 @@ public class GateStartPhaseFragment extends RaceFragment implements GateStartPha
RaceDialogFragment fragment = new AbortModeSelectionDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
args.putString(AppConstants.FLAG_KEY, Flags.AP.name());
fragment.setArguments(args);
@@ -92,7 +92,7 @@ public class StartphaseRaceFragment extends RaceFragment {
RaceDialogFragment fragment = new AbortModeSelectionDialog();
Bundle args = getParameterBundle();
Bundle args = getRecentArguments();
args.putString(AppConstants.FLAG_KEY, Flags.AP.name());
fragment.setArguments(args);