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

This commit is contained in:
fmittag committed 2013-08-30 15:28:06 +02:00
commit daee1914a3
111 files changed
+2469 -1241

No files matched your search

@@ -142,7 +142,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
private TimePoint loadTimePoint(DBObject object, FieldNames field) {
return new MillisecondsTimePoint((Long) object.get(field.name()));
return new MillisecondsTimePoint(((Number) object.get(field.name())).longValue());
}
/**
@@ -1006,14 +1006,15 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
RaceLog result = new RaceLogImpl(RaceLogImpl.class.getSimpleName(), identifier.getIdentifier());
try {
BasicDBObject query = new BasicDBObject();
query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(identifier.getIdentifier().toString()));
DBCollection raceLog = database.getCollection(CollectionNames.RACE_LOGS.name());
for (DBObject o : raceLog.find(query)) {
RaceLogEvent raceLogEvent = loadRaceLogEvent((DBObject) o.get(FieldNames.RACE_LOG_EVENT.name()));
if (raceLogEvent != null)
result.add(raceLogEvent);
}
query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(identifier.getIdentifier()));
loadRaceLogEvents(result, query);
// query for obsolete events
query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(String.format("%s.%s.%s",
identifier.getTemplate().getParentObjectName(),
identifier.getRaceColumnName(),
identifier.getFleetName())));
loadRaceLogEvents(result, query);
} catch (Throwable t) {
// something went wrong during DB access; report, then use empty new race log
logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load recorded race log data. Check MongoDB settings.");
@@ -1022,6 +1023,16 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
return result;
}
private void loadRaceLogEvents(RaceLog result, BasicDBObject query) {
DBCollection raceLog = database.getCollection(CollectionNames.RACE_LOGS.name());
for (DBObject o : raceLog.find(query)) {
RaceLogEvent raceLogEvent = loadRaceLogEvent((DBObject) o.get(FieldNames.RACE_LOG_EVENT.name()));
if (raceLogEvent != null) {
result.add(raceLogEvent);
}
}
}
public RaceLogEvent loadRaceLogEvent(DBObject dbObject) {
TimePoint timePoint = loadTimePoint(dbObject);
TimePoint createdAt = loadTimePoint(dbObject, FieldNames.RACE_LOG_EVENT_CREATED_AT);
@@ -1190,7 +1201,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private CourseBase loadCourseData(BasicDBList dbCourseList, String courseName) {
if (courseName == null) {
courseName = "Course template";
courseName = "Course";
}
CourseBase courseData = new CourseDataImpl(courseName);
int i = 0;
@@ -1,5 +1,5 @@
package com.sap.sailing.domain.persistence.impl;
import java.io.Serializable;
import java.util.List;
import java.util.logging.Level;
@@ -536,7 +536,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogFlagEvent flagEvent) {
BasicDBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogFlagEvent(flagEvent));
return result;
@@ -544,7 +544,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogStartTimeEvent startTimeEvent) {
BasicDBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogStartTimeEvent(startTimeEvent));
return result;
@@ -552,7 +552,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogPassChangeEvent passChangeEvent) {
BasicDBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogPassChangeEvent(passChangeEvent));
return result;
@@ -560,7 +560,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogRaceStatusEvent raceStatusEvent) {
BasicDBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogRaceStatusEvent(raceStatusEvent));
return result;
@@ -568,63 +568,63 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogCourseAreaChangedEvent courseAreaChangedEvent) {
BasicDBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogCourseAreaChangedEvent(courseAreaChangedEvent));
return result;
}
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogCourseDesignChangedEvent courseDesignChangedEvent) {
BasicDBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogCourseDesignChangedEvent(courseDesignChangedEvent));
return result;
}
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogFinishPositioningListChangedEvent finishPositioningListChangedEvent) {
BasicDBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogFinishPositioningListChangedEvent(finishPositioningListChangedEvent));
return result;
}
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogFinishPositioningConfirmedEvent finishPositioningConfirmedEvent) {
BasicDBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogFinishPositioningConfirmedEvent(finishPositioningConfirmedEvent));
return result;
}
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogPathfinderEvent pathfinderEvent) {
BasicDBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogPathfinderEvent(pathfinderEvent));
return result;
}
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogGateLineOpeningTimeEvent gateLineOpeningTimeEvent) {
BasicDBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogGateLineOpeningTimeEvent(gateLineOpeningTimeEvent));
return result;
}
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogStartProcedureChangedEvent event) {
DBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogStartProcedureChangedEvent(event));
return result;
}
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogProtestStartTimeEvent event) {
DBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogProtestStartTimeEvent(event));
return result;
}
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogWindFixEvent event) {
DBObject result = new BasicDBObject();
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(raceLogIdentifier.getIdentifier().toString()));
result.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(raceLogIdentifier.getIdentifier()));
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogWindFix(event));
return result;
}
@@ -0,0 +1,15 @@
package com.sap.sailing.domain.persistence.impl;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.sap.sailing.domain.common.impl.Util.Triple;
public class TripleSerializer {
public static DBObject serialize(Triple<String, String, String> value) {
DBObject container = new BasicDBObject();
container.put("a", value.getA());
container.put("b", value.getB());
container.put("c", value.getC());
return container;
}
}
@@ -831,4 +831,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
// TODO Auto-generated method stub
}
@Override
public TimePoint getStartTimeReceived() {
// TODO Auto-generated method stub
return null;
}
}
@@ -539,4 +539,10 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
}
@Override
public TimePoint getStartTimeReceived() {
// TODO Auto-generated method stub
return null;
}
}
@@ -11,5 +11,6 @@ public interface TracTracConnectionConstants {
final int PORT_STORED = PORT_LIVE + 1; //For official usage use 4401;
final String HIDDEN_STATUS = "HIDDEN";
final Object REPLAY_STATUS = "REPLAY";
}
@@ -41,6 +41,7 @@ public class TracTracCourseDesignUpdateHandler extends UpdateHandler implements
@Override
public void courseDesignChanged(CourseBase newCourseDesign) throws MalformedURLException, IOException {
if (!isActive()) {
logger.info("Not sending course update to TracTrac because no URL has been provided.");
return;
}
@@ -582,12 +582,27 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
updateStatusOfTrackedRaces();
} else {
// ask RacingEventService to cleanly stop and unregister this tracker
for (RaceDefinition race : getRaces()) {
try {
trackedRegattaRegistry.stopTracking(getRegatta(), race);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error trying to stop tracker for race "+race.getName()+
" in regatta "+getRegatta().getName(), e);
// if the race has all data loaded. Doing this asynchronously because
// stopping can take longer and if you're loading many races in parallel
// this can slow down loading extremely
for (final RaceDefinition race : getRaces()) {
DynamicTrackedRace trackedRace = getTrackedRegatta().getExistingTrackedRace(race);
final Regatta regatta = getRegatta();
if (trackedRace.getStatus().getLoadingProgress() == 1.0) {
Thread raceStopper = new Thread(new Runnable() {
@Override
public void run() {
try {
trackedRegattaRegistry.stopTracking(regatta, race);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error trying to stop tracker for race "+race.getName()+
" in regatta "+getRegatta().getName(), e);
}
}
});
raceStopper.start();
} else {
logger.log(Level.SEVERE, "Not stopping race "+race.getName()+" because it has not all data loaded! Progress: " + trackedRace.getStatus().getLoadingProgress());
}
}
}
@@ -2,13 +2,15 @@ package com.sap.sailing.domain.racelog;
import java.io.Serializable;
import com.sap.sailing.domain.common.impl.Util.Triple;
public interface RaceLogIdentifier extends Serializable {
String getRaceColumnName();
String getFleetName();
Serializable getIdentifier();
Triple<String, String, String> getIdentifier();
RaceLogIdentifierTemplate getTemplate();
@@ -1,8 +1,8 @@
package com.sap.sailing.domain.racelog.impl;
import java.io.Serializable;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogIdentifierTemplate;
@@ -21,8 +21,8 @@ public class RaceLogIdentifierImpl implements RaceLogIdentifier {
}
@Override
public Serializable getIdentifier() {
return String.format("%s.%s.%s", template.getParentObjectName(), raceColumnName, fleetName);
public Triple<String, String, String> getIdentifier() {
return new Util.Triple<String, String, String>(template.getParentObjectName(), raceColumnName, fleetName);
}
@Override
@@ -575,4 +575,9 @@ public interface TrackedRace extends Serializable {
* If the competitor hasn't started yet, <code>null</code> is returned.
*/
Distance getDistanceFromStarboardSideOfStartLineWhenPassingStart(Competitor competitor);
/**
* Start time received by the tracking infrastructure. To determine real start time use {@link #getStartOfRace()}.
*/
TimePoint getStartTimeReceived();
}
@@ -1,6 +1,9 @@
package com.sap.sailing.domain.tracking.impl;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
@@ -21,19 +24,22 @@ import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent;
import com.sap.sailing.domain.racelog.RaceLogWindFixEvent;
import com.sap.sailing.domain.racelog.analyzing.impl.LastPublishedCourseDesignFinder;
import com.sap.sailing.domain.racelog.analyzing.impl.RaceStatusAnalyzer;
import com.sap.sailing.domain.racelog.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.racelog.analyzing.impl.WindFixesFinder;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.Wind;
public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
public static final Logger log = Logger.getLogger(DynamicTrackedRaceLogListener.class.getName());
private DynamicTrackedRace trackedRace;
private final WindSource raceCommitteeWindSource;
private RaceStatusAnalyzer statusAnalyzer;
private LastPublishedCourseDesignFinder courseDesignFinder;
private StartTimeFinder startTimeFinder;
public DynamicTrackedRaceLogListener(DynamicTrackedRace trackedRace) {
this.trackedRace = trackedRace;
@@ -105,20 +111,34 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
// Because this code can be triggered by an obsolete (delayed) event...
// ... onCourseDesignChangedByRaceCommittee() might be called more than once.
trackedRace.onCourseDesignChangedByRaceCommittee(courseDesign);
} else {
log.info("Could not find any course design update on race log of " + trackedRace.getRace().getName() + "! Not sending out any events.");
}
}
private void analyzeStartTime() {
/* start time will be set by StartTimeAnalyzer in TrackedRace.getStartTime() */
trackedRace.invalidateStartTime();
TimePoint startTime = startTimeFinder.analyze();
if (startTime != null) {
/* invoke listeners with received start time, this will also trigger tractrac update */
trackedRace.onStartTimeChangedByRaceCommittee(startTime);
} else {
log.info("Could not find any valid start time on race log of " + trackedRace.getRace().getName() + "! Not sending out any events.");
}
}
@Override
public void visit(RaceLogPassChangeEvent event) {
trackedRace.invalidateStartTime();
/* this will send tractrac the original start time */
trackedRace.onStartTimeChangedByRaceCommittee(trackedRace.getStartTimeReceived());
}
@Override
public void visit(RaceLogStartTimeEvent event) {
/* start time will be set by StartTimeAnalyzer in TrackedRace.getStartTime() */
trackedRace.invalidateStartTime();
/* invoke listeners with received start time, this will also trigger tractrac update */
trackedRace.onStartTimeChangedByRaceCommittee(event.getStartTime());
analyzeStartTime();
}
@Override
@@ -495,7 +495,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
}
}
if (startTime == null) {
startTime = startTimeReceived;
startTime = getStartTimeReceived();
// If not null, check if the first mark passing for the start line is too much after the
// startTimeReceived; if so, return an adjusted, later start time.
// If no official start time was received, try to estimate the start time using the mark passings for
@@ -1356,7 +1356,8 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
}
}
protected TimePoint getStartTimeReceived() {
@Override
public TimePoint getStartTimeReceived() {
return startTimeReceived;
}
@@ -833,4 +833,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
}
@Override
public TimePoint getStartTimeReceived() {
// TODO Auto-generated method stub
return null;
}
}
@@ -706,3 +706,8 @@ video {
max-width: 100%;
height: auto;
}
.pointeredLink {
cursor: pointer;
cursor: hand;
}
@@ -1,6 +1,8 @@
package com.sap.sailing.gwt.ui.masterdataimport;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.google.gwt.event.dom.client.ClickEvent;
@@ -8,6 +10,8 @@ import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
@@ -15,7 +19,6 @@ import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
@@ -39,7 +42,9 @@ public class MasterDataImportPanel extends VerticalPanel {
private ListBox leaderboardgroupListBox;
private TextBox hostBox;
private Button importLeaderboardGroupsButton;
private Button fetchIdsButton;
private Button fetchIdsButton;
private List<String> allLeaderboardGroupNames;
private RegExp urlValidator;
private RegExp urlPlusTldValidator;
@@ -113,7 +118,7 @@ public class MasterDataImportPanel extends VerticalPanel {
protected void importLeaderboardGroups() {
String[] groupNames = createLeaderBoardGroupNamesFromListBox();
if (groupNames.length >= 1) {
changeAllButtonState(false);
disableAllButtons();
boolean override = overrideSwitch.getValue();
sailingService.importMasterData(currentHost, groupNames, override,
new AsyncCallback<MasterDataImportObjectCreationCount>() {
@@ -135,13 +140,13 @@ public class MasterDataImportPanel extends VerticalPanel {
}
showSuccessAlert(leaderboardsCreated, leaderboardGroupsCreated, eventsCreated,
regattasCreated);
changeAllButtonState(true);
changeButtonStateAccordingToApplicationState();
}
@Override
public void onFailure(Throwable caught) {
showErrorAlert(caught.getLocalizedMessage());
changeAllButtonState(true);
changeButtonStateAccordingToApplicationState();
}
});
} else {
@@ -150,11 +155,33 @@ public class MasterDataImportPanel extends VerticalPanel {
}
private void changeAllButtonState(boolean enabled) {
private void disableAllButtons() {
boolean enabled = false;
importLeaderboardGroupsButton.setEnabled(enabled);
overrideSwitch.setEnabled(enabled);
fetchIdsButton.setEnabled(enabled);
}
private void changeButtonStateAccordingToApplicationState() {
if (leaderboardgroupListBox.getItemCount() > 0 && countSelectedItems() > 0) {
importLeaderboardGroupsButton.setEnabled(true);
} else {
importLeaderboardGroupsButton.setEnabled(false);
}
overrideSwitch.setEnabled(true);
fetchIdsButton.setEnabled(true);
}
private int countSelectedItems() {
int count = 0;
for (int i = 0; i < leaderboardgroupListBox.getItemCount(); i++) {
if (leaderboardgroupListBox.isItemSelected(i)) {
count++;
}
}
return count;
}
protected void showSuccessAlert(int leaderboardsCreated, int leaderboardGroupsCreated, int eventsCreated,
int regattasCreated) {
@@ -187,7 +214,7 @@ public class MasterDataImportPanel extends VerticalPanel {
showErrorAlert(stringMessages.importUrlInvalid(getLgsUrl));
return;
}
changeAllButtonState(false);
disableAllButtons();
RequestBuilder getLgsRequestBuilder = new RequestBuilder(RequestBuilder.GET, getLgsUrl);
getLgsRequestBuilder.setCallback(new RequestCallback() {
@@ -196,41 +223,38 @@ public class MasterDataImportPanel extends VerticalPanel {
if (response.getStatusCode() != 200) {
showErrorAlert(stringMessages.importGetLeaderboardsFailed(response.getStatusCode(), getLgsUrl));
}
int itemCount = leaderboardgroupListBox.getItemCount();
for (int i = itemCount - 1; i >= 0; i--) {
leaderboardgroupListBox.removeItem(i);
}
clearListBox();
String body = response.getText();
if (body == null || body.isEmpty()) {
showErrorAlert(stringMessages.importNoDataReturned());
changeAllButtonState(true);
changeButtonStateAccordingToApplicationState();
return;
}
JSONArray leaderboardGroups = JSONParser.parseStrict(body).isArray();
leaderboardgroupListBox.setVisibleItemCount(leaderboardGroups.size());
List<String> toSort = new ArrayList<String>();
for (int i = 0; i < leaderboardGroups.size(); i++) {
JSONString leaderboardGroupName = leaderboardGroups.get(i).isString();
leaderboardgroupListBox.addItem(leaderboardGroupName.stringValue());
toSort.add(leaderboardGroups.get(i).isString().stringValue());
}
if (leaderboardGroups.size() > 1) {
importLeaderboardGroupsButton.setEnabled(true);
} else {
importLeaderboardGroupsButton.setEnabled(false);
Collections.sort(toSort);
allLeaderboardGroupNames = toSort;
leaderboardgroupListBox.setVisibleItemCount(leaderboardGroups.size());
for (String lgName : toSort) {
leaderboardgroupListBox.addItem(lgName);
}
changeAllButtonState(true);
changeButtonStateAccordingToApplicationState();
}
@Override
public void onError(Request request, Throwable exception) {
showErrorAlert(stringMessages.importServerError());
changeAllButtonState(true);
changeButtonStateAccordingToApplicationState();
}
});
try {
getLgsRequestBuilder.send();
} catch (RequestException e) {
showErrorAlert(stringMessages.importServerError());
changeAllButtonState(true);
changeButtonStateAccordingToApplicationState();
}
}
@@ -266,8 +290,16 @@ public class MasterDataImportPanel extends VerticalPanel {
private void addContentToLeftPanel(VerticalPanel contentPanel) {
contentPanel.add(new Label(stringMessages.importLeaderboardGroups()));
HorizontalPanel filterPanel = new HorizontalPanel();
filterPanel.add(new Label(stringMessages.filterName() + ":"));
TextBox filterBox = new TextBox();
setFilterHandler(filterBox);
filterPanel.add(filterBox);
contentPanel.add(filterPanel);
leaderboardgroupListBox = new ListBox(true);
addSelectionChangedListener();
contentPanel.add(leaderboardgroupListBox);
overrideSwitch = new CheckBox(stringMessages.importOverrideSwitchLabel());
@@ -279,4 +311,48 @@ public class MasterDataImportPanel extends VerticalPanel {
contentPanel.add(importLeaderboardGroupsButton);
}
private void addSelectionChangedListener() {
leaderboardgroupListBox.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
changeButtonStateAccordingToApplicationState();
}
});
}
private void setFilterHandler(final TextBox filterBox) {
filterBox.addKeyUpHandler(new KeyUpHandler() {
@Override
public void onKeyUp(KeyUpEvent event) {
clearListBox();
int visibleNameCount = 0;
List<String> filterTexts = Arrays.asList(filterBox.getText().split(" "));
for(String name : allLeaderboardGroupNames) {
boolean containsAllFilterTexts = true;
for (String filterText : filterTexts) {
if (!name.toUpperCase().contains(filterText.toUpperCase())) {
containsAllFilterTexts = false;
break;
}
}
if (containsAllFilterTexts) {
leaderboardgroupListBox.addItem(name);
visibleNameCount++;
}
}
leaderboardgroupListBox.setVisibleItemCount(visibleNameCount);
changeButtonStateAccordingToApplicationState();
}
});
}
public void clearListBox() {
int itemCount = leaderboardgroupListBox.getItemCount();
for (int i = itemCount - 1; i >= 0; i--) {
leaderboardgroupListBox.removeItem(i);
}
}
}
@@ -9,11 +9,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gwt.cell.client.ClickableTextCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.SafeHtmlCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
@@ -38,8 +36,6 @@ import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.CellPreviewEvent;
import com.google.gwt.view.client.CellPreviewEvent.Handler;
import com.google.gwt.view.client.ListDataProvider;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.NauticalSide;
@@ -57,6 +53,7 @@ import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.shared.components.Component;
import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialogComponent;
import com.sap.sailing.gwt.ui.shared.ClickableSafeHtmlCell;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.RaceGroupDTO;
@@ -145,14 +142,6 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
regattaOverviewDataProvider = new ListDataProvider<RegattaOverviewEntryDTO>();
regattaOverviewTable = createRegattaTable();
regattaOverviewTable.addCellPreviewHandler(new Handler<RegattaOverviewEntryDTO>() {
@Override
public void onCellPreview(CellPreviewEvent<RegattaOverviewEntryDTO> event) {
if (BrowserEvents.CLICK.equals(event.getNativeEvent().getType()) && entryClickedHandler != null){
entryClickedHandler.onEntryClicked(event.getValue());
}
}
});
mainPanel.add(regattaOverviewTable);
setWidget(mainPanel);
@@ -379,13 +368,24 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
});
TextColumn<RegattaOverviewEntryDTO> raceStatusColumn = new TextColumn<RegattaOverviewEntryDTO>() {
Column<RegattaOverviewEntryDTO, SafeHtml> raceStatusColumn = new Column<RegattaOverviewEntryDTO, SafeHtml>(new ClickableSafeHtmlCell()) {
@Override
public String getValue(RegattaOverviewEntryDTO entryDTO) {
return flagInterpreter.getMeaningOfRaceStateAndFlags(entryDTO.raceInfo.lastStatus, entryDTO.raceInfo.lastUpperFlag,
public SafeHtml getValue(final RegattaOverviewEntryDTO entryDTO) {
String status = flagInterpreter.getMeaningOfRaceStateAndFlags(entryDTO.raceInfo.lastStatus, entryDTO.raceInfo.lastUpperFlag,
entryDTO.raceInfo.lastLowerFlag, entryDTO.raceInfo.isLastFlagDisplayed);
SafeHtmlBuilder sb = new SafeHtmlBuilder();
sb.appendHtmlConstant("<a class=\"pointeredLink\">");
sb.appendEscaped(status);
sb.appendHtmlConstant("</a>");
return sb.toSafeHtml();
}
};
raceStatusColumn.setFieldUpdater(new FieldUpdater<RegattaOverviewEntryDTO, SafeHtml>() {
@Override
public void update(int index, RegattaOverviewEntryDTO object, SafeHtml value) {
entryClickedHandler.onEntryClicked(object);
}
});
raceStatusColumn.setSortable(true);
regattaOverviewListHandler.setComparator(raceStatusColumn, new Comparator<RegattaOverviewEntryDTO>() {
@@ -397,29 +397,32 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
});
raceStatusColumn.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
Column<RegattaOverviewEntryDTO, String> raceCourseColumn = new Column<RegattaOverviewEntryDTO, String>(new ClickableTextCell()) {
Column<RegattaOverviewEntryDTO, SafeHtml> raceCourseColumn = new Column<RegattaOverviewEntryDTO, SafeHtml>(new ClickableSafeHtmlCell()) {
@Override
public String getValue(RegattaOverviewEntryDTO entryDTO) {
String courseName = "-";
public SafeHtml getValue(final RegattaOverviewEntryDTO entryDTO) {
SafeHtmlBuilder sb = new SafeHtmlBuilder();
if (entryDTO.raceInfo.lastCourseName != null) {
courseName = entryDTO.raceInfo.lastCourseName;
String courseName = entryDTO.raceInfo.lastCourseName;
sb.appendHtmlConstant("<a class=\"pointeredLink\">");
sb.appendEscaped(courseName);
sb.appendHtmlConstant("</a>");
return sb.toSafeHtml();
} else {
return sb.toSafeHtml();
}
return courseName;
}
};
raceCourseColumn.setFieldUpdater(new FieldUpdater<RegattaOverviewEntryDTO, String>() {
raceCourseColumn.setFieldUpdater(new FieldUpdater<RegattaOverviewEntryDTO, SafeHtml>() {
@Override
public void update(int index, RegattaOverviewEntryDTO object, String value) {
public void update(int index, RegattaOverviewEntryDTO object, SafeHtml value) {
if (object.raceInfo.lastCourseDesign.waypoints.size() > 0) {
DialogBox courseViewDialogBox = createCourseViewDialogBox(object.raceInfo);
courseViewDialogBox.center();
courseViewDialogBox.setGlassEnabled(true);
courseViewDialogBox.setAnimationEnabled(true);
courseViewDialogBox.show();
}
}
}
});
raceCourseColumn.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
@@ -94,12 +94,12 @@ import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.RegattaScoreCorrections;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionForCompetitorInRace;
import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionsForRace;
import com.sap.sailing.domain.common.ScoreCorrectionProvider;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WindSource;
@@ -811,10 +811,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
logger.info("Loaded race " + record.getName() + " in " + record.getEventName() + " start:" + record.getRaceStartTime() + " trackingStart:" + record.getTrackingStartTime() + " trackingEnd:" + record.getTrackingEndTime());
// note that the live URI may be null for races that were put into replay mode
final String effectiveLiveURI;
if (liveURI == null || liveURI.trim().length() == 0) {
effectiveLiveURI = record.getLiveURI() == null ? null : record.getLiveURI().toString();
if (!record.getRaceStatus().equals(TracTracConnectionConstants.REPLAY_STATUS)) {
if (liveURI == null || liveURI.trim().length() == 0) {
effectiveLiveURI = record.getLiveURI() == null ? null : record.getLiveURI().toString();
} else {
effectiveLiveURI = liveURI;
}
} else {
effectiveLiveURI = liveURI;
effectiveLiveURI = null;
}
final String effectiveStoredURI;
if (storedURI == null || storedURI.trim().length() == 0) {
@@ -0,0 +1,62 @@
package com.sap.sailing.gwt.ui.shared;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
/**
* Cell that is clickable and renders its contents as HTML.
* Especially useful if you want to render a ClickableTextCell
* in a CellTable as an anchor.
*
* Use it like this:
* <pre>
* Column<RegattaOverviewEntryDTO, SafeHtml> raceStatusColumn = new Column<RegattaOverviewEntryDTO, SafeHtml>(new ClickableSafeHtmlCell()) {
* @Override
* public SafeHtml getValue(final RegattaOverviewEntryDTO entryDTO) {
* String status = "my string";
* SafeHtmlBuilder sb = new SafeHtmlBuilder();
* sb.appendHtmlConstant("<a>");
* sb.appendEscaped(status);
* sb.appendHtmlConstant("</a>");
* return sb.toSafeHtml();
* }
* };
* </pre>
*
* @author Simon Pamies
*/
public class ClickableSafeHtmlCell extends AbstractCell<SafeHtml> {
public ClickableSafeHtmlCell() {
super("click", "keydown");
}
@Override
public void onBrowserEvent(Context context, Element parent, SafeHtml value, NativeEvent event,
ValueUpdater<SafeHtml> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
if ("click".equals(event.getType())) {
onEnterKeyDown(context, parent, value, event, valueUpdater);
}
}
@Override
protected void onEnterKeyDown(Context context, Element parent, SafeHtml value,
NativeEvent event, ValueUpdater<SafeHtml> valueUpdater) {
if (valueUpdater != null) {
valueUpdater.update(value);
}
}
@Override
public void render(Context context, SafeHtml value, SafeHtmlBuilder sb) {
if (value != null) {
sb.append(value);
}
}
}
@@ -21,6 +21,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.persistence.MongoFactory;
@@ -58,7 +59,7 @@ public class StoreAndLoadRaceLogEventsTest extends AbstractMongoDBTest {
@Before
public void setUp() {
logIdentifier = mock(RaceLogIdentifier.class);
when(logIdentifier.getIdentifier()).thenReturn(UUID.randomUUID());
when(logIdentifier.getIdentifier()).thenReturn(new Triple<String, String, String>("a", "b", UUID.randomUUID().toString()));
}
public void assertBaseFields(RaceLogEvent expectedEvent, RaceLogEvent actualEvent) {
@@ -0,0 +1,152 @@
package com.sap.sailing.mongodb.test;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.Serializable;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.UUID;
import org.bson.types.ObjectId;
import org.junit.Test;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.persistence.MongoFactory;
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogEventFactory;
import com.sap.sailing.domain.racelog.RaceLogFlagEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogIdentifierTemplate;
import com.sap.sailing.domain.racelog.impl.RaceLogIdentifierImpl;
public class StoreAndLoadRaceLogsTest extends AbstractMongoDBTest {
protected MongoObjectFactoryImpl mongoFactory = (MongoObjectFactoryImpl) MongoFactory.INSTANCE
.getMongoObjectFactory(getMongoService());
protected DomainObjectFactoryImpl domainFactory = (DomainObjectFactoryImpl) MongoFactory.INSTANCE
.getDomainObjectFactory(getMongoService());
protected RaceLogEventFactory eventFactory = RaceLogEventFactory.INSTANCE;
public StoreAndLoadRaceLogsTest() throws UnknownHostException, MongoException {
super();
}
@Test
public void testStoreAndLoadSingleRaceLog() {
RaceLogIdentifier identifier = createIdentifier("A.A", "B.B", "C.C");
RaceLogFlagEvent event1 = createRaceLogFlagEvent(UUID.randomUUID());
RaceLogFlagEvent event2 = createRaceLogFlagEvent(UUID.randomUUID());
DBObject dbEvent1 = mongoFactory.storeRaceLogEntry(identifier, event1);
mongoFactory.getRaceLogCollection().insert(dbEvent1);
DBObject dbEvent2 = mongoFactory.storeRaceLogEntry(identifier, event2);
mongoFactory.getRaceLogCollection().insert(dbEvent2);
RaceLog raceLog = domainFactory.loadRaceLog(identifier);
try {
raceLog.lockForRead();
assertEquals(2, Util.size(raceLog.getRawFixes()));
} finally {
raceLog.unlockAfterRead();
}
}
@Test
public void testStoreAndLoadCollidingRaceLogs() {
// RaceLog A.A B.B C.C
RaceLogIdentifier identifier = createIdentifier("A.A", "B.B", "C.C");
RaceLogFlagEvent event1 = createRaceLogFlagEvent(UUID.randomUUID());
RaceLogFlagEvent event2 = createRaceLogFlagEvent(UUID.randomUUID());
DBObject dbEvent1 = mongoFactory.storeRaceLogEntry(identifier, event1);
mongoFactory.getRaceLogCollection().insert(dbEvent1);
DBObject dbEvent2 = mongoFactory.storeRaceLogEntry(identifier, event2);
mongoFactory.getRaceLogCollection().insert(dbEvent2);
// RaceLog A.A.B B.C C
RaceLogIdentifier collidingIdentifier = createIdentifier("A.A.B", "B.C", "C");
RaceLogFlagEvent event3 = createRaceLogFlagEvent(UUID.randomUUID());
DBObject dbEvent3 = mongoFactory.storeRaceLogEntry(collidingIdentifier, event3);
mongoFactory.getRaceLogCollection().insert(dbEvent3);
RaceLog raceLog = domainFactory.loadRaceLog(identifier);
try {
raceLog.lockForRead();
assertEquals(2, Util.size(raceLog.getRawFixes()));
} finally {
raceLog.unlockAfterRead();
}
}
@Test
public void testdLoadObsoleteIdentifier() {
// Identifier is A.A B.B C.C
DBObject dbEvent1 = (DBObject) com.mongodb.util.JSON.parse(createOldFormatRaceLogEventJson(UUID.randomUUID()));
mongoFactory.getRaceLogCollection().insert(dbEvent1);
DBObject dbEvent2 = (DBObject) com.mongodb.util.JSON.parse(createOldFormatRaceLogEventJson(UUID.randomUUID()));
mongoFactory.getRaceLogCollection().insert(dbEvent2);
RaceLog raceLog = domainFactory.loadRaceLog(createIdentifier("A.A", "B.B", "C.C"));
try {
raceLog.lockForRead();
assertEquals(2, Util.size(raceLog.getRawFixes()));
} finally {
raceLog.unlockAfterRead();
}
}
@Test
public void testdLoadObsoleteAndNewIdentifier() {
// event1 stored with obsolete identifier
DBObject dbEvent1 = (DBObject) com.mongodb.util.JSON.parse(createOldFormatRaceLogEventJson(UUID.randomUUID()));
mongoFactory.getRaceLogCollection().insert(dbEvent1);
// event2 stored with new identifier
RaceLogIdentifier identifier = createIdentifier("A.A", "B.B", "C.C");
RaceLogFlagEvent event2 = createRaceLogFlagEvent(UUID.randomUUID());
DBObject dbEvent2 = mongoFactory.storeRaceLogEntry(identifier, event2);
mongoFactory.getRaceLogCollection().insert(dbEvent2);
RaceLog raceLog = domainFactory.loadRaceLog(identifier);
try {
raceLog.lockForRead();
assertEquals(2, Util.size(raceLog.getRawFixes()));
} finally {
raceLog.unlockAfterRead();
}
}
private String createOldFormatRaceLogEventJson(Serializable id) {
return "{ \"_id\" : { \"$oid\" : \""+ new ObjectId().toStringMongod() + "\"} , \"RACE_LOG_IDENTIFIER\" : \"A%2EA%2EB%2EB%2EC%2EC\" , \"RACE_LOG_EVENT\" : "
+ "{ \"TIME_AS_MILLIS\" : 42 , \"RACE_LOG_EVENT_CREATED_AT\" : 1376058090051 , \"RACE_LOG_EVENT_ID\" : { \"$uuid\" : \"" + id.toString() + "\"} , "
+ "\"RACE_LOG_EVENT_PASS_ID\" : 42 , \"RACE_LOG_EVENT_INVOLVED_BOATS\" : [ ] , \"RACE_LOG_EVENT_CLASS\" : \"RaceLogFlagEvent\" , \"RACE_LOG_EVENT_FLAG_UPPER\" : \"ALPHA\" , "
+ "\"RACE_LOG_EVENT_FLAG_LOWER\" : \"BRAVO\" , \"RACE_LOG_EVENT_FLAG_DISPLAYED\" : \"true\"}}";
}
private RaceLogIdentifier createIdentifier(String groupName, String raceColumnName, String fleetName) {
RaceLogIdentifierTemplate template = mock(RaceLogIdentifierTemplate.class);
when(template.getParentObjectName()).thenReturn(groupName);
when(template.getRaceColumnName()).thenReturn(raceColumnName);
Fleet fleet = mock(Fleet.class);
when(fleet.getName()).thenReturn(fleetName);
return new RaceLogIdentifierImpl(template, raceColumnName, fleet);
}
private RaceLogFlagEvent createRaceLogFlagEvent(Serializable id) {
return eventFactory.createFlagEvent(new MillisecondsTimePoint(42), id, Collections.<Competitor> emptyList(),
42, Flags.ALPHA, Flags.BRAVO, true);
}
}
@@ -28,7 +28,7 @@ import com.sap.sailing.domain.racelog.RaceLogCourseDesignChangedEvent;
import com.sap.sailing.domain.racelog.RaceLogEventFactory;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.ControlPointDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseDataDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseBaseDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.GateDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.MarkDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.WaypointDeserializer;
@@ -61,7 +61,7 @@ public class RaceLogCourseDesignChangedEventSerializerTest {
new CourseBaseJsonSerializer(new WaypointJsonSerializer(new ControlPointJsonSerializer(
new MarkJsonSerializer(), new GateJsonSerializer(new MarkJsonSerializer())))));
deserializer = new RaceLogCourseDesignChangedEventDeserializer(new CompetitorDeserializer(factory),
new CourseDataDeserializer(new WaypointDeserializer(new ControlPointDeserializer(new MarkDeserializer(
new CourseBaseDeserializer(new WaypointDeserializer(new ControlPointDeserializer(new MarkDeserializer(
factory), new GateDeserializer(factory, new MarkDeserializer(factory))))));
now = MillisecondsTimePoint.now();
@@ -1,47 +1,47 @@
package com.sap.sailing.server.gateway.deserialization.coursedata.impl;
import java.util.ArrayList;
import java.util.List;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.CourseDataImpl;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseBaseJsonSerializer;
/**
* Deserializer for CourseData.
*/
public class CourseDataDeserializer implements JsonDeserializer<CourseBase> {
private final WaypointDeserializer waypointDeserializer;
public CourseDataDeserializer(WaypointDeserializer waypointDeserializer) {
this.waypointDeserializer = waypointDeserializer;
}
@Override
public CourseBase deserialize(JSONObject object) throws JsonDeserializationException {
String courseName = (String) object.get(CourseBaseJsonSerializer.FIELD_NAME);
JSONArray jsonWaypoints = (JSONArray) object.get(CourseBaseJsonSerializer.FIELD_WAYPOINTS);
List<Waypoint> waypoints = new ArrayList<Waypoint>();
for (Object waypointObject : jsonWaypoints) {
JSONObject jsonWaypoint = (JSONObject) waypointObject;
Waypoint waypoint = waypointDeserializer.deserialize(jsonWaypoint);
waypoints.add(waypoint);
}
CourseBase courseBase = new CourseDataImpl(courseName);
for (int i = 0; i < waypoints.size(); i++) {
courseBase.addWaypoint(i, waypoints.get(i));
}
return courseBase;
}
package com.sap.sailing.server.gateway.deserialization.coursedata.impl;
import java.util.ArrayList;
import java.util.List;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.CourseDataImpl;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseBaseJsonSerializer;
/**
* Deserializer for CourseData.
*/
public class CourseBaseDeserializer implements JsonDeserializer<CourseBase> {
private final WaypointDeserializer waypointDeserializer;
public CourseBaseDeserializer(WaypointDeserializer waypointDeserializer) {
this.waypointDeserializer = waypointDeserializer;
}
@Override
public CourseBase deserialize(JSONObject object) throws JsonDeserializationException {
String courseName = (String) object.get(CourseBaseJsonSerializer.FIELD_NAME);
JSONArray jsonWaypoints = (JSONArray) object.get(CourseBaseJsonSerializer.FIELD_WAYPOINTS);
List<Waypoint> waypoints = new ArrayList<Waypoint>();
for (Object waypointObject : jsonWaypoints) {
JSONObject jsonWaypoint = (JSONObject) waypointObject;
Waypoint waypoint = waypointDeserializer.deserialize(jsonWaypoint);
waypoints.add(waypoint);
}
CourseBase courseBase = new CourseDataImpl(courseName);
for (int i = 0; i < waypoints.size(); i++) {
courseBase.addWaypoint(i, waypoints.get(i));
}
return courseBase;
}
}
@@ -17,8 +17,8 @@ public class Helpers {
if (object instanceof JSONArray) {
return (JSONArray) object;
}
throw new JsonDeserializationException(String.format("Expected a JSONArray, got %s.", object.getClass()
.getName()));
throw new JsonDeserializationException(String.format("Expected a JSONArray, got %s.", object != null ? object.getClass()
.getName() : ""));
}
public static JSONObject toJSONObjectSafe(Object object) throws JsonDeserializationException {
@@ -33,7 +33,7 @@ public class WindJsonDeserializer implements JsonDeserializer<Wind> {
direction = (Number) object.get("bearing");
}
Number speedInKnots = (Number) object.get(WindJsonSerializer.FIELD_SPEED_IN_KNOTS);
Bearing degreeBearing = new DegreeBearingImpl(direction.doubleValue());
Bearing degreeBearing = new DegreeBearingImpl(direction.doubleValue());
SpeedWithBearing speedBearing = new KnotSpeedWithBearingImpl(speedInKnots.doubleValue(), degreeBearing);
Wind wind = new WindImpl(position, new MillisecondsTimePoint(timeStamp.longValue()), speedBearing);
return wind;
@@ -11,14 +11,14 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseDataDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseBaseDeserializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogCourseDesignChangedEventSerializer;
public class RaceLogCourseDesignChangedEventDeserializer extends BaseRaceLogEventDeserializer {
private final CourseDataDeserializer courseDataDeserializer;
private final CourseBaseDeserializer courseDataDeserializer;
public RaceLogCourseDesignChangedEventDeserializer(JsonDeserializer<Competitor> competitorDeserializer, CourseDataDeserializer courseDataDeserializer) {
public RaceLogCourseDesignChangedEventDeserializer(JsonDeserializer<Competitor> competitorDeserializer, CourseBaseDeserializer courseDataDeserializer) {
super(competitorDeserializer);
this.courseDataDeserializer = courseDataDeserializer;
}
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.ControlPointDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseDataDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseBaseDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.GateDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.MarkDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.WaypointDeserializer;
@@ -40,7 +40,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
new RaceLogRaceStatusEventDeserializer(competitorDeserializer),
new RaceLogCourseAreaChangedEventDeserializer(competitorDeserializer),
new RaceLogCourseDesignChangedEventDeserializer(competitorDeserializer,
new CourseDataDeserializer(
new CourseBaseDeserializer(
new WaypointDeserializer(
new ControlPointDeserializer(
new MarkDeserializer(domainFactory), new GateDeserializer(domainFactory, new MarkDeserializer(domainFactory)))))),
@@ -28,7 +28,7 @@ public class RaceLogSerializer implements JsonSerializer<RaceLog> {
events.add(itemSerializer.serialize(event));
}
result.put(FIELD_EVENTS, events);
result.put(FIELD_RACELOG_IDENTIFIER, object.getId());
result.put(FIELD_RACELOG_IDENTIFIER, object.getId().toString());
} finally {
object.unlockAfterRead();
}
@@ -9,7 +9,7 @@ import com.sap.sailing.server.RacingEventService;
public abstract class AbstractSeriesOperation<ResultType> extends AbstractRacingEventServiceOperation<ResultType> {
private static final long serialVersionUID = -7786133902346491275L;
private Logger logger = Logger.getLogger(AbstractSeriesOperation.class.getName());
private static final Logger logger = Logger.getLogger(AbstractSeriesOperation.class.getName());
private final RegattaIdentifier regattaIdentifier;
private final String seriesName;
@@ -500,4 +500,10 @@ public class DummyTrackedRace implements TrackedRace {
// TODO Auto-generated method stub
}
@Override
public TimePoint getStartTimeReceived() {
// TODO Auto-generated method stub
return null;
}
}
@@ -19,25 +19,12 @@
<div id='mainContent'>
<h1 id='pageHeadline'>Extreme Sailing Series 2013</h1>
<h2>Next Event</h2>
<p>
Act 5 - Porto, Portugal
</p>
<p>
July 25th - 28th
</p>
<!--<p>
Act 6 - Cardiff, England
</p>
<p>
August 22th - 25th
</p>
<p>
Act 7 - Nice, France
</p>
<p>
October 3th - 6th
September 2th - 6th
</p>
-->
</div>
<div id='navigation'>
<a class='navButton overall' href='http://ess40-2013.sapsailing.com/gwt/Leaderboard.html?name=Extreme%20Sailing%20Series%202013%20Overall&amp;showRaceDetails=false&amp;refreshIntervalMillis=10000&amp;displayName=Overall%20Standings&amp;leaderboardGroupName=Extreme%20Sailing%20Series%202013&amp;root=leaderboardGroupPanel'>
@@ -48,10 +35,10 @@
</a>
<a class='navButton event' href='http://ess40-2013.sapsailing.com/gwt/Leaderboard.html?name=ESS%202013%20Nice%20%28Extreme40%29&displayName=Nice&delayToLiveMillis=3000&refreshIntervalMillis=10000&legDetail=AVERAGE_SPEED_OVER_GROUND_IN_KNOTS&legDetail=DISTANCE_TRAVELED&legDetail=RANK_GAIN&raceDetail=DISPLAY_LEGS&maneuverDetail=TACK&maneuverDetail=JIBE&maneuverDetail=PENALTY_CIRCLE&lastN=3'>
Nice
</a>
</a>-->
<a class='navButton event' href='http://ess40-2013.sapsailing.com/gwt/Leaderboard.html?name=ESS%202013%20Cardiff%20%28Extreme40%29&displayName=Cardiff&delayToLiveMillis=3000&refreshIntervalMillis=10000&legDetail=AVERAGE_SPEED_OVER_GROUND_IN_KNOTS&legDetail=DISTANCE_TRAVELED&legDetail=RANK_GAIN&raceDetail=DISPLAY_LEGS&maneuverDetail=TACK&maneuverDetail=JIBE&maneuverDetail=PENALTY_CIRCLE&lastN=3'>
Cardiff
</a>-->
</a>
<a class='navButton event' href='http://ess40-2013.sapsailing.com/gwt/Leaderboard.html?name=ESS%202013%20Porto%20%28Extreme40%29&displayName=Porto&delayToLiveMillis=3000&refreshIntervalMillis=10000&legDetail=AVERAGE_SPEED_OVER_GROUND_IN_KNOTS&legDetail=DISTANCE_TRAVELED&legDetail=RANK_GAIN&raceDetail=DISPLAY_LEGS&maneuverDetail=TACK&maneuverDetail=JIBE&maneuverDetail=PENALTY_CIRCLE&lastN=3'>
Porto
</a>
+47 -27
View File
@@ -33,22 +33,64 @@
<div class='innerContent'>
<!-- Uncomment the following in case there are events in live state and add the timeBar plus raceSection -->
<!--
<h2 class='articleSubheadline'>Live</h2>
<!--<h2 class='articleSubheadline'>Live</h2>
<br/>
-->
<div class="timeBar">
</div>-->
<!-- Uncomment the following in case there are upcoming and add the timeBar plus raceSection -->
<!--
<h2 class='articleSubheadline'>Upcoming</h2>
<div class="timeBar">
<div class='raceSection'>
<h5 class='date'>
Sep 2 - 7
</h5>
<div class='raceTimepoint'></div>
<a href='http://ess40-2013.sapsailing.com' target='_blank'>
<ul class='racePanel'>
<li class='raceName'>
Extreme Sailing Series 2013
</li>
<li class='raceLocation'>
Nice, France
</li>
</ul>
</a>
<div class='raceDescription'>
<p>Extreme Sailing Series 2013</p>
<a href='http://ess40-2013.sapsailing.com' target='_blank'>SAP Sailing Analytics</a><br/>
<a href='http://www.extremesailingseries.com' target='_blank'>Official website</a>
</div>
</div>
</div>
<br/>
-->
<h2 class='articleSubheadline'>2013</h2>
<div class="timeBar">
<div class='raceSection'>
<h5 class='date'>
Aug 23 - 26
</h5>
<div class='raceTimepoint'></div>
<a href='http://ess40-2013.sapsailing.com' target='_blank'>
<ul class='racePanel'>
<li class='raceName'>
Extreme Sailing Series 2013
</li>
<li class='raceLocation'>
Cardiff, Wales
</li>
</ul>
</a>
<div class='raceDescription'>
<p>Extreme Sailing Series 2013</p>
<a href='http://ess40-2013.sapsailing.com' target='_blank'>SAP Sailing Analytics</a><br/>
<a href='http://www.extremesailingseries.com' target='_blank'>Official website</a>
</div>
</div>
<div class='raceSection'>
<h5 class='date'>
July 19 - 28
@@ -111,28 +153,6 @@
</div>
</div>
<div class='raceSection'>
<h5 class='date'>
July 25 - 28
</h5>
<div class='raceTimepoint'></div>
<a href='http://ess40-2013.sapsailing.com' target='_blank'>
<ul class='racePanel'>
<li class='raceName'>
Extreme Sailing Series 2013
</li>
<li class='raceLocation'>
Porto, Portugal
</li>
</ul>
</a>
<div class='raceDescription'>
<p>Extreme Sailing Series 2013</p>
<a href='http://ess40-2013.sapsailing.com' target='_blank'>SAP Sailing Analytics</a><br/>
<a href='http://www.extremesailingseries.com' target='_blank'>Official website</a>
</div>
</div>
<div class='raceSection'>
<h5 class='date'>
May 1 - 5
@@ -2,7 +2,7 @@
monitor.interval = 30000
# time the monitoring server waits before polling, in milliseconds
monitor.gracetime = 10000
monitor.gracetime = 60000
# time between interval checks, in milliseconds
monitor.pause = 1000
@@ -45,7 +45,8 @@
<activity
android:name=".ui.activities.LoginActivity"
android:screenOrientation="landscape"
android:launchMode="singleTop" >
android:launchMode="standard"
android:finishOnTaskLaunch="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

@@ -2,10 +2,15 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory android:title="@string/settings_category_general" >
<Preference
android:key="languagePref"
android:title="@string/settings_general_language_header"
android:summary="@string/settings_general_language_description"/>
<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>
@@ -16,7 +16,7 @@
<TextView android:id="@+id/Welter.Cell.OneRowNoImage.txtTitle"
android:padding="5dp"
android:textColor="@drawable/text_selector"
android:textSize="10pt"
android:textSize="@dimen/Welter.BigTitleTextSize"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true">
</TextView>
@@ -15,14 +15,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:textSize="10pt" android:alpha="1" >
android:textSize="@dimen/Welter.RegularTextSize" android:alpha="1" >
</TextView>
<ImageButton android:id="@+id/Welter.Cell.OneRowNoImageLabel.btnJury"
android:src="@drawable/bravo_mini"
android:padding="5dp"
android:textColor="@color/welter_red"
android:textSize="10pt"
android:textSize="@dimen/Welter.RegularTextSize"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:contentDescription="@string/flag_jury"
android:layout_centerVertical="true" android:layout_alignParentRight="true" />
@@ -17,9 +17,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Title of the first line"
android:text="@string/empty"
android:textColor="@drawable/text_selector"
android:textSize="9pt" >
android:textSize="@dimen/Welter.RegularTextSize" >
</TextView>
<TextView
@@ -30,8 +30,8 @@
android:paddingLeft="6pt"
android:layout_below="@+id/Welter.Cell.TwoRowNoImage.lineOne"
android:layout_centerVertical="true"
android:text="Title of the second line"
android:textSize="8pt" >
android:text="@string/empty"
android:textSize="@dimen/Welter.ProgressTextSize" >
</TextView>
</RelativeLayout>
@@ -2,139 +2,117 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="@drawable/background"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true"
android:background="@drawable/background" >
android:focusableInTouchMode="true"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="18dp"
android:background="@color/welter_extralight_gray_transparent">
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="18dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_wind_direction"
android:textAppearance="@style/TextAppearance.Welter.BigTitleLabel" />
<com.sap.sailing.racecommittee.app.ui.views.CompassView
android:id="@+id/compassView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</com.sap.sailing.racecommittee.app.ui.views.CompassView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_wind_speed"
android:textAppearance="@style/TextAppearance.Welter.BigTitleLabel" />
<SeekBar
android:id="@+id/seekbar_wind_speed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="100"
android:max="250"
android:padding="18dp" />
</LinearLayout>
<LinearLayout
android:layout_width="2dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="18dp"
android:background="@color/welter_deep_gray">
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="18dp"
android:layout_marginRight="26dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="9dp"
android:text="@string/check_all_data"
android:textAppearance="@style/TextAppearance.Welter.BigTitleLabel" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/wind_direction_degrees" />
<EditText
android:id="@+id/editTextWindDirection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:hint="@string/in_degrees"
android:minWidth="200dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/wind_speed_knots" />
<EditText
android:id="@+id/editTextWindSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:hint="@string/in_knots"
android:minWidth="200dp">
</EditText>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/your_position" />
<EditText
android:id="@+id/et_position_lat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/latitude"
android:minWidth="200dp"
android:clickable="false"
android:cursorVisible="false"
android:focusable="false"
android:focusableInTouchMode="false">
</EditText>
<EditText
android:id="@+id/et_position_lon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/longitude"
android:minWidth="200dp"
android:clickable="false"
android:cursorVisible="false"
android:focusable="false"
android:focusableInTouchMode="false">
</EditText>
<Button
android:id="@+id/btn_wind_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/store_wind"
android:layout_gravity="center"/>
</LinearLayout>
</LinearLayout>
android:background="@color/welter_extralight_gray_transparent"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="18dp"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_wind_direction"
android:textAppearance="@style/TextAppearance.Welter.BigTitleLabel" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/wind_direction_degrees" />
<EditText
android:id="@+id/editTextWindDirection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/in_degrees"
android:inputType="numberDecimal"
android:minWidth="200dp"
android:textSize="24sp" />
<com.sap.sailing.racecommittee.app.ui.views.CompassView
android:id="@+id/compassView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</com.sap.sailing.racecommittee.app.ui.views.CompassView>
</LinearLayout>
<LinearLayout
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_margin="18dp"
android:background="@color/welter_deep_gray"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="18dp"
android:layout_marginRight="26dp"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_wind_speed"
android:textAppearance="@style/TextAppearance.Welter.BigTitleLabel" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/wind_speed_knots" />
<EditText
android:id="@+id/editTextWindSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/in_knots"
android:inputType="numberDecimal"
android:minWidth="200dp"
android:textSize="24sp" />
<SeekBar
android:id="@+id/seekbar_wind_speed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="500"
android:padding="10dp"
android:progress="100" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="9dp"
android:text="@string/check_all_data"
android:textAppearance="@style/TextAppearance.Welter.BigTitleLabel" />
<TextView
android:id="@+id/textWaitingForGPS"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/waiting_for_gps"
android:textAppearance="@style/TextAppearance.Welter.TitleLabel"
android:textColor="@color/welter_red"
android:textSize="24sp" />
<Button
android:id="@+id/btn_wind_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/store_wind" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
@@ -6,7 +6,9 @@
<string name="course_design_number_waypoints">Kurs hat %s Wegpunkte</string>
<string name="choose_running_course">Wähle Kurs aus</string>
<string name="service_info">SAP Race Committee Service</string>
<string name="service_title">Verwaltet alle Rennen auf gewählter Rennbahn.</string>
<string name="service_text_no_races">Warte auf Rennen&#8230;</string>
<string name="service_text_num_races">Aktuell %d Rennen verwaltet.</string>
<string name="app_name">SAP Race Committee Cockpit</string>
<string name="set_time">Start</string>
<string name="reset_time">Startzeit ändern</string>
@@ -179,6 +181,7 @@
<string name="wind_speed_direction_not_a_valid_number">Die Windgeschwindigkeit oder -richtung ist keine valide Zahl. Bitte korrigieren Sie den Wert.</string>
<string name="wind_location_or_fields_not_valid">Die eingegebene Position, Windgeschwindigkeit oder -richtung ist leer. Bitte korrigieren Sie die Werte.</string>
<string name="your_position">Aktuelle Position</string>
<string name="waiting_for_gps">Warte auf GPS Signal&#8230;</string>
<string name="store_wind_entry">4. Windeingaben speichern</string>
<string name="store_wind">Windeingaben speichern</string>
@@ -186,6 +189,8 @@
<string name="settings_category_general">Allgemeine Einstellungen</string>
<string name="settings_general_manageareas_header">Verwaltete Rennbahnen</string>
<string name="settings_general_manageareas_description">Verwaltete Rennbahnen einstellen (Komma-getrennt)</string>
<string name="settings_general_language_header">Sprache auswählen</string>
<string name="settings_general_language_description">Sprache auswählen. Eventuell muss die App neugestartet werden.</string>
<string name="settings_category_race">Renneinstellungen</string>
<string name="override_default_start_procedure_type_header">Standard Startprozedur überschreiben</string>
@@ -196,7 +201,7 @@
<string name="settings_time_frame_description">Zeit in Minuten einstellen, welches Zeitlimit angibt</string>
<string name="settings_category_advanced">Erweiterte Einstellungen</string>
<string name="settings_advanced_service_header">Wählen sie die Server URL aus.</string>
<string name="settings_advanced_service_header">Wählen sie die Server URL aus. Nach Änderung muss die Option \'Neuladen\' gewählt werden.</string>
<string name="settings_advanced_service_description">Webservice URL einstellen.</string>
<string name="settings_advanced_mail_description">Empfänger für Bilder der Ergebnislisten.</string>
<string name="settings_advanced_mail_header">Empfänger Ergebnislisten</string>
@@ -233,4 +238,8 @@
<string name="number">Nummer</string>
<string name="snap">Fotos schießen</string>
<string name="logout_dialog_title">Möchten Sie sich abmelden?</string>
<string name="logout_dialog_message">Wenn Sie sich abmelden wird kein Rennen mehr verwaltet.</string>
<string name="logout">Abmelden</string>
</resources>
@@ -6,7 +6,9 @@
<string name="course_design_number_waypoints">Course has %s waypoints</string>
<string name="choose_running_course">Choose running course</string>
<string name="service_info">SAP Race Committee Service</string>
<string name="service_title">Monitoring all races on selected event course.</string>
<string name="service_text_no_races">Waiting for races to monitor&#8230;</string>
<string name="service_text_num_races">Currently monitoring %d races.</string>
<string name="app_name">SAP Race Committee Cockpit</string>
<string name="set_time">Start</string>
<string name="reset_time">Restart</string>
@@ -167,7 +169,7 @@
<string name="results_mail_subject">[Race Committee] %s</string>
<string name="results_mail_body">Results for race %s are attached.</string>
<string name="wind_direction_degrees">Wind direction (degrees)</string>
<string name="wind_direction_degrees">Wind direction from (degrees)</string>
<string name="wind_speed_knots">Wind speed (knots)</string>
<string name="enter_wind_direction">1. Enter the wind direction</string>
<string name="enter_wind_speed">2. Enter the wind speed</string>
@@ -178,7 +180,7 @@
<string name="longitude">Longitude</string>
<string name="wind_speed_direction_not_a_valid_number">The wind speed or direction is not a valid number. Please correct the value.</string>
<string name="wind_location_or_fields_not_valid">The entered location, wind speed or direction is empty. Please correct the values.</string>
<string name="your_position">Your position</string>
<string name="waiting_for_gps">Waiting for GPS Signal&#8230;</string>
<string name="store_wind_entry">4. Store wind entry</string>
<string name="store_wind">Store wind</string>
@@ -186,6 +188,8 @@
<string name="settings_category_general">General Settings</string>
<string name="settings_general_manageareas_header">Managed Course Areas</string>
<string name="settings_general_manageareas_description">Enter course area names managed by you (comma-separated)</string>
<string name="settings_general_language_header">Select Language</string>
<string name="settings_general_language_description">Select the language. You might need to restart the app to have changes applied.</string>
<string name="settings_category_race">Race Settings</string>
<string name="override_default_start_procedure_type_header">Override default start procedure type</string>
@@ -262,4 +266,8 @@
<string name="confirm_positionings">Confirm positionings</string>
<string name="snap">Snap</string>
<string name="logout_dialog_title">Are you sure want to logout?</string>
<string name="logout_dialog_message">Logging out stops automatic monitoring for all races.</string>
<string name="logout">Logout</string>
</resources>
@@ -6,6 +6,7 @@ import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.SetStartTimeRaceF
public class AppConstants {
public static final boolean IS_DATA_OFFLINE = false;
public static final boolean ENABLE_LIFECYCLE_LOGGING = false;
// Intent extra fields
public final static String COURSE_AREA_UUID_KEY = "courseUuid";
@@ -109,13 +109,13 @@ public class AppPreferences {
return Arrays.asList(managedCourseAreas);
}
public static double getWindBearing(Context context) {
public static double getWindBearingFromDirection(Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
long windBearingAsLong = sp.getLong(PREFERENCE_WIND_BEARING, 0);
return Double.longBitsToDouble(windBearingAsLong);
}
public static void setWindBearing(Context context, double enteredWindBearing) {
public static void setWindBearingFromDirection(Context context, double enteredWindBearing) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
long windBearingAsLong = Double.doubleToLongBits(enteredWindBearing);
sp.edit().putLong(PREFERENCE_WIND_BEARING, windBearingAsLong).apply();
@@ -4,13 +4,14 @@ import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.ref.WeakReference;
import java.util.Date;
import com.sap.sailing.racecommittee.app.logging.ExLog;
import com.sap.sailing.racecommittee.app.logging.FileLoggingTask;
import android.app.Application;
import android.content.Context;
import android.util.Log;
import com.sap.sailing.racecommittee.app.logging.ExLog;
import com.sap.sailing.racecommittee.app.logging.FileLoggingTask;
import com.sap.sailing.racecommittee.app.logging.LifecycleLogger;
/**
* Register a additional exception handler for uncaught exception to have some crash logging.
*/
@@ -27,12 +28,12 @@ public class RaceApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ExLog.i(TAG, "Application is starting");
Thread.setDefaultUncaughtExceptionHandler(new LoggingExceptionHandler(Thread
.getDefaultUncaughtExceptionHandler()));
LifecycleLogger.enableLifecycleLogging(AppConstants.ENABLE_LIFECYCLE_LOGGING);
stringContext = new StringContext(new WeakReference<Context>(getApplicationContext()));
}
@@ -1,21 +1,21 @@
package com.sap.sailing.racecommittee.app.data;
public class DataLoadingException extends Exception {
private static final long serialVersionUID = 4787160523984332591L;
private static final long serialVersionUID = 4787160523984332591L;
public DataLoadingException() {
}
public DataLoadingException() {
}
public DataLoadingException(String detailMessage) {
super(detailMessage);
}
public DataLoadingException(String detailMessage) {
super(detailMessage);
}
public DataLoadingException(Throwable throwable) {
super(throwable);
}
public DataLoadingException(Throwable throwable) {
super(throwable);
}
public DataLoadingException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
public DataLoadingException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
}
@@ -4,18 +4,23 @@ import com.sap.sailing.racecommittee.app.AppConstants;
import android.content.Context;
/**
* Base class for all data managers. Use {@link DataManager#create(Context)} for creating your {@link DataManager}.
*/
public abstract class DataManager implements ReadonlyDataManager {
public static ReadonlyDataManager create(Context context) {
if (AppConstants.IS_DATA_OFFLINE) {
return new OfflineDataManager(InMemoryDataStore.INSTANCE);
return new OfflineDataManager(context, InMemoryDataStore.INSTANCE);
}
return new OnlineDataManager(context, InMemoryDataStore.INSTANCE);
}
protected Context context;
protected DataStore dataStore;
public DataManager(DataStore dataStore) {
public DataManager(Context context, DataStore dataStore) {
this.context = context;
this.dataStore = dataStore;
}
@@ -23,4 +28,8 @@ public abstract class DataManager implements ReadonlyDataManager {
return dataStore;
}
public Context getContext() {
return context;
}
}
@@ -7,6 +7,10 @@ import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseArea;
@@ -31,6 +35,8 @@ import com.sap.sailing.domain.racelog.RaceLogEventFactory;
import com.sap.sailing.domain.racelog.impl.RaceLogEventFactoryImpl;
import com.sap.sailing.domain.racelog.impl.RaceLogImpl;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
import com.sap.sailing.racecommittee.app.data.loaders.DataLoaderResult;
import com.sap.sailing.racecommittee.app.data.loaders.ImmediateDataLoaderCallbacks;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.impl.ManagedRaceIdentifierImpl;
import com.sap.sailing.racecommittee.app.domain.impl.ManagedRaceImpl;
@@ -39,8 +45,8 @@ public class OfflineDataManager extends DataManager {
private static boolean isInitialized = false;
public OfflineDataManager(DataStore dataStore) {
super(dataStore);
public OfflineDataManager(Context context, DataStore dataStore) {
super(context, dataStore);
if (!isInitialized) {
isInitialized = true;
@@ -49,7 +55,8 @@ public class OfflineDataManager extends DataManager {
}
private void fillDataStore(DataStore dataStore) {
dataStore.addEvent(new EventBaseImpl("Extreme Sailing Series 2012 (Cardiff)", "Cardiff", "", true, "DUMBUUIDA"));
dataStore
.addEvent(new EventBaseImpl("Extreme Sailing Series 2012 (Cardiff)", "Cardiff", "", true, "DUMBUUIDA"));
dataStore.addEvent(new EventBaseImpl("Extreme Sailing Series 2012 (Nice)", "Nice", "", true, "DUMBUUIDB"));
dataStore.addEvent(new EventBaseImpl("Extreme Sailing Series 2012 (Rio)", "Rio", "", true, "DUMBUUIDC"));
EventBase newEvent = new EventBaseImpl("Extreme Sailing Series 2013 (Muscat)", "Muscat", "", true, "FIXUUID");
@@ -59,12 +66,9 @@ public class OfflineDataManager extends DataManager {
SeriesWithRows qualifying = new SeriesWithRowsImpl("Qualifying", false, null);
SeriesWithRows medal = new SeriesWithRowsImpl("Medal", true, null);
RaceGroup raceGroup = new RaceGroupImpl(
"ESS",
new BoatClassImpl("X40", false),
null,
Arrays.asList(qualifying, medal));
RaceGroup raceGroup = new RaceGroupImpl("ESS", new BoatClassImpl("X40", false), null, Arrays.asList(qualifying,
medal));
List<Competitor> competitors = new ArrayList<Competitor>();
competitors.add(new CompetitorImpl(UUID.randomUUID(), "SAP Extreme Sailing Team", null, null));
competitors.add(new CompetitorImpl(UUID.randomUUID(), "The Wave Muscat", null, null));
@@ -74,103 +78,117 @@ public class OfflineDataManager extends DataManager {
RaceLogEventFactory factory = new RaceLogEventFactoryImpl();
RaceLog log = new RaceLogImpl(UUID.randomUUID());
log.add(factory.createStartTimeEvent(
new MillisecondsTimePoint(new Date().getTime() - 2000),
1,
log.add(factory.createStartTimeEvent(new MillisecondsTimePoint(new Date().getTime() - 2000), 1,
new MillisecondsTimePoint(new Date().getTime() - 1000)));
log.add(factory.createRaceStatusEvent(
new MillisecondsTimePoint(new Date().getTime()),
1,
log.add(factory.createRaceStatusEvent(new MillisecondsTimePoint(new Date().getTime()), 1,
RaceLogRaceStatus.FINISHING));
ManagedRace q1 = new ManagedRaceImpl(
new ManagedRaceIdentifierImpl(
"Q1",
new FleetImpl("Default"),
qualifying,
raceGroup),
StartProcedureType.ESS,
log);
ManagedRace q1 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("A.B", new FleetImpl("A"), qualifying,
raceGroup), StartProcedureType.ESS, log);
log = new RaceLogImpl(UUID.randomUUID());
/*log.add(factory.createStartTimeEvent(
new MillisecondsTimePoint(new Date()),
1,
RaceLogRaceStatus.SCHEDULED,
new MillisecondsTimePoint(new Date().getTime() + 100000)));
/*
* log.add(factory.createStartTimeEvent( new MillisecondsTimePoint(new Date()), 1, RaceLogRaceStatus.SCHEDULED,
* new MillisecondsTimePoint(new Date().getTime() + 100000)));
*/
ManagedRace q2 = new ManagedRaceImpl(
new ManagedRaceIdentifierImpl(
"Q2",
new FleetImpl("Default"),
qualifying,
raceGroup),
StartProcedureType.ESS,
log);
ManagedRace q2 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("B", new FleetImpl("A.A"), qualifying,
raceGroup), StartProcedureType.ESS, log);
log = new RaceLogImpl(UUID.randomUUID());
/*log.add(factory.createRaceStatusEvent(
new MillisecondsTimePoint(new Date()),
5,
RaceLogRaceStatus.FINISHED));*/
ManagedRace q3 = new ManagedRaceImpl(
new ManagedRaceIdentifierImpl(
"Q3",
new FleetImpl("Default"),
qualifying,
raceGroup),
StartProcedureType.ESS,
log);
/*ManagedRace m1 = new ManagedRaceImpl(
new ManagedRaceIdentifierImpl(
"M1",
new FleetImpl("Default"),
medal,
raceGroup),
null);*/
/*
* log.add(factory.createRaceStatusEvent( new MillisecondsTimePoint(new Date()), 5,
* RaceLogRaceStatus.FINISHED));
*/
ManagedRace q3 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("Q3", new FleetImpl("Default"), qualifying,
raceGroup), StartProcedureType.ESS, log);
/*
* ManagedRace m1 = new ManagedRaceImpl( new ManagedRaceIdentifierImpl( "M1", new FleetImpl("Default"), medal,
* raceGroup), null);
*/
dataStore.addRace(q1);
dataStore.addRace(q2);
dataStore.addRace(q3);
//dataStore.addRace(m1);
// dataStore.addRace(m1);
Mark m1 = new MarkImpl("Red");
Mark m2 = new MarkImpl("Green");
Mark m3 = new MarkImpl("White");
dataStore.addMark(m1);
dataStore.addMark(m2);
dataStore.addMark(m3);
}
public void loadEvents(LoadClient<Collection<EventBase>> client) {
client.onLoadSucceded(dataStore.getEvents());
}
public void loadCourseAreas(Serializable parentEventId,
LoadClient<Collection<CourseArea>> client) {
client.onLoadSucceded(dataStore.getCourseAreas(dataStore.getEvent(parentEventId)));
}
public void loadRaces(Serializable courseAreaId,
LoadClient<Collection<ManagedRace>> client) {
client.onLoadSucceded(dataStore.getRaces());
@Override
public LoaderCallbacks<DataLoaderResult<Collection<EventBase>>> createEventsLoader(
LoadClient<Collection<EventBase>> callback) {
return new ImmediateDataLoaderCallbacks<Collection<EventBase>>(context, callback,
new Callable<Collection<EventBase>>() {
@Override
public Collection<EventBase> call() throws Exception {
return dataStore.getEvents();
}
});
}
@Override
public void loadMarks(ManagedRace managedRace, LoadClient<Collection<Mark>> client) {
client.onLoadSucceded(dataStore.getMarks());
public LoaderCallbacks<DataLoaderResult<Collection<CourseArea>>> createCourseAreasLoader(
final Serializable parentEventId, LoadClient<Collection<CourseArea>> callback) {
return new ImmediateDataLoaderCallbacks<Collection<CourseArea>>(context, callback,
new Callable<Collection<CourseArea>>() {
@Override
public Collection<CourseArea> call() throws Exception {
return dataStore.getCourseAreas(dataStore.getEvent(parentEventId));
}
});
}
@Override
public void loadCourse(ManagedRace managedRace, LoadClient<CourseBase> client) {
client.onLoadSucceded(managedRace.getCourseOnServer());
public LoaderCallbacks<DataLoaderResult<Collection<ManagedRace>>> createRacesLoader(Serializable courseAreaId,
LoadClient<Collection<ManagedRace>> callback) {
return new ImmediateDataLoaderCallbacks<Collection<ManagedRace>>(context, callback,
new Callable<Collection<ManagedRace>>() {
@Override
public Collection<ManagedRace> call() throws Exception {
return dataStore.getRaces();
}
});
}
@Override
public void loadCompetitors(ManagedRace managedRace, LoadClient<Collection<Competitor>> client) {
client.onLoadSucceded((Collection<Competitor>) managedRace.getCompetitors());
public LoaderCallbacks<DataLoaderResult<Collection<Mark>>> createMarksLoader(ManagedRace managedRace,
LoadClient<Collection<Mark>> callback) {
return new ImmediateDataLoaderCallbacks<Collection<Mark>>(context, callback, new Callable<Collection<Mark>>() {
@Override
public Collection<Mark> call() throws Exception {
return dataStore.getMarks();
}
});
}
@Override
public LoaderCallbacks<DataLoaderResult<CourseBase>> createCourseLoader(final ManagedRace managedRace,
LoadClient<CourseBase> callback) {
return new ImmediateDataLoaderCallbacks<CourseBase>(context, callback, new Callable<CourseBase>() {
@Override
public CourseBase call() throws Exception {
return managedRace.getCourseOnServer();
}
});
}
@Override
public LoaderCallbacks<DataLoaderResult<Collection<Competitor>>> createCompetitorsLoader(
final ManagedRace managedRace, LoadClient<Collection<Competitor>> callback) {
return new ImmediateDataLoaderCallbacks<Collection<Competitor>>(context, callback,
new Callable<Collection<Competitor>>() {
@Override
public Collection<Competitor> call() throws Exception {
return managedRace.getCompetitors();
}
});
}
}
@@ -1,13 +1,15 @@
package com.sap.sailing.racecommittee.app.data;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.concurrent.Callable;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.Loader;
import android.os.Bundle;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
@@ -19,14 +21,18 @@ import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.racecommittee.app.AppPreferences;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
import com.sap.sailing.racecommittee.app.data.handlers.CompetitorsDataHandler;
import com.sap.sailing.racecommittee.app.data.handlers.CourseDataHandler;
import com.sap.sailing.racecommittee.app.data.handlers.CourseBaseHandler;
import com.sap.sailing.racecommittee.app.data.handlers.DataHandler;
import com.sap.sailing.racecommittee.app.data.handlers.EventsDataHandler;
import com.sap.sailing.racecommittee.app.data.handlers.ManagedRacesDataHandler;
import com.sap.sailing.racecommittee.app.data.handlers.MarksDataHandler;
import com.sap.sailing.racecommittee.app.data.loaders.DataLoader;
import com.sap.sailing.racecommittee.app.data.loaders.DataLoaderCallbacks;
import com.sap.sailing.racecommittee.app.data.loaders.DataLoaderCallbacks.LoaderCreator;
import com.sap.sailing.racecommittee.app.data.loaders.DataLoaderResult;
import com.sap.sailing.racecommittee.app.data.loaders.ImmediateDataLoaderCallbacks;
import com.sap.sailing.racecommittee.app.data.loaders.OnlineDataLoader;
import com.sap.sailing.racecommittee.app.data.parsers.CompetitorsDataParser;
import com.sap.sailing.racecommittee.app.data.parsers.CourseDataParser;
import com.sap.sailing.racecommittee.app.data.parsers.CourseBaseParser;
import com.sap.sailing.racecommittee.app.data.parsers.DataParser;
import com.sap.sailing.racecommittee.app.data.parsers.EventsDataParser;
import com.sap.sailing.racecommittee.app.data.parsers.ManagedRacesDataParser;
@@ -34,9 +40,10 @@ import com.sap.sailing.racecommittee.app.data.parsers.MarksDataParser;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier;
import com.sap.sailing.racecommittee.app.domain.impl.DomainFactoryImpl;
import com.sap.sailing.racecommittee.app.logging.ExLog;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.ControlPointDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseDataDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseBaseDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.GateDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.MarkDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.WaypointDeserializer;
@@ -58,25 +65,10 @@ import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogEventD
* Enables accessing of data.
*/
public class OnlineDataManager extends DataManager {
// private static final String TAG = OnlineDataManager.class.getName();
private Context context;
private static final String TAG = OnlineDataManager.class.getName();
OnlineDataManager(Context context, DataStore dataStore) {
super(dataStore);
this.context = context;
}
public Context getContext() {
return context;
}
public void loadEvents(LoadClient<Collection<EventBase>> client) {
if (dataStore.getEvents().isEmpty()) {
reloadEvents(client);
} else {
client.onLoadSucceded(dataStore.getEvents());
}
super(context, dataStore);
}
public void addEvents(Collection<EventBase> events) {
@@ -85,160 +77,155 @@ public class OnlineDataManager extends DataManager {
}
}
protected void reloadEvents(LoadClient<Collection<EventBase>> client) {
SharedDomainFactory domainFactory = DomainFactoryImpl.INSTANCE;
DataParser<Collection<EventBase>> parser = new EventsDataParser(new EventBaseJsonDeserializer(
new VenueJsonDeserializer(new CourseAreaJsonDeserializer(domainFactory))));
DataHandler<Collection<EventBase>> handler = new EventsDataHandler(this, client);
try {
new DataLoader<Collection<EventBase>>(context, URI.create(AppPreferences.getServerBaseURL(context)
+ "/sailingserver/events"), parser, handler).forceLoad();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void loadCourseAreas(final Serializable parentEventId, final LoadClient<Collection<CourseArea>> client) {
if (dataStore.hasEvent(parentEventId)) {
EventBase event = dataStore.getEvent(parentEventId);
client.onLoadSucceded(dataStore.getCourseAreas(event));
} else {
reloadEvents(new LoadClient<Collection<EventBase>>() {
public void onLoadSucceded(Collection<EventBase> data) {
if (dataStore.hasEvent(parentEventId)) {
EventBase event = dataStore.getEvent(parentEventId);
client.onLoadSucceded(dataStore.getCourseAreas(event));
} else {
client.onLoadFailed(new DataLoadingException(String.format(
"There was no event object found for id %s.", parentEventId)));
}
}
public void onLoadFailed(Exception reason) {
client.onLoadFailed(new DataLoadingException(
String.format(
"There was no event object found for id %s. While reloading the events an error occured: %s",
parentEventId, reason), reason));
}
});
}
}
public void addRaces(Collection<ManagedRace> data) {
for (ManagedRace race : data) {
dataStore.addRace(race);
}
}
public void loadRaces(Serializable courseAreaId, LoadClient<Collection<ManagedRace>> client) {
if (!dataStore.hasCourseArea(courseAreaId)) {
client.onLoadFailed(new DataLoadingException(String.format("No course area found with id %s", courseAreaId)));
return;
}
SharedDomainFactory domainFactory = DomainFactoryImpl.INSTANCE;
JsonDeserializer<BoatClass> boatClassDeserializer = new BoatClassJsonDeserializer(domainFactory);
DataParser<Collection<ManagedRace>> parser = new ManagedRacesDataParser(context, new RaceGroupDeserializer(
boatClassDeserializer, new SeriesWithRowsDeserializer(new RaceRowDeserializer(new FleetDeserializer(
new ColorDeserializer()), new RaceCellDeserializer(
new RaceLogDeserializer(RaceLogEventDeserializer.create(domainFactory)))))));
DataHandler<Collection<ManagedRace>> handler = new ManagedRacesDataHandler(this, client);
try {
new DataLoader<Collection<ManagedRace>>(context, URI.create(AppPreferences.getServerBaseURL(context)
+ "/sailingserver/rc/racegroups?courseArea=" + courseAreaId.toString()), parser, handler)
.forceLoad();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void addMarks(Collection<Mark> marks) {
for (Mark mark : marks) {
dataStore.addMark(mark);
}
}
public void loadMarks(ManagedRace managedRace, LoadClient<Collection<Mark>> client) {
SharedDomainFactory domainFactory = DomainFactoryImpl.INSTANCE;
JsonDeserializer<Mark> markDeserializer = new MarkDeserializer(domainFactory);
DataParser<Collection<Mark>> parser = new MarksDataParser(markDeserializer);
DataHandler<Collection<Mark>> handler = new MarksDataHandler(this, client);
ManagedRaceIdentifier identifier = managedRace.getIdentifier();
String raceGroupName = URLEncoder.encode(identifier.getRaceGroup().getName());
String raceColumnName = URLEncoder.encode(identifier.getRaceName());
String fleetName = URLEncoder.encode(identifier.getFleet().getName());
try {
new DataLoader<Collection<Mark>>(context, URI.create(AppPreferences.getServerBaseURL(context)
+ "/sailingserver/rc/marks?leaderboard=" + raceGroupName + "&raceColumn=" + raceColumnName
+ "&fleet=" + fleetName), parser, handler)
.forceLoad();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
@Override
public LoaderCallbacks<DataLoaderResult<Collection<EventBase>>> createEventsLoader(
LoadClient<Collection<EventBase>> callback) {
return new DataLoaderCallbacks<Collection<EventBase>>(callback, new LoaderCreator<Collection<EventBase>>() {
@Override
public Loader<DataLoaderResult<Collection<EventBase>>> create(int id, Bundle args) throws Exception {
SharedDomainFactory domainFactory = DomainFactoryImpl.INSTANCE;
DataParser<Collection<EventBase>> parser = new EventsDataParser(new EventBaseJsonDeserializer(
new VenueJsonDeserializer(new CourseAreaJsonDeserializer(domainFactory))));
DataHandler<Collection<EventBase>> handler = new EventsDataHandler(OnlineDataManager.this);
ExLog.i(TAG, "getEventsLoader created new loader...");
return new OnlineDataLoader<Collection<EventBase>>(context, new URL(AppPreferences
.getServerBaseURL(context) + "/sailingserver/events"), parser, handler);
}
});
}
@Override
public void loadCourse(ManagedRace managedRace, LoadClient<CourseBase> client) {
SharedDomainFactory domainFactory = DomainFactoryImpl.INSTANCE;
JsonDeserializer<CourseBase> courseBaseDeserializer = new CourseDataDeserializer(
new WaypointDeserializer(
new ControlPointDeserializer(
new MarkDeserializer(domainFactory),
new GateDeserializer(domainFactory,
new MarkDeserializer(domainFactory)))));
DataParser<CourseBase> parser = new CourseDataParser(courseBaseDeserializer);
DataHandler<CourseBase> handler = new CourseDataHandler(this, client, managedRace);
ManagedRaceIdentifier identifier = managedRace.getIdentifier();
String raceGroupName = URLEncoder.encode(identifier.getRaceGroup().getName());
String raceColumnName = URLEncoder.encode(identifier.getRaceName());
String fleetName = URLEncoder.encode(identifier.getFleet().getName());
try {
new DataLoader<CourseBase>(context, URI.create(AppPreferences.getServerBaseURL(context)
+ "/sailingserver/rc/currentcourse?leaderboard=" + raceGroupName + "&raceColumn=" + raceColumnName
+ "&fleet=" + fleetName), parser, handler)
.forceLoad();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
public LoaderCallbacks<DataLoaderResult<Collection<CourseArea>>> createCourseAreasLoader(
final Serializable parentEventId, LoadClient<Collection<CourseArea>> callback) {
return new ImmediateDataLoaderCallbacks<Collection<CourseArea>>(context, callback,
new Callable<Collection<CourseArea>>() {
@Override
public Collection<CourseArea> call() throws Exception {
if (dataStore.hasEvent(parentEventId)) {
EventBase event = dataStore.getEvent(parentEventId);
return dataStore.getCourseAreas(event);
} else {
throw new DataLoadingException(String.format(
"Unable to load course areas for unknown event %s.", parentEventId));
}
}
});
}
@Override
public void loadCompetitors(ManagedRace managedRace, LoadClient<Collection<Competitor>> client) {
SharedDomainFactory domainFactory = DomainFactoryImpl.INSTANCE;
JsonDeserializer<Competitor> competitorDeserializer = new CompetitorDeserializer(domainFactory);
DataParser<Collection<Competitor>> parser = new CompetitorsDataParser(competitorDeserializer);
DataHandler<Collection<Competitor>> handler = new CompetitorsDataHandler(this, client, managedRace);
ManagedRaceIdentifier identifier = managedRace.getIdentifier();
String raceGroupName = URLEncoder.encode(identifier.getRaceGroup().getName());
String raceColumnName = URLEncoder.encode(identifier.getRaceName());
String fleetName = URLEncoder.encode(identifier.getFleet().getName());
try {
new DataLoader<Collection<Competitor>>(context, URI.create(AppPreferences.getServerBaseURL(context)
+ "/sailingserver/rc/competitors?leaderboard=" + raceGroupName + "&raceColumn=" + raceColumnName
+ "&fleet=" + fleetName), parser, handler)
.forceLoad();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
public LoaderCallbacks<DataLoaderResult<Collection<ManagedRace>>> createRacesLoader(final Serializable courseAreaId,
LoadClient<Collection<ManagedRace>> callback) {
return new DataLoaderCallbacks<Collection<ManagedRace>>(callback, new LoaderCreator<Collection<ManagedRace>>() {
@Override
public Loader<DataLoaderResult<Collection<ManagedRace>>> create(int id, Bundle args) throws Exception {
SharedDomainFactory domainFactory = DomainFactoryImpl.INSTANCE;
JsonDeserializer<BoatClass> boatClassDeserializer = new BoatClassJsonDeserializer(domainFactory);
DataParser<Collection<ManagedRace>> parser = new ManagedRacesDataParser(context,
new RaceGroupDeserializer(boatClassDeserializer, new SeriesWithRowsDeserializer(
new RaceRowDeserializer(new FleetDeserializer(new ColorDeserializer()),
new RaceCellDeserializer(new RaceLogDeserializer(
RaceLogEventDeserializer.create(domainFactory)))))));
DataHandler<Collection<ManagedRace>> handler = new ManagedRacesDataHandler(OnlineDataManager.this);
return new OnlineDataLoader<Collection<ManagedRace>>(context, new URL(AppPreferences
.getServerBaseURL(context)
+ "/sailingserver/rc/racegroups?courseArea="
+ courseAreaId.toString()), parser, handler);
}
});
}
@Override
public LoaderCallbacks<DataLoaderResult<Collection<Mark>>> createMarksLoader(final ManagedRace managedRace,
LoadClient<Collection<Mark>> callback) {
return new DataLoaderCallbacks<Collection<Mark>>(callback, new LoaderCreator<Collection<Mark>>() {
@Override
public Loader<DataLoaderResult<Collection<Mark>>> create(int id, Bundle args) throws Exception {
SharedDomainFactory domainFactory = DomainFactoryImpl.INSTANCE;
JsonDeserializer<Mark> markDeserializer = new MarkDeserializer(domainFactory);
DataParser<Collection<Mark>> parser = new MarksDataParser(markDeserializer);
DataHandler<Collection<Mark>> handler = new MarksDataHandler(OnlineDataManager.this);
ManagedRaceIdentifier identifier = managedRace.getIdentifier();
String raceGroupName = URLEncoder.encode(identifier.getRaceGroup().getName());
String raceColumnName = URLEncoder.encode(identifier.getRaceName());
String fleetName = URLEncoder.encode(identifier.getFleet().getName());
return new OnlineDataLoader<Collection<Mark>>(context, new URL(AppPreferences
.getServerBaseURL(context)
+ "/sailingserver/rc/marks?leaderboard="
+ raceGroupName
+ "&raceColumn=" + raceColumnName + "&fleet=" + fleetName), parser, handler);
}
});
}
@Override
public LoaderCallbacks<DataLoaderResult<CourseBase>> createCourseLoader(final ManagedRace managedRace,
LoadClient<CourseBase> callback) {
return new DataLoaderCallbacks<CourseBase>(callback, new LoaderCreator<CourseBase>() {
@Override
public Loader<DataLoaderResult<CourseBase>> create(int id, Bundle args) throws Exception {
SharedDomainFactory domainFactory = DomainFactoryImpl.INSTANCE;
JsonDeserializer<CourseBase> courseBaseDeserializer = new CourseBaseDeserializer(
new WaypointDeserializer(new ControlPointDeserializer(new MarkDeserializer(domainFactory),
new GateDeserializer(domainFactory, new MarkDeserializer(domainFactory)))));
DataParser<CourseBase> parser = new CourseBaseParser(courseBaseDeserializer);
DataHandler<CourseBase> handler = new CourseBaseHandler(OnlineDataManager.this, managedRace);
ManagedRaceIdentifier identifier = managedRace.getIdentifier();
String raceGroupName = URLEncoder.encode(identifier.getRaceGroup().getName());
String raceColumnName = URLEncoder.encode(identifier.getRaceName());
String fleetName = URLEncoder.encode(identifier.getFleet().getName());
return new OnlineDataLoader<CourseBase>(context, new URL(AppPreferences.getServerBaseURL(context)
+ "/sailingserver/rc/currentcourse?leaderboard=" + raceGroupName + "&raceColumn="
+ raceColumnName + "&fleet=" + fleetName), parser, handler);
}
});
}
@Override
public LoaderCallbacks<DataLoaderResult<Collection<Competitor>>> createCompetitorsLoader(
final ManagedRace managedRace, LoadClient<Collection<Competitor>> callback) {
return new DataLoaderCallbacks<Collection<Competitor>>(callback, new LoaderCreator<Collection<Competitor>>() {
@Override
public Loader<DataLoaderResult<Collection<Competitor>>> create(int id, Bundle args) throws Exception {
ExLog.i(TAG, String.format("Creating Competitor-OnlineDataLoader %d", id));
SharedDomainFactory domainFactory = DomainFactoryImpl.INSTANCE;
JsonDeserializer<Competitor> competitorDeserializer = new CompetitorDeserializer(domainFactory);
DataParser<Collection<Competitor>> parser = new CompetitorsDataParser(competitorDeserializer);
DataHandler<Collection<Competitor>> handler = new CompetitorsDataHandler(OnlineDataManager.this,
managedRace);
ManagedRaceIdentifier identifier = managedRace.getIdentifier();
String raceGroupName = URLEncoder.encode(identifier.getRaceGroup().getName());
String raceColumnName = URLEncoder.encode(identifier.getRaceName());
String fleetName = URLEncoder.encode(identifier.getFleet().getName());
return new OnlineDataLoader<Collection<Competitor>>(context, new URL(AppPreferences
.getServerBaseURL(context)
+ "/sailingserver/rc/competitors?leaderboard="
+ raceGroupName
+ "&raceColumn=" + raceColumnName + "&fleet=" + fleetName), parser, handler);
}
});
}
}
@@ -3,27 +3,118 @@ package com.sap.sailing.racecommittee.app.data;
import java.io.Serializable;
import java.util.Collection;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
import com.sap.sailing.racecommittee.app.data.loaders.DataLoaderResult;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
/**
* <p>
* Interface for everything read related when working with data.
* </p>
*
* <p>
* The result values of this interface's methods are meant to be used in conjunction with the Android's
* {@link LoaderManager} facility. Everything that is loaded by this interface is stored and cached in the underlying
* {@link DataStore}.
* </p>
*
* <p>
* If there are cached results your {@link LoadClient}'s callback methods might be called twice (first cached results;
* afterwards remote results). See {@link LoadClient} for restrictions that apply to your data handling code.
* </p>
*/
public interface ReadonlyDataManager {
/**
* Gets the underlying data store.
*
* @return the {@link DataStore}.
*/
public DataStore getDataStore();
public void loadEvents(LoadClient<Collection<EventBase>> client);
/**
* Creates a new {@link LoaderCallbacks} object for loading {@link EventBase}s.
*
* @param callback
* {@link LoadClient} implementing your data handling code.
* @return {@link LoaderCallbacks} to be used in
* {@link LoaderManager#initLoader(int, android.os.Bundle, LoaderCallbacks)} or
* {@link LoaderManager#restartLoader(int, android.os.Bundle, LoaderCallbacks)}.
*/
public LoaderCallbacks<DataLoaderResult<Collection<EventBase>>> createEventsLoader(
LoadClient<Collection<EventBase>> callback);
public void loadCourseAreas(Serializable parentEventId, LoadClient<Collection<CourseArea>> client);
/**
* Creates a new {@link LoaderCallbacks} object for loading {@link CourseArea}s.
*
* @param callback
* {@link LoadClient} implementing your data handling code.
* @return {@link LoaderCallbacks} to be used in
* {@link LoaderManager#initLoader(int, android.os.Bundle, LoaderCallbacks)} or
* {@link LoaderManager#restartLoader(int, android.os.Bundle, LoaderCallbacks)}.
*/
public LoaderCallbacks<DataLoaderResult<Collection<CourseArea>>> createCourseAreasLoader(
Serializable parentEventId, LoadClient<Collection<CourseArea>> callback);
public void loadRaces(Serializable courseAreaId, LoadClient<Collection<ManagedRace>> client);
/**
* Creates a new {@link LoaderCallbacks} object for loading {@link ManagedRace}s of a specific {@link CourseArea}.
*
* @param courseAreaId
* the {@link CourseArea}'s Identifier.
* @param callback
* {@link LoadClient} implementing your data handling code.
* @return {@link LoaderCallbacks} to be used in
* {@link LoaderManager#initLoader(int, android.os.Bundle, LoaderCallbacks)} or
* {@link LoaderManager#restartLoader(int, android.os.Bundle, LoaderCallbacks)}.
*/
public LoaderCallbacks<DataLoaderResult<Collection<ManagedRace>>> createRacesLoader(Serializable courseAreaId,
LoadClient<Collection<ManagedRace>> callback);
public void loadMarks(ManagedRace managedRace, LoadClient<Collection<Mark>> client);
public void loadCourse(ManagedRace managedRace, LoadClient<CourseBase> client);
public void loadCompetitors(ManagedRace managedRace, LoadClient<Collection<Competitor>> client);
/**
* Creates a new {@link LoaderCallbacks} object for loading a race's {@link Mark}s.
*
* @param managedRace
* the {@link ManagedRace}.
* @param callback
* {@link LoadClient} implementing your data handling code.
* @return {@link LoaderCallbacks} to be used in
* {@link LoaderManager#initLoader(int, android.os.Bundle, LoaderCallbacks)} or
* {@link LoaderManager#restartLoader(int, android.os.Bundle, LoaderCallbacks)}.
*/
public LoaderCallbacks<DataLoaderResult<Collection<Mark>>> createMarksLoader(ManagedRace managedRace,
LoadClient<Collection<Mark>> callback);
/**
* Creates a new {@link LoaderCallbacks} object for loading a race's {@link CourseBase}.
*
* @param managedRace
* the {@link ManagedRace}.
* @param callback
* {@link LoadClient} implementing your data handling code.
* @return {@link LoaderCallbacks} to be used in
* {@link LoaderManager#initLoader(int, android.os.Bundle, LoaderCallbacks)} or
* {@link LoaderManager#restartLoader(int, android.os.Bundle, LoaderCallbacks)}.
*/
public LoaderCallbacks<DataLoaderResult<CourseBase>> createCourseLoader(ManagedRace managedRace,
LoadClient<CourseBase> callback);
/**
* Creates a new {@link LoaderCallbacks} object for loading {@link Competitor}s.
*
* @param callback
* {@link LoadClient} implementing your data handling code.
* @return {@link LoaderCallbacks} to be used in
* {@link LoaderManager#initLoader(int, android.os.Bundle, LoaderCallbacks)} or
* {@link LoaderManager#restartLoader(int, android.os.Bundle, LoaderCallbacks)}.
*/
public LoaderCallbacks<DataLoaderResult<Collection<Competitor>>> createCompetitorsLoader(ManagedRace managedRace,
LoadClient<Collection<Competitor>> callback);
}
@@ -1,6 +1,33 @@
package com.sap.sailing.racecommittee.app.data.clients;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Loader;
/**
* Interface to hide complexity of {@link LoaderCallbacks#onLoadFinished(Loader, Object)}. Keep in mind that all
* restrictions that apply on the LoaderCallback's method also apply to all methods of this interface.
*
* @param <T>
* loaded data type.
*/
public interface LoadClient<T> {
public void onLoadFailed(Exception reason);
public void onLoadSucceded(T data);
/**
* Called when a {@link Loader} returned a failure to its {@link LoaderCallbacks#onLoadFinished(Loader, Object)}.
*
* @param reason
* the loading has failed.
*/
public void onLoadFailed(Exception reason);
/**
* Called when a {@link Loader} returned successfully to its {@link LoaderCallbacks#onLoadFinished(Loader, Object)}.
*
* @param data
* that was loaded.
* @param isCached
* <code>true</code> if returned data is cached data.
*/
public void onLoadSucceded(T data, boolean isCached);
}
@@ -4,21 +4,29 @@ import java.util.Collection;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
public class CompetitorsDataHandler extends DataHandler<Collection<Competitor>> {
private ManagedRace race;
public CompetitorsDataHandler(OnlineDataManager manager, LoadClient<Collection<Competitor>> client, ManagedRace managedRace) {
super(manager, client);
public CompetitorsDataHandler(OnlineDataManager manager, ManagedRace managedRace) {
super(manager);
race = managedRace;
}
@Override
public boolean hasCachedResults() {
return race.getCompetitors() != null;
}
@Override
public Collection<Competitor> getCachedResults() {
return race.getCompetitors();
}
@Override
public void onLoaded(Collection<Competitor> data) {
super.onLoaded(data);
public void onResult(Collection<Competitor> data) {
race.setCompetitors(data);
}
@@ -0,0 +1,31 @@
package com.sap.sailing.racecommittee.app.data.handlers;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
public class CourseBaseHandler extends DataHandler<CourseBase> {
private ManagedRace race;
public CourseBaseHandler(OnlineDataManager manager, ManagedRace managedRace) {
super(manager);
race = managedRace;
}
@Override
public boolean hasCachedResults() {
return race.getCourseOnServer() != null;
}
@Override
public CourseBase getCachedResults() {
return race.getCourseOnServer();
}
@Override
public void onResult(CourseBase data) {
race.setCourseOnServer(data);
}
}
@@ -1,23 +0,0 @@
package com.sap.sailing.racecommittee.app.data.handlers;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
public class CourseDataHandler extends DataHandler<CourseBase> {
private ManagedRace race;
public CourseDataHandler(OnlineDataManager manager, LoadClient<CourseBase> client, ManagedRace managedRace) {
super(manager, client);
race = managedRace;
}
@Override
public void onLoaded(CourseBase data) {
super.onLoaded(data);
race.setCourseOnServer(data);
}
}
@@ -1,22 +1,21 @@
package com.sap.sailing.racecommittee.app.data.handlers;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
public abstract class DataHandler<T> {
protected OnlineDataManager manager;
protected LoadClient<T> client;
public DataHandler(OnlineDataManager manager, LoadClient<T> client) {
public DataHandler(OnlineDataManager manager) {
this.manager = manager;
this.client = client;
}
public void onLoaded(T data) {
client.onLoadSucceded(data);
public abstract void onResult(T data);
public boolean hasCachedResults() {
return false;
}
public void onFailed(Exception reason) {
client.onLoadFailed(reason);
public T getCachedResults() {
return null;
}
}
@@ -4,18 +4,26 @@ import java.util.Collection;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
public class EventsDataHandler extends DataHandler<Collection<EventBase>> {
public EventsDataHandler(OnlineDataManager manager, LoadClient<Collection<EventBase>> client) {
super(manager, client);
public EventsDataHandler(OnlineDataManager manager) {
super(manager);
}
@Override
public void onLoaded(Collection<EventBase> data) {
super.onLoaded(data);
public void onResult(Collection<EventBase> data) {
manager.addEvents(data);
}
@Override
public boolean hasCachedResults() {
return !manager.getDataStore().getEvents().isEmpty();
}
@Override
public Collection<EventBase> getCachedResults() {
return manager.getDataStore().getEvents();
}
}
@@ -3,18 +3,26 @@ package com.sap.sailing.racecommittee.app.data.handlers;
import java.util.Collection;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
public class ManagedRacesDataHandler extends DataHandler<Collection<ManagedRace>> {
public ManagedRacesDataHandler(OnlineDataManager manager, LoadClient<Collection<ManagedRace>> client) {
super(manager, client);
public ManagedRacesDataHandler(OnlineDataManager manager) {
super(manager);
}
@Override
public boolean hasCachedResults() {
return !manager.getDataStore().getRaces().isEmpty();
}
@Override
public Collection<ManagedRace> getCachedResults() {
return manager.getDataStore().getRaces();
}
@Override
public void onLoaded(Collection<ManagedRace> data) {
super.onLoaded(data);
public void onResult(Collection<ManagedRace> data) {
manager.addRaces(data);
}
@@ -4,17 +4,25 @@ import java.util.Collection;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
public class MarksDataHandler extends DataHandler<Collection<Mark>> {
public MarksDataHandler(OnlineDataManager manager, LoadClient<Collection<Mark>> client) {
super(manager, client);
public MarksDataHandler(OnlineDataManager manager) {
super(manager);
}
@Override
public boolean hasCachedResults() {
return !manager.getDataStore().getMarks().isEmpty();
}
@Override
public Collection<Mark> getCachedResults() {
return manager.getDataStore().getMarks();
}
@Override
public void onLoaded(Collection<Mark> data) {
super.onLoaded(data);
public void onResult(Collection<Mark> data) {
manager.addMarks(data);
}
@@ -4,17 +4,17 @@ import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
public class HttpGetRequest extends HttpRequest {
public HttpGetRequest(URI requestUri) throws MalformedURLException, IOException {
super(requestUri.toURL());
public HttpGetRequest(URL requestUrl) {
super(requestUrl);
}
@Override
protected InputStream execute(HttpURLConnection connection) throws IOException {
return new BufferedInputStream(connection.getInputStream());
protected BufferedInputStream execute(HttpURLConnection connection) throws IOException {
InputStream stream = connection.getInputStream();
return new BufferedInputStream(stream);
}
}
@@ -3,11 +3,9 @@ package com.sap.sailing.racecommittee.app.data.http;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
public class HttpJsonPostRequest extends HttpRequest {
@@ -15,14 +13,13 @@ public class HttpJsonPostRequest extends HttpRequest {
private String requestBody;
public HttpJsonPostRequest(URI requestUri, String body) throws MalformedURLException, IOException {
super(requestUri.toURL());
public HttpJsonPostRequest(URL requestUrl, String body) {
super(requestUrl);
this.requestBody = body;
}
@Override
protected InputStream execute(HttpURLConnection connection) throws IOException {
// connection.setDoInput(true);
protected BufferedInputStream execute(HttpURLConnection connection) throws IOException {
connection.setDoOutput(true);
connection.setChunkedStreamingMode(0);
@@ -1,5 +1,8 @@
package com.sap.sailing.racecommittee.app.data.http;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -28,25 +31,24 @@ public abstract class HttpRequest {
throw new IOException(String.format("Request response had no valid status."));
}
private HttpURLConnection connection;
private URL url;
public HttpRequest(URL url) throws IOException {
this((HttpURLConnection) url.openConnection());
public HttpRequest(URL url) {
this.url = url;
}
public HttpRequest(HttpURLConnection connection) {
this.connection = connection;
}
protected abstract InputStream execute(HttpURLConnection connection) throws IOException;
protected abstract BufferedInputStream execute(HttpURLConnection connection) throws IOException;
public InputStream execute() throws Exception {
ExLog.i(TAG, String.format("(Request %d) Executing HTTP request on %s.", this.hashCode(), connection.getURL()));
ExLog.i(TAG, String.format("(Request %d) Executing HTTP request on %s.", this.hashCode(), url));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(15000);
connection.setRequestProperty("connection", "close");
InputStream stream = null;
BufferedInputStream stream = null;
try {
stream = execute(connection);
} catch (FileNotFoundException fnfe) {
@@ -55,12 +57,21 @@ public abstract class HttpRequest {
}
validateHttpResponse(connection);
byte[] streamData = copyStream(stream);
connection.disconnect();
ExLog.i(TAG, String.format("(Request %d) HTTP request executed.", this.hashCode()));
return stream;
return new ByteArrayInputStream(streamData);
}
public void disconnect() {
connection.disconnect();
private byte[] copyStream(BufferedInputStream stream) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len;
byte[] buffer = new byte[4096];
while ((len = stream.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
return bos.toByteArray();
}
}
@@ -1,80 +0,0 @@
package com.sap.sailing.racecommittee.app.data.loaders;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URI;
import android.content.AsyncTaskLoader;
import android.content.Context;
import com.sap.sailing.racecommittee.app.data.handlers.DataHandler;
import com.sap.sailing.racecommittee.app.data.http.HttpGetRequest;
import com.sap.sailing.racecommittee.app.data.http.HttpRequest;
import com.sap.sailing.racecommittee.app.data.parsers.DataParser;
import com.sap.sailing.racecommittee.app.logging.ExLog;
public class DataLoader<T> extends AsyncTaskLoader<T> {
private static final String TAG = DataLoader.class.getName();
protected DataParser<T> dataParser;
protected DataHandler<T> dataHandler;
protected HttpRequest httpRequest;
protected volatile Exception lastException;
public DataLoader(Context context, URI httpGetUri, DataParser<T> dataParser, DataHandler<T> dataHandler)
throws MalformedURLException, IOException {
this(context, new HttpGetRequest(httpGetUri), dataParser, dataHandler);
}
public DataLoader(Context context, HttpRequest request, DataParser<T> dataParser, DataHandler<T> dataHandler) {
super(context);
this.httpRequest = request;
this.dataParser = dataParser;
this.dataHandler = dataHandler;
}
@Override
public T loadInBackground() {
lastException = null;
try {
return loadDataInBackground();
} catch (Exception e) {
lastException = e;
ExLog.e(TAG, String.format("Exception while loading data:\n%s", e.toString()));
StringWriter stringWriter = new StringWriter();
PrintWriter stream = new PrintWriter(stringWriter);
e.printStackTrace(stream);
ExLog.e(TAG, stringWriter.toString());
}
return null;
}
private T loadDataInBackground() throws Exception {
Reader reader = null;
try {
InputStream inputStream = httpRequest.execute();
reader = new InputStreamReader(inputStream);
return dataParser.parse(reader);
} finally {
if (reader != null) {
reader.close();
}
httpRequest.disconnect();
}
}
@Override
public void deliverResult(T data) {
if (data == null) {
dataHandler.onFailed(lastException);
} else {
dataHandler.onLoaded(data);
}
}
}
@@ -0,0 +1,67 @@
package com.sap.sailing.racecommittee.app.data.loaders;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Loader;
import android.os.Bundle;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
import com.sap.sailing.racecommittee.app.logging.ExLog;
/**
* <p>
* {@link LoaderCallbacks} wrapper to be used with {@link LoadClient}s.
* </p>
*
* <p>
* On result {@link DataLoaderResult#isSuccessful()} is checked. If true
* {@link LoadClient#onLoadSucceded(Object, boolean)} is called; other {@link LoadClient#onLoadFailed(Exception)} is
* called.
* </p>
*
* <p>
* There is currently no handling of a {@link Loader}'s reset.
* </p>
*
* @param <T>
* result type.
*/
public class DataLoaderCallbacks<T> implements LoaderCallbacks<DataLoaderResult<T>> {
private static String TAG = DataLoaderCallbacks.class.getName();
public interface LoaderCreator<T> {
Loader<DataLoaderResult<T>> create(int id, Bundle args) throws Exception;
}
private LoadClient<T> clientCallback;
private LoaderCreator<T> loaderCreator;
public DataLoaderCallbacks(LoadClient<T> clientCallback, LoaderCreator<T> loaderCreator) {
this.clientCallback = clientCallback;
this.loaderCreator = loaderCreator;
}
@Override
public Loader<DataLoaderResult<T>> onCreateLoader(int id, Bundle args) {
try {
return loaderCreator.create(id, args);
} catch (Exception e) {
ExLog.ex(TAG, e);
}
throw new IllegalStateException("Exception while creating a loader.");
}
@Override
public void onLoadFinished(Loader<DataLoaderResult<T>> loader, DataLoaderResult<T> result) {
if (result.isSuccessful()) {
clientCallback.onLoadSucceded(result.getResult(), result.isResultCached());
} else {
clientCallback.onLoadFailed(result.getException());
}
}
@Override
public void onLoaderReset(Loader<DataLoaderResult<T>> loader) {
// currently we ignore loader resets...
}
}
@@ -0,0 +1,54 @@
package com.sap.sailing.racecommittee.app.data.loaders;
/**
* <p>
* Wrapper class for data loading results.
* </p>
*
* <p>
* A {@link DataLoaderResult} may be successful or failed. It carries either the failure's {@link Exception} or the
* actual result.
* </p>
*
* @param <T>
* result type.
*/
public class DataLoaderResult<T> {
// private static final String TAG = DataLoaderResult.class.getName();
private final Exception exception;
private final boolean cached;
private final T result;
public DataLoaderResult(T result, boolean isCached) {
this.result = result;
this.cached = isCached;
this.exception = null;
}
public DataLoaderResult(Exception exception) {
this.exception = exception;
this.cached = false;
this.result = null;
}
public boolean isSuccessful() {
return exception == null;
}
public Exception getException() {
return exception;
}
public T getResult() {
return result;
}
public boolean isResultCached() {
if (!isSuccessful()) {
throw new IllegalStateException("Failures cannot be cached!");
}
return cached;
}
}
@@ -0,0 +1,40 @@
package com.sap.sailing.racecommittee.app.data.loaders;
import java.util.concurrent.Callable;
import android.content.Context;
import android.content.Loader;
/**
* A {@link Loader} that (immediately) returns a {@link Callable}'s result. All results are announced as non-cached.
*
* @param <T> result type.
*/
public class ImmediateDataLoader<T> extends Loader<DataLoaderResult<T>> {
private Callable<T> resultCallable;
public ImmediateDataLoader(Context context, Callable<T> resultCallable) {
super(context);
this.resultCallable = resultCallable;
}
@Override
protected void onStartLoading() {
publishResult();
}
@Override
protected void onForceLoad() {
publishResult();
}
private void publishResult() {
try {
// We act as a never caching DataLoader to ensure that it looks like remote access!
deliverResult(new DataLoaderResult<T>(resultCallable.call(), false));
} catch (Exception e) {
deliverResult(new DataLoaderResult<T>(e));
}
}
}
@@ -0,0 +1,25 @@
package com.sap.sailing.racecommittee.app.data.loaders;
import java.util.concurrent.Callable;
import android.content.Context;
import android.content.Loader;
import android.os.Bundle;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
/**
* Simple wrapper for a {@link DataLoaderCallbacks} creating an {@link ImmediateDataLoader}.
* @param <T> the result type.
*/
public class ImmediateDataLoaderCallbacks<T> extends DataLoaderCallbacks<T> {
public ImmediateDataLoaderCallbacks(final Context context, LoadClient<T> clientCallback,
final Callable<T> loadingFunc) {
super(clientCallback, new LoaderCreator<T>() {
@Override
public Loader<DataLoaderResult<T>> create(int id, Bundle args) throws Exception {
return new ImmediateDataLoader<T>(context, loadingFunc);
}
});
}
}
@@ -0,0 +1,110 @@
package com.sap.sailing.racecommittee.app.data.loaders;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.Loader;
import com.sap.sailing.racecommittee.app.data.handlers.DataHandler;
import com.sap.sailing.racecommittee.app.data.http.HttpGetRequest;
import com.sap.sailing.racecommittee.app.data.http.HttpRequest;
import com.sap.sailing.racecommittee.app.data.parsers.DataParser;
import com.sap.sailing.racecommittee.app.logging.ExLog;
/**
* <p>
* A {@link Loader} that loads data by accessing a remote resource.
* </p>
*
* <p>
* An {@link OnlineDataLoader} may return cached results as announced by its {@link DataHandler}. Call
* {@link OnlineDataLoader#forceLoad()} to ensure that the remote resource is checked for new data.
* </p>
*
* <p>
* The data returned by the remote resource is parsed by the given {@link DataParser} and may be cached through your
* implementation of {@link DataHandler#onResult(Object)}.
* </p>
*
* @param <T>
* result type.
*/
public class OnlineDataLoader<T> extends AsyncTaskLoader<DataLoaderResult<T>> {
private static final String TAG = OnlineDataLoader.class.getName();
protected DataParser<T> dataParser;
protected DataHandler<T> dataHandler;
protected HttpRequest httpRequest;
/**
* Initializes a new {@link OnlineDataLoader} which initiates HTTP GET requests to load the remote data.
*/
public OnlineDataLoader(Context context, URL requestUrl, DataParser<T> dataParser, DataHandler<T> dataHandler) {
this(context, new HttpGetRequest(requestUrl), dataParser, dataHandler);
}
/**
* Initializes a new {@link OnlineDataLoader} which executes the given {@link HttpRequest} to load the remote data.
*/
public OnlineDataLoader(Context context, HttpRequest request, DataParser<T> dataParser, DataHandler<T> dataHandler) {
super(context);
this.httpRequest = request;
this.dataParser = dataParser;
this.dataHandler = dataHandler;
ExLog.i(TAG, String.format("Loader created: %s", Integer.toHexString(this.hashCode())));
}
@Override
protected void onStartLoading() {
if (dataHandler.hasCachedResults()) {
ExLog.i(TAG, String.format("Using cached results... %s", Integer.toHexString(this.hashCode())));
deliverResult(new DataLoaderResult<T>(dataHandler.getCachedResults(), true));
} else {
ExLog.i(TAG, String.format("No cached results. Forcing load now %s", Integer.toHexString(this.hashCode())));
forceLoad();
}
}
@Override
protected void onForceLoad() {
ExLog.i(TAG, String.format("Forcing load %s", Integer.toHexString(this.hashCode())));
super.onForceLoad();
}
@Override
public void deliverResult(DataLoaderResult<T> result) {
if (result.isSuccessful()) {
dataHandler.onResult(result.getResult());
}
super.deliverResult(result);
}
@Override
public DataLoaderResult<T> loadInBackground() {
try {
return new DataLoaderResult<T>(loadDataInBackground(), false);
} catch (Exception e) {
ExLog.ex(TAG, e);
return new DataLoaderResult<T>(e);
}
}
private T loadDataInBackground() throws Exception {
Reader reader = null;
try {
InputStream inputStream = httpRequest.execute();
reader = new InputStreamReader(inputStream);
return dataParser.parse(reader);
} finally {
if (reader != null) {
reader.close();
}
}
}
}
@@ -9,11 +9,11 @@ import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
public class CourseDataParser implements DataParser<CourseBase> {
public class CourseBaseParser implements DataParser<CourseBase> {
private JsonDeserializer<CourseBase> deserializer;
public CourseDataParser(JsonDeserializer<CourseBase> deserializer) {
public CourseBaseParser(JsonDeserializer<CourseBase> deserializer) {
this.deserializer = deserializer;
}
@@ -8,11 +8,11 @@ import com.sap.sailing.domain.base.racegroup.RaceGroup;
public interface FleetIdentifier {
public Fleet getFleet();
public SeriesBase getSeries();
public RaceGroup getRaceGroup();
public Fleet getFleet();
public Serializable getId();
public SeriesBase getSeries();
public RaceGroup getRaceGroup();
public Serializable getId();
}
@@ -32,7 +32,14 @@ public class FleetIdentifierImpl implements FleetIdentifier {
}
public Serializable getId() {
return String.format("%s.%s.%s", getRaceGroup().getName(), getSeries().getName(), getFleet().getName());
return String.format("%s.%s.%s",
escapeIdentifierFragment(getRaceGroup().getName()),
escapeIdentifierFragment(getSeries().getName()),
escapeIdentifierFragment(getFleet().getName()));
}
protected String escapeIdentifierFragment(String fragment) {
return fragment.replace(".", "\\.");
}
}
@@ -10,28 +10,24 @@ import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier;
public class ManagedRaceIdentifierImpl extends FleetIdentifierImpl implements ManagedRaceIdentifier {
private String raceName;
public ManagedRaceIdentifierImpl(
String raceName,
Fleet fleetWithRaceNames,
SeriesBase series,
RaceGroup raceGroup) {
super(fleetWithRaceNames, series, raceGroup);
this.raceName = raceName;
}
public ManagedRaceIdentifierImpl(String raceName, FleetIdentifier identifier) {
this(raceName, identifier.getFleet(), identifier.getSeries(), identifier.getRaceGroup());
}
private String raceName;
public String getRaceName() {
return raceName;
}
public ManagedRaceIdentifierImpl(String raceName, Fleet fleetWithRaceNames, SeriesBase series, RaceGroup raceGroup) {
super(fleetWithRaceNames, series, raceGroup);
this.raceName = raceName;
}
@Override
public Serializable getId() {
return String.format("%s.%s", super.getId(), getRaceName());
}
public ManagedRaceIdentifierImpl(String raceName, FleetIdentifier identifier) {
this(raceName, identifier.getFleet(), identifier.getSeries(), identifier.getRaceGroup());
}
public String getRaceName() {
return raceName;
}
@Override
public Serializable getId() {
return String.format("%s.%s", super.getId(), escapeIdentifierFragment(getRaceName()));
}
}
@@ -1,5 +1,7 @@
package com.sap.sailing.racecommittee.app.logging;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.EnumSet;
import java.util.concurrent.ArrayBlockingQueue;
@@ -127,6 +129,10 @@ public class ExLog {
public synchronized static void e(String tag, String msg) {
getInstance().error(tag, msg);
}
public synchronized static void ex(String tag, Exception e) {
getInstance().exception(tag, e);
}
private final static String logMsgTemplate = "%s - %s - %s : %s";
@@ -179,6 +185,19 @@ public class ExLog {
if (isTargetActive(Target.FILE))
logToFile(LogLevel.ERROR, tag, msg);
}
private void exception(String tag, Exception e) {
StringWriter stringWriter = new StringWriter();
PrintWriter stream = new PrintWriter(stringWriter);
e.printStackTrace(stream);
String msg = stringWriter.toString();
if (isTargetActive(Target.LOGCAT))
Log.e(tag, msg);
if (isTargetActive(Target.FILE))
logToFile(LogLevel.ERROR, tag, msg);
}
private boolean isTargetActive(Target logcat) {
return activeLoggingTargets.contains(logcat);
@@ -0,0 +1,21 @@
package com.sap.sailing.racecommittee.app.logging;
/**
* Stolen from API Level 14.
*
*/
public interface LifecycleCallbacks {
void onCreate(Object activityOrFragment);
void onStart(Object activityOrFragment);
void onResume(Object activityOrFragment);
void onPause(Object activityOrFragment);
void onStop(Object activityOrFragment);
void onSaveInstanceState(Object activityOrFragment);
void onDestroy(Object activityOrFragment);
}
@@ -0,0 +1,58 @@
package com.sap.sailing.racecommittee.app.logging;
/**
* Logger to track all activity and fragment lifecycle events.
*/
public class LifecycleLogger implements LifecycleCallbacks {
private static final String TAG = LifecycleLogger.class.getName();
private static boolean isLifecycleLoggingEnabled = false;
public static void enableLifecycleLogging(boolean enabled) {
isLifecycleLoggingEnabled = enabled;
}
private void onEvent(Object activityOrFragment, String action) {
if (isLifecycleLoggingEnabled) {
ExLog.i(TAG,
String.format("%s %s %s", action, activityOrFragment.getClass().getSimpleName(),
Integer.toHexString(System.identityHashCode(activityOrFragment))));
}
}
@Override
public void onCreate(Object activityOrFragment) {
onEvent(activityOrFragment, "Creating");
}
@Override
public void onStart(Object activityOrFragment) {
onEvent(activityOrFragment, "Starting");
}
@Override
public void onResume(Object activityOrFragment) {
onEvent(activityOrFragment, "Resuming");
}
@Override
public void onPause(Object activityOrFragment) {
onEvent(activityOrFragment, "Pausing");
}
@Override
public void onStop(Object activityOrFragment) {
onEvent(activityOrFragment, "Stopping");
}
@Override
public void onSaveInstanceState(Object activityOrFragment) {
onEvent(activityOrFragment, "Saving instance");
}
@Override
public void onDestroy(Object activityOrFragment) {
onEvent(activityOrFragment, "Destroying");
}
}
@@ -9,13 +9,15 @@ import java.util.Map.Entry;
import java.util.UUID;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Binder;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
@@ -38,6 +40,7 @@ import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerialize
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogEventSerializer;
public class RaceStateService extends Service {
private final static String TAG = RaceStateService.class.getName();
/**
@@ -50,6 +53,7 @@ public class RaceStateService extends Service {
}
private final static String EXTRAS_SERVICE_ID = RaceStateService.class.getName() + ".serviceId";
private final static int NOTIFICATION_ID = 42;
private final IBinder mBinder = new RaceStateServiceBinder();
@@ -66,6 +70,9 @@ public class RaceStateService extends Service {
private Map<ManagedRace, RaceStateEventListener> registeredStateEventListeners;
private Map<Serializable, List<PendingIntent>> managedIntents;
private NotificationManager notificationManager;
private NotificationCompat.Builder notificationBuilder;
@Override
public void onCreate() {
@@ -77,10 +84,27 @@ public class RaceStateService extends Service {
this.registeredStateChangeListeners = new HashMap<ManagedRace, RaceStateChangedListener>();
this.registeredStateEventListeners = new HashMap<ManagedRace, RaceStateEventListener>();
this.managedIntents = new HashMap<Serializable, List<PendingIntent>>();
startForeground();
ExLog.i(TAG, "Started.");
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
setupNotificationBuilder();
startForeground(NOTIFICATION_ID, notificationBuilder.build());
super.onCreate();
ExLog.i(TAG, "Started.");
}
private void setupNotificationBuilder() {
Intent launcherIntent = new Intent(this, LoginActivity.class);
launcherIntent.setAction(Intent.ACTION_MAIN);
launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, launcherIntent, 0);
notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.sap_sailing_app_icon)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.sap_sailing_app_icon))
.setContentTitle(getText(R.string.service_info))
.setContentText(getText(R.string.service_text_no_races))
.setContentIntent(contentIntent)
.setOngoing(true);
}
@Override
@@ -132,27 +156,6 @@ public class RaceStateService extends Service {
ExLog.i(TAG, "All races unregistered.");
}
private void startForeground() {
CharSequence text = getText(R.string.service_title);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.sap_sailing_app_icon, text, System.currentTimeMillis());
Intent intent = new Intent(this, LoginActivity.class);
// clear all activities above started and reuse if already existing!
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// The PendingIntent to launch our activity if the user selects this
// notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.service_info), text, contentIntent);
notification.flags |= Notification.FLAG_NO_CLEAR;
// Send the notification.
this.startForeground(42, notification);
}
private void handleStartCommand(Intent intent) {
String action = intent.getAction();
ExLog.i(TAG, String.format("Command action '%s' received.", action));
@@ -197,6 +200,11 @@ public class RaceStateService extends Service {
private void handleClearRaces(Intent intent) {
unregisterAllRaces();
clearAllRaces();
notificationManager.notify(NOTIFICATION_ID, notificationBuilder
.setNumber(0)
.setContentText(getString(R.string.service_text_no_races))
.build());
}
private void clearAllRaces() {
@@ -211,6 +219,12 @@ public class RaceStateService extends Service {
return;
}
registerRace(race);
int numRaces = managedIntents.keySet().size();
notificationManager.notify(NOTIFICATION_ID, notificationBuilder
.setNumber(numRaces)
.setContentText(String.format(getString(R.string.service_text_num_races), numRaces))
.build());
}
private ManagedRace getRaceFromIntent(Intent intent) {
@@ -245,7 +259,7 @@ public class RaceStateService extends Service {
ExLog.i(TAG, "Race " + race.getId() + " registered.");
} else {
ExLog.e(TAG, "Race " + race.getId() + " was already registered. Ignoring.");
ExLog.w(TAG, "Race " + race.getId() + " was already registered. Ignoring.");
}
}
@@ -1,8 +1,7 @@
package com.sap.sailing.racecommittee.app.services.sending;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URI;
import java.net.URL;
import android.content.Intent;
import android.os.AsyncTask;
@@ -44,13 +43,8 @@ public class EventSenderTask extends AsyncTask<Intent, Void, Pair<Intent, Intege
try {
ExLog.i(TAG, "Posting event: " + serializedEvent);
HttpRequest post = new HttpJsonPostRequest(URI.create(url), serializedEvent.toString());
try {
final InputStream inputStream = post.execute();
inputStream.close();
} finally {
post.disconnect();
}
HttpRequest post = new HttpJsonPostRequest(new URL(url), serializedEvent.toString());
post.execute().close();
ExLog.i(TAG, "Post successful for the following event: " + serializedEvent);
} catch (Exception e) {
ExLog.e(TAG, String.format("Post not successful, exception occured: %s", e.toString()));
@@ -2,12 +2,10 @@ package com.sap.sailing.racecommittee.app.ui.activities;
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.os.Bundle;
import android.os.IBinder;
import android.view.Menu;
import android.view.MenuInflater;
@@ -25,7 +23,7 @@ import com.sap.sailing.racecommittee.app.services.sending.EventSendingService.Ev
/**
* Base activity for all race committee cockpit activities enabling basic menu functionality.
*/
public abstract class BaseActivity extends Activity {
public abstract class BaseActivity extends LoggableActivity {
private class EventSendingServiceConnection implements ServiceConnection, EventSendingServiceLogger {
@Override
@@ -77,7 +75,6 @@ public abstract class BaseActivity extends Activity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.options_menu_settings:
ExLog.i(TAG, "Clicked SETTINGS.");
@@ -86,8 +83,7 @@ public abstract class BaseActivity extends Activity {
case R.id.options_menu_reload:
ExLog.i(TAG, "Clicked RESET.");
InMemoryDataStore.INSTANCE.reset();
fadeActivity(LoginActivity.class, true);
return true;
return onReset();
case R.id.options_menu_live:
ExLog.i(TAG, "Clicked LIVE.");
Toast.makeText(this, getLiveIconText(), Toast.LENGTH_LONG).show();
@@ -109,31 +105,34 @@ public abstract class BaseActivity extends Activity {
updateSendingServiceInformation();
return super.onPrepareOptionsMenu(menu);
}
@Override
public void onBackPressed() {
super.onBackPressed();
ExLog.i(TAG, String.format("Back pressed on activity %s", this.getClass().getSimpleName()));
}
protected boolean onHomeClicked() {
return false;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ExLog.i(TAG, String.format("Creating activity %s", this.getClass().getSimpleName()));
protected boolean onReset() {
fadeActivity(LoginActivity.class, true);
return true;
}
@Override
protected void onStart() {
public void onStart() {
super.onStart();
ExLog.i(TAG, String.format("Starting activity %s", this.getClass().getSimpleName()));
Intent intent = new Intent(this, EventSendingService.class);
bindService(intent, sendingServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
public void onStop() {
super.onStop();
ExLog.i(TAG, String.format("Stopping activity %s", this.getClass().getSimpleName()));
if (boundSendingService) {
unbindService(sendingServiceConnection);
boundSendingService = false;
@@ -0,0 +1,51 @@
package com.sap.sailing.racecommittee.app.ui.activities;
import android.app.Activity;
import android.os.Bundle;
import com.sap.sailing.racecommittee.app.logging.LifecycleLogger;
public abstract class LoggableActivity extends Activity {
private LifecycleLogger lifeLogger;
public LoggableActivity() {
this.lifeLogger = new LifecycleLogger();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifeLogger.onCreate(this);
}
@Override
public void onStart() {
super.onStart();
lifeLogger.onStart(this);
}
@Override
public void onResume() {
super.onResume();
lifeLogger.onResume(this);
}
@Override
public void onPause() {
super.onPause();
lifeLogger.onPause(this);
}
@Override
public void onStop() {
super.onStop();
lifeLogger.onStop(this);
}
@Override
public void onDestroy() {
super.onDestroy();
lifeLogger.onDestroy(this);
}
}
@@ -15,7 +15,7 @@ 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.logging.ExLog;
import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.BaseDialogFragment;
import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.AttachedDialogFragment;
import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.DialogListenerHost;
import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.LoginDialog;
import com.sap.sailing.racecommittee.app.ui.fragments.lists.CourseAreaListFragment;
@@ -27,6 +27,8 @@ import com.sap.sailing.racecommittee.app.ui.fragments.lists.selection.ItemSelect
public class LoginActivity extends BaseActivity implements EventSelectedListenerHost,
CourseAreaSelectedListenerHost, DialogListenerHost {
private final static String TAG = LoginActivity.class.getName();
private final static String CourseAreaListFragmentTag = "CourseAreaListFragmentTag";
private LoginDialog loginDialog;
private CourseArea selectedCourseArea;
@@ -35,6 +37,16 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener
this.loginDialog = new LoginDialog();
this.selectedCourseArea = null;
}
@Override
protected boolean onReset() {
Fragment courseAreaFragment = getFragmentManager().findFragmentByTag(CourseAreaListFragmentTag);
if (courseAreaFragment != null) {
getFragmentManager().beginTransaction().remove(courseAreaFragment).commit();
}
recreate();
return true;
}
/** Called when the activity is first created. */
@Override
@@ -49,13 +61,14 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener
// on first create add event list fragment
if (savedInstanceState == null) {
ExLog.i(TAG, "Seems to be first start. Creating event fragment.");
addEventListFragment();
}
}
private void addEventListFragment() {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.login_view_left_container, new EventListFragment());
transaction.replace(R.id.login_view_left_container, new EventListFragment());
transaction.setTransition(FragmentTransaction.TRANSIT_NONE);
transaction.commit();
}
@@ -68,7 +81,7 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener
fragment.setArguments(args);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.animator.slide_in, R.animator.slide_out);
transaction.replace(R.id.login_view_right_container, fragment);
transaction.replace(R.id.login_view_right_container, fragment, CourseAreaListFragmentTag);
transaction.commit();
ExLog.i("LoginActivity", "CourseFragment created.");
}
@@ -87,7 +100,6 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener
}
private void showCourseAreaListFragment(Serializable eventId) {
Toast.makeText(LoginActivity.this, eventId.toString(), Toast.LENGTH_LONG).show();
addCourseAreaListFragment(eventId);
}
@@ -114,7 +126,7 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener
return new DialogResultListener() {
@Override
public void onDialogPositiveButton(BaseDialogFragment dialog) {
public void onDialogPositiveButton(AttachedDialogFragment dialog) {
switch (loginDialog.getSelectedLoginType()) {
case OFFICER:
ExLog.i(TAG, "Communication with backend is active.");
@@ -136,14 +148,13 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener
return;
}
Toast.makeText(LoginActivity.this, selectedCourseArea.getId().toString(), Toast.LENGTH_LONG).show();
Intent message = new Intent(LoginActivity.this, RacingActivity.class);
message.putExtra(AppConstants.COURSE_AREA_UUID_KEY, selectedCourseArea.getId());
fadeActivity(message);
}
@Override
public void onDialogNegativeButton(BaseDialogFragment dialog) {
public void onDialogNegativeButton(AttachedDialogFragment dialog) {
/* nothing here... */
}
};
@@ -31,13 +31,16 @@ import com.sap.sailing.racecommittee.app.ui.fragments.RaceInfoFragment;
import com.sap.sailing.racecommittee.app.ui.fragments.lists.ManagedRaceListFragment;
import com.sap.sailing.racecommittee.app.ui.fragments.lists.ManagedRaceListFragment.FilterMode;
import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.RaceInfoListener;
import com.sap.sailing.racecommittee.app.utils.CollectionUtils;
public class RacingActivity extends BaseActivity implements RaceInfoListener {
public class RacingActivity extends SessionActivity implements RaceInfoListener {
// private final static String TAG = RacingActivity.class.getName();
private final static String ListFragmentTag = RacingActivity.class.getName() + ".ManagedRaceListFragment";
private static final String TAG = RacingActivity.class.getName();
private static final int RacesLoaderId = 0;
private class FilterModeSelectionBinder implements OnNavigationListener {
private ManagedRaceListFragment targetList;
@@ -68,6 +71,59 @@ public class RacingActivity extends BaseActivity implements RaceInfoListener {
}
}
private class RaceLoadClient implements LoadClient<Collection<ManagedRace>> {
private Collection<ManagedRace> lastSeenRaces = null;
private CourseArea courseArea;
public RaceLoadClient(CourseArea courseArea) {
this.courseArea = courseArea;
}
@Override
public void onLoadSucceded(Collection<ManagedRace> data, boolean isCached) {
setProgressBarIndeterminateVisibility(false);
// Let's do the setup stuff only when the data is changed (or its the first time)
if (lastSeenRaces != null && CollectionUtils.isEqualCollection(data, lastSeenRaces)) {
ExLog.i(TAG, "Same races are already loaded...");
} else {
lastSeenRaces = data;
registerOnService(data);
raceListFragment.setupOn(data);
Toast.makeText(RacingActivity.this,
String.format(getString(R.string.racing_load_success), data.size()), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onLoadFailed(Exception reason) {
setProgressBarIndeterminateVisibility(false);
AlertDialog.Builder builder = new AlertDialog.Builder(RacingActivity.this);
builder.setMessage(String.format(getString(R.string.generic_load_failure), reason.getMessage()))
.setTitle(getString(R.string.loading_failure)).setIcon(R.drawable.ic_dialog_alert_holo_light)
.setCancelable(true)
.setPositiveButton(getString(R.string.retry), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
setProgressBarIndeterminateVisibility(true);
ExLog.i(TAG, "Issuing a reload of managed races");
getLoaderManager().restartLoader(RacesLoaderId, null,
dataManager.createRacesLoader(courseArea.getId(), RaceLoadClient.this));
dialog.cancel();
}
}).setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
builder.create().show();
}
};
/**
* Selection list of all races
*/
@@ -80,12 +136,6 @@ public class RacingActivity extends BaseActivity implements RaceInfoListener {
private ReadonlyDataManager dataManager;
@Override
protected boolean onHomeClicked() {
fadeActivity(LoginActivity.class, true);
return true;
}
@Override
public void onCreate(Bundle savedInstanceState) {
// features must be requested before anything else
@@ -109,12 +159,15 @@ public class RacingActivity extends BaseActivity implements RaceInfoListener {
Toast.makeText(this, getString(R.string.racing_course_area_missing), Toast.LENGTH_LONG).show();
} else {
setupActionBar(courseArea);
// unload and unregister from all races here!
unloadAllRaces();
loadRaces(courseArea);
}
}
@Override
public void onBackPressed() {
logoutSession();
}
private ManagedRaceListFragment getOrCreateRaceListFragment() {
Fragment fragment = getFragmentManager().findFragmentByTag(ListFragmentTag);
// on first create add race list fragment
@@ -149,8 +202,6 @@ public class RacingActivity extends BaseActivity implements RaceInfoListener {
private void setupActionBar(CourseArea courseArea) {
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(String.format(getString(R.string.racingview_header), courseArea.getName()));
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
@@ -160,40 +211,12 @@ public class RacingActivity extends BaseActivity implements RaceInfoListener {
new FilterModeSelectionBinder(raceListFragment, actionBar, adapter, ManagedRaceListFragment.FilterMode.values());
}
private void unloadAllRaces() {
ExLog.i(TAG, "Issuing intent action clear races");
Intent intent = new Intent(AppConstants.INTENT_ACTION_CLEAR_RACES);
this.startService(intent);
}
private void loadRaces(final CourseArea courseArea) {
setProgressBarIndeterminateVisibility(true);
ExLog.i(TAG, "Issuing loading of managed races from data manager");
dataManager.loadRaces(courseArea.getId(), new LoadClient<Collection<ManagedRace>>() {
public void onLoadSucceded(Collection<ManagedRace> data) {
onLoadRacesSucceded(courseArea, data);
}
public void onLoadFailed(Exception reason) {
onLoadRacesFailed(courseArea, reason);
}
});
}
private void onLoadRacesSucceded(CourseArea courseArea, Collection<ManagedRace> data) {
setProgressBarIndeterminateVisibility(false);
registerOnService(data);
raceListFragment.setupOn(data);
Toast.makeText(RacingActivity.this, String.format(getString(R.string.racing_load_success), data.size()),
Toast.LENGTH_SHORT).show();
}
private void onLoadRacesFailed(final CourseArea courseArea, Exception reason) {
setProgressBarIndeterminateVisibility(false);
showLoadFailedDialog(courseArea, reason.getMessage());
getLoaderManager().initLoader(RacesLoaderId, null,
dataManager.createRacesLoader(courseArea.getId(), new RaceLoadClient(courseArea)));
}
private void registerOnService(Collection<ManagedRace> races) {
@@ -207,26 +230,6 @@ public class RacingActivity extends BaseActivity implements RaceInfoListener {
}
}
private void showLoadFailedDialog(final CourseArea courseArea, String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(
String.format(getString(R.string.generic_load_failure), message))
.setTitle(getString(R.string.loading_failure)).setIcon(R.drawable.ic_dialog_alert_holo_light).setCancelable(true)
.setPositiveButton(getString(R.string.retry), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
loadRaces(courseArea);
}
}).setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
if (!isFinishing()) {
AlertDialog alert = builder.create();
alert.show();
}
}
public void onRaceItemClicked(ManagedRace managedRace) {
infoFragment = new RaceInfoFragment();
infoFragment.setArguments(RaceFragment.createArguments(managedRace));
@@ -29,7 +29,7 @@ import com.sap.sailing.racecommittee.app.ui.adapters.PhotoAdapter;
import com.sap.sailing.racecommittee.app.ui.views.CameraView;
import com.sap.sailing.racecommittee.app.utils.MailHelper;
public class ResultsCapturingActivity extends BaseActivity {
public class ResultsCapturingActivity extends SessionActivity {
private static String ARGUMENTS_KEY_SUBJECT = "subject";
private static String ARGUMENTS_KEY_TEXT = "text";
@@ -54,7 +54,7 @@ public class ResultsCapturingActivity extends BaseActivity {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupActionBar();
@@ -111,13 +111,13 @@ public class ResultsCapturingActivity extends BaseActivity {
}
@Override
protected void onResume() {
public void onResume() {
super.onResume();
setupCamera();
}
@Override
protected void onPause() {
public void onPause() {
super.onPause();
releaseCamera();
}
@@ -161,12 +161,6 @@ public class ResultsCapturingActivity extends BaseActivity {
currentImageFile = createFinisherImageFile(currentImageIndex++);
}
@Override
protected boolean onHomeClicked() {
fadeActivity(LoginActivity.class, true);
return true;
}
private void setupListView() {
TextView header = new TextView(this);
header.setText(getString(R.string.results_capturing_view_list_header));
@@ -181,7 +175,6 @@ public class ResultsCapturingActivity extends BaseActivity {
private void setupActionBar() {
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(getString(R.string.results_capturing_title));
}
@@ -0,0 +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 android.app.ActionBar;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
public class SessionActivity extends BaseActivity {
private static final String TAG = BaseActivity.class.getName();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
@Override
public void onResume() {
super.onResume();
ExLog.i(TAG, String.format("Logging in from activity %s", this.getClass().getSimpleName()));
}
@Override
protected boolean onHomeClicked() {
return logoutSession();
}
@Override
protected boolean onReset() {
return logoutSession();
}
protected boolean logoutSession() {
ExLog.i(TAG, String.format("Logging out from activity %s", this.getClass().getSimpleName()));
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(getString(R.string.logout_dialog_title))
.setMessage(getString(R.string.logout_dialog_message))
.setPositiveButton(getString(R.string.logout), new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
doLogout();
}
})
.setNegativeButton(getString(R.string.cancel), new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
/* nothing here */
}
})
.create();
dialog.show();
return true;
}
private void doLogout() {
unloadAllRaces();
fadeActivity(LoginActivity.class, true);
}
private void unloadAllRaces() {
ExLog.i(TAG, "Issuing intent action clear races");
Intent intent = new Intent(AppConstants.INTENT_ACTION_CLEAR_RACES);
this.startService(intent);
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.racecommittee.app.ui.activities;
import java.util.ArrayList;
import java.util.List;
import android.content.Intent;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
@@ -24,6 +25,11 @@ public class SettingsActivity extends PreferenceActivity {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.layout.settings_view);
setupStartProcedurePreferences();
setupLanguageButton();
}
private void setupStartProcedurePreferences() {
final ListPreference startProcedurePreference = (ListPreference) findPreference("defaultStartProcedureType");
List<CharSequence> entries = new ArrayList<CharSequence>();
@@ -47,8 +53,19 @@ public class SettingsActivity extends PreferenceActivity {
return true;
}
});
}
private void setupLanguageButton() {
Preference button = (Preference)findPreference("languagePref");
button.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName("com.android.settings", "com.android.settings.LanguageSettings");
startActivity(intent);
return true;
}
});
}
}
@@ -15,7 +15,7 @@ import com.sap.sailing.racecommittee.app.R;
public class SystemInformationActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.system_information_view);
setupVersionView();
@@ -20,6 +20,7 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import com.sap.sailing.domain.common.Bearing;
@@ -37,9 +38,8 @@ import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.logging.ExLog;
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.GeoUtils;
public class WindActivity extends BaseActivity implements CompassDirectionListener, LocationListener {
public class WindActivity extends SessionActivity implements CompassDirectionListener, LocationListener {
private final static int FIVE_SEC = 5000;
private final static int EVERY_POSITION_CHANGE = 0;
@@ -47,10 +47,9 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
CompassView compassView;
EditText windBearingEditText;
EditText windSpeedEditText;
EditText latitudeEditText;
EditText longitudeEditText;
SeekBar windSpeedSeekBar;
Button sendButton;
TextView waitingForGpsTextView;
LocationManager locationManager;
Location currentLocation;
@@ -58,17 +57,16 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
DecimalFormat bearingFormat;
@Override
protected void onCreate(Bundle savedInstanceState) {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.wind_view);
compassView = (CompassView) findViewById(R.id.compassView);
windBearingEditText = (EditText) findViewById(R.id.editTextWindDirection);
windSpeedEditText = (EditText) findViewById(R.id.editTextWindSpeed);
latitudeEditText = (EditText) findViewById(R.id.et_position_lat);
longitudeEditText = (EditText) findViewById(R.id.et_position_lon);
windSpeedSeekBar = (SeekBar) findViewById(R.id.seekbar_wind_speed);
sendButton = (Button) findViewById(R.id.btn_wind_send);
waitingForGpsTextView = (TextView) findViewById(R.id.textWaitingForGPS);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
@@ -144,9 +142,9 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
double enteredWindSpeed = AppPreferences.getWindSpeed(this);
windSpeedSeekBar.setProgress(Double.valueOf(enteredWindSpeed).intValue() * 10);
windSpeedEditText.setText(speedFormat.format(enteredWindSpeed));
double enteredWindBearing = AppPreferences.getWindBearing(this);
compassView.setDirection((float)enteredWindBearing);
windBearingEditText.setText(bearingFormat.format(enteredWindBearing));
double enteredWindBearingFrom = AppPreferences.getWindBearingFromDirection(this);
compassView.setDirection((float)enteredWindBearingFrom);
windBearingEditText.setText(bearingFormat.format(enteredWindBearingFrom));
}
private void buildAlertMessageNoGps() {
@@ -169,12 +167,14 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
protected void saveEntriesInPreferences(Wind wind) {
AppPreferences.setWindBearing(getBaseContext(), wind.getBearing().getDegrees());
// Wind.getBearing() returns a value that assumes that the wind flows in that direction
// But for this app we need to display the direction the wind is coming from
AppPreferences.setWindBearingFromDirection(getBaseContext(), wind.getBearing().reverse().getDegrees());
AppPreferences.setWindSpeed(getBaseContext(), wind.getKnots());
}
@Override
protected void onStart() {
public void onStart() {
super.onStart();
compassView.setDirectionListener(this);
@@ -187,7 +187,7 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
}
@Override
protected void onPause() {
public void onPause() {
locationManager.removeUpdates(this);
super.onPause();
}
@@ -195,8 +195,7 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
@Override
public void onLocationChanged(Location location) {
currentLocation = location;
latitudeEditText.setText(String.valueOf(GeoUtils.getDegMinSecFormatForDecimalDegree(location.getLatitude())));
longitudeEditText.setText(String.valueOf(GeoUtils.getDegMinSecFormatForDecimalDegree(location.getLongitude())));
waitingForGpsTextView.setText("");
sendButton.setEnabled(true);
}
@@ -227,9 +226,10 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
Position currentPosition = new DegreePosition(currentLocation.getLatitude(), currentLocation.getLongitude());
double windSpeed = Double.valueOf(windSpeedEditText.getText().toString().replace(",", "."));
double windBearing = Double.valueOf(windBearingEditText.getText().toString());
Bearing bearing = new DegreeBearingImpl(windBearing);
SpeedWithBearing speedBearing = new KnotSpeedWithBearingImpl(windSpeed, bearing);
Bearing bearing_from = new DegreeBearingImpl(windBearing);
// this is not a standard bearing but the direction where the wind comes from, needs to be converted
// to match the assumption that a bearing is always the direction the wind flows to
SpeedWithBearing speedBearing = new KnotSpeedWithBearingImpl(windSpeed, bearing_from.reverse());
return new WindImpl(currentPosition, MillisecondsTimePoint.now(), speedBearing);
}
@@ -0,0 +1,52 @@
package com.sap.sailing.racecommittee.app.ui.fragments;
import android.app.Fragment;
import android.os.Bundle;
import com.sap.sailing.racecommittee.app.logging.LifecycleLogger;
public abstract class LoggableFragment extends Fragment {
private LifecycleLogger lifeLogger;
public LoggableFragment() {
this.lifeLogger = new LifecycleLogger();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifeLogger.onCreate(this);
}
@Override
public void onStart() {
super.onStart();
lifeLogger.onStart(this);
}
@Override
public void onResume() {
super.onResume();
lifeLogger.onResume(this);
}
@Override
public void onPause() {
super.onPause();
lifeLogger.onPause(this);
}
@Override
public void onStop() {
super.onStop();
lifeLogger.onStop(this);
}
@Override
public void onDestroy() {
super.onDestroy();
lifeLogger.onDestroy(this);
}
}
@@ -2,16 +2,17 @@ package com.sap.sailing.racecommittee.app.ui.fragments;
import java.io.Serializable;
import android.os.Bundle;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.utils.TickListener;
import com.sap.sailing.racecommittee.app.utils.TickSingleton;
import android.app.Fragment;
import android.os.Bundle;
public abstract class RaceFragment extends Fragment implements TickListener {
public abstract class RaceFragment extends LoggableFragment implements TickListener {
//private static final String TAG = RaceFragment.class.getName();
public static Bundle createArguments(ManagedRace race) {
Bundle arguments = new Bundle();
@@ -63,7 +64,7 @@ public abstract class RaceFragment extends Fragment implements TickListener {
@Override
public void notifyTick() {
// may be overriden in subclasses.
// see subclasses.
}
}
@@ -218,7 +218,7 @@ public class RaceInfoFragment extends RaceFragment implements RaceStateChangedLi
LastWindFixFinder windFinder = new LastWindFixFinder(getRace().getState().getRaceLog());
if (windFinder.analyze()!=null){
Wind wind = windFinder.analyze();
windInfoHeader.setText(String.format(getString(R.string.wind_info), wind.getKnots(), wind.getBearing().toString()));
windInfoHeader.setText(String.format(getString(R.string.wind_info), wind.getKnots(), wind.getBearing().reverse().toString()));
}
}
@@ -29,9 +29,9 @@ public class RaceInfoFragmentChooser {
private RaceFragment createInfoFragment(Class<? extends RaceFragment> fragmentClass, ManagedRace managedRace) {
try {
ExLog.i(TAG, String.format("Fragment %s is chosen.", fragmentClass.getName()));
RaceFragment fragment = fragmentClass.newInstance();
fragment.setArguments(RaceFragment.createArguments(managedRace));
ExLog.i(TAG, String.format("Fragment %s may be choosen", fragmentClass.getName()));
return fragment;
} catch (Exception e) {
ExLog.e(TAG, String.format("Exception while instantiating race info fragment:\n%s", e.toString()));
@@ -1,7 +1,7 @@
package com.sap.sailing.racecommittee.app.ui.fragments.dialogs;
public abstract class ActivityAttachedDialogFragment extends BaseDialogFragment {
public abstract class ActivityAttachedDialogFragment extends AttachedDialogFragment {
protected DialogListenerHost hostActivity;
@@ -1,54 +1,53 @@
package com.sap.sailing.racecommittee.app.ui.fragments.dialogs;
import com.sap.sailing.racecommittee.app.logging.ExLog;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
public abstract class BaseDialogFragment extends DialogFragment {
private final static String TAG = BaseDialogFragment.class.getName();
protected abstract CharSequence getNegativeButtonLabel();
protected abstract CharSequence getPositiveButtonLabel();
protected abstract Builder createDialog(AlertDialog.Builder builder);
protected abstract DialogListenerHost getHost();
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return createDialog(
new Builder(getActivity()).setNegativeButton(getNegativeButtonLabel(), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
onNegativeButton();
}
}).setPositiveButton(getPositiveButtonLabel(), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
onPositiveButton();
}
})).create();
}
protected void onNegativeButton() {
if (getHost() != null) {
getHost().getListener().onDialogNegativeButton(this);
} else {
ExLog.w(TAG, "Dialog host was null.");
}
}
protected void onPositiveButton() {
if (getHost() != null) {
getHost().getListener().onDialogPositiveButton(this);
} else {
ExLog.w(TAG, "Dialog host was null.");
}
}
}
package com.sap.sailing.racecommittee.app.ui.fragments.dialogs;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import com.sap.sailing.racecommittee.app.logging.ExLog;
public abstract class AttachedDialogFragment extends LoggableDialogFragment {
private final static String TAG = AttachedDialogFragment.class.getName();
protected abstract CharSequence getNegativeButtonLabel();
protected abstract CharSequence getPositiveButtonLabel();
protected abstract Builder createDialog(AlertDialog.Builder builder);
protected abstract DialogListenerHost getHost();
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return createDialog(
new Builder(getActivity()).setNegativeButton(getNegativeButtonLabel(), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
onNegativeButton();
}
}).setPositiveButton(getPositiveButtonLabel(), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
onPositiveButton();
}
})).create();
}
protected void onNegativeButton() {
if (getHost() != null) {
getHost().getListener().onDialogNegativeButton(this);
} else {
ExLog.w(TAG, "Dialog host was null.");
}
}
protected void onPositiveButton() {
if (getHost() != null) {
getHost().getListener().onDialogPositiveButton(this);
} else {
ExLog.w(TAG, "Dialog host was null.");
}
}
}
@@ -1,15 +1,14 @@
package com.sap.sailing.racecommittee.app.ui.fragments.dialogs;
import com.sap.sailing.racecommittee.app.R;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.widget.DatePicker;
public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener,
import com.sap.sailing.racecommittee.app.R;
public class DatePickerFragment extends LoggableDialogFragment implements DatePickerDialog.OnDateSetListener,
DatePickerDialog.OnDismissListener {
private DatePickerDialog.OnDateSetListener listener;
@@ -2,8 +2,8 @@ package com.sap.sailing.racecommittee.app.ui.fragments.dialogs;
public interface DialogListenerHost {
public interface DialogResultListener {
void onDialogNegativeButton(BaseDialogFragment dialog);
void onDialogPositiveButton(BaseDialogFragment dialog);
void onDialogNegativeButton(AttachedDialogFragment dialog);
void onDialogPositiveButton(AttachedDialogFragment dialog);
}
DialogResultListener getListener();
@@ -7,6 +7,7 @@ import java.util.List;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Loader;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
@@ -47,7 +48,9 @@ import com.sap.sailing.racecommittee.app.ui.comparators.NaturalNamedComparator;
import com.sap.sailing.racecommittee.app.utils.ESSMarkImageHelper;
public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
//private final static String TAG = ESSCourseDesignDialogFragment.class.getName();
// private final static String TAG = ESSCourseDesignDialogFragment.class.getName();
private static int MarksLoaderId = 0;
private List<Mark> aMarkList;
private MarkGridAdapter gridAdapter;
@@ -57,15 +60,15 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
private CourseElementListAdapter previousCourseElementAdapter;
private DragSortListView newCourseListView;
private ListView previousCourseListView;
private DragSortController dragSortController;
private Button publishButton;
private Button unpublishButton;
private Button takePreviousButton;
private ReadonlyDataManager dataManager;
private int dragStartMode = DragSortController.ON_DRAG;
private boolean removeEnabled = true;
private int removeMode = DragSortController.FLING_REMOVE;
@@ -83,17 +86,19 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
getDialog().setTitle(getActivity().getText(R.string.course_design_dialog_title));
dataManager = DataManager.create(getActivity());
aMarkList = new ArrayList<Mark>();
aMarkList = new ArrayList<Mark>();
courseElements = new ArrayList<CourseListDataElement>();
previousCourseElements = new ArrayList<CourseListDataElement>();
gridAdapter = new MarkGridAdapter(getActivity(), aMarkList, ESSMarkImageHelper.getInstance());
courseElementAdapter = new DraggableCourseElementListAdapter(getActivity(), courseElements, ESSMarkImageHelper.getInstance());
previousCourseElementAdapter = new CourseElementListAdapter(getActivity(), previousCourseElements, ESSMarkImageHelper.getInstance());
courseElementAdapter = new DraggableCourseElementListAdapter(getActivity(), courseElements,
ESSMarkImageHelper.getInstance());
previousCourseElementAdapter = new CourseElementListAdapter(getActivity(), previousCourseElements,
ESSMarkImageHelper.getInstance());
loadMarks();
loadCourseOnServer();
fillPreviousCourseElementsWithLastPublishedCourseDesign();
GridView gridView = (GridView) getView().findViewById(R.id.gridViewAssets);
gridView.setAdapter(gridAdapter);
@@ -107,7 +112,7 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
}
});
newCourseListView = (DragSortListView) getView().findViewById(R.id.listViewNewCourse);
newCourseListView.setAdapter(courseElementAdapter);
newCourseListView.setDropListener(new DragSortListView.DropListener() {
@@ -121,16 +126,16 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
}
}
});
newCourseListView.setRemoveListener(new DragSortListView.RemoveListener() {
@Override
public void remove(int toBeRemoved) {
CourseListDataElement item = courseElementAdapter.getItem(toBeRemoved);
courseElementAdapter.remove(item);
}
});
newCourseListView.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
@@ -140,7 +145,7 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
return true;
}
});
dragSortController = buildDragSortController(newCourseListView);
newCourseListView.setFloatViewManager(dragSortController);
newCourseListView.setOnTouchListener(dragSortController);
@@ -163,14 +168,12 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
} else {
copyPreviousToNewCourseDesign();
}
}
else {
} else {
Toast.makeText(getActivity(), "No course available to copy", Toast.LENGTH_LONG).show();
}
}
});
publishButton = (Button) getView().findViewById(R.id.publishCourseDesignButton);
publishButton.setOnClickListener(new OnClickListener() {
@@ -180,13 +183,19 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
sendCourseDataAndDismiss(courseData);
} catch (IllegalStateException ex) {
if (ex.getMessage().equals("Gate has no right buoy")) {
Toast.makeText(getActivity(), "A right buoy is missing for a gate. Please select the right buoy.", Toast.LENGTH_LONG).show();
Toast.makeText(getActivity(),
"A right buoy is missing for a gate. Please select the right buoy.", Toast.LENGTH_LONG)
.show();
} else if (ex.getMessage().equals("Each waypoints needs a passing side")) {
Toast.makeText(getActivity(), "A waypoint has no passing side. Please select the passing side by clicking long on the waypoint.", Toast.LENGTH_LONG).show();
Toast.makeText(
getActivity(),
"A waypoint has no passing side. Please select the passing side by clicking long on the waypoint.",
Toast.LENGTH_LONG).show();
}
} catch (IllegalArgumentException ex) {
Toast.makeText(getActivity(), "The course design has to have at least one waypoint.", Toast.LENGTH_LONG).show();
Toast.makeText(getActivity(), "The course design has to have at least one waypoint.",
Toast.LENGTH_LONG).show();
}
}
@@ -194,19 +203,16 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
unpublishButton = (Button) getView().findViewById(R.id.unpublishCourseDesignButton);
unpublishButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
CourseBase emptyCourse = new CourseDataImpl("Unpublished course design");
sendCourseDataAndDismiss(emptyCourse);
}
});
}
/**
* Creates a DragSortController and thereby defines the behaviour of the drag sort list
* Creates a DragSortController and thereby defines the behavior of the drag sort list
*/
public DragSortController buildDragSortController(DragSortListView dragSortListView) {
DragSortController controller = new DragSortController(dragSortListView);
@@ -219,54 +225,30 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
controller.setBackgroundColor(getActivity().getResources().getColor(R.color.welter_medium_blue));
return controller;
}
@Override
public void onResume() {
super.onResume();
onLoadMarksSucceeded(dataManager.getDataStore().getMarks());
}
private void loadCourseOnServer() {
dataManager.loadCourse(getRace(), new LoadClient<CourseBase>() {
@Override
public void onLoadFailed(Exception reason) {
fillPreviousCourseElementsWithLastPublishedCourseDesign();
}
@Override
public void onLoadSucceded(CourseBase data) {
/*if (Util.size(data.getWaypoints()) > 0) {
fillPreviousCourseElementsInList(data);
} else*/ {
fillPreviousCourseElementsWithLastPublishedCourseDesign();
}
}
});
}
private void fillPreviousCourseElementsWithLastPublishedCourseDesign() {
CourseBase lastPublishedCourseDesign = InMemoryDataStore.INSTANCE.getLastPublishedCourseDesign();
if(lastPublishedCourseDesign != null) {
if (lastPublishedCourseDesign != null) {
fillPreviousCourseElementsInList(lastPublishedCourseDesign);
}
}
private void loadMarks() {
dataManager.loadMarks(getRace(), new LoadClient<Collection<Mark>>() {
Loader<?> marksLoader = getLoaderManager().restartLoader(MarksLoaderId, null,
dataManager.createMarksLoader(getRace(), new LoadClient<Collection<Mark>>() {
@Override
public void onLoadFailed(Exception reason) {
Toast.makeText(getActivity(), String.format("Marks: %s",reason.toString()), Toast.LENGTH_LONG).show();
}
@Override
public void onLoadFailed(Exception reason) {
}
@Override
public void onLoadSucceded(Collection<Mark> data) {
onLoadMarksSucceeded(data);
}
});
@Override
public void onLoadSucceded(Collection<Mark> data, boolean isCached) {
onLoadMarksSucceeded(data);
}
}));
// ignore any cached results and force a remote load of the marks,
// because we always need the most current.
marksLoader.forceLoad();
}
protected void onLoadMarksSucceeded(Collection<Mark> data) {
@@ -295,10 +277,10 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
for (Waypoint waypoint : courseData.getWaypoints()) {
ControlPoint controlPoint = waypoint.getControlPoint();
if (controlPoint instanceof Mark) {
CourseListDataElement element = new CourseListDataElement();
element.setLeftMark((Mark)controlPoint);
element.setLeftMark((Mark) controlPoint);
element.setRoundingDirection(getRoundingDirection(waypoint.getPassingSide()));
elementList.add(element);
} else if (controlPoint instanceof Gate) {
@@ -327,24 +309,28 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
}
protected CourseBase convertCourseElementsToACourseData() throws IllegalStateException, IllegalArgumentException {
//TODO find a proper name for the highly flexible ESS courses to be shown on the regatta overview page
CourseBase design = new CourseDataImpl("Course template");
// TODO find a proper name for the highly flexible ESS courses to be shown on the regatta overview page
CourseBase design = new CourseDataImpl("Course");
List<Waypoint> waypoints = new ArrayList<Waypoint>();
for (CourseListDataElement courseElement : courseElements) {
if (courseElement.getRoundingDirection().equals(RoundingDirection.Gate) && courseElement.getRightMark() != null) {
String gateName = "Gate " + courseElement.getLeftMark().getName() + " / " + courseElement.getRightMark().getName();
if (courseElement.getRoundingDirection().equals(RoundingDirection.Gate)
&& courseElement.getRightMark() != null) {
String gateName = "Gate " + courseElement.getLeftMark().getName() + " / "
+ courseElement.getRightMark().getName();
Gate gate = new GateImpl(courseElement.getLeftMark(), courseElement.getRightMark(), gateName);
Waypoint waypoint = new WaypointImpl(gate);
waypoints.add(waypoint);
} else if (courseElement.getRoundingDirection().equals(RoundingDirection.Gate) && courseElement.getRightMark() == null) {
} else if (courseElement.getRoundingDirection().equals(RoundingDirection.Gate)
&& courseElement.getRightMark() == null) {
throw new IllegalStateException("Gate has no right buoy");
} else if (courseElement.getRoundingDirection().equals(RoundingDirection.None)) {
throw new IllegalStateException("Each waypoints needs a passing side");
} else {
Waypoint waypoint = new WaypointImpl(courseElement.getLeftMark(), getNauticalSide(courseElement.getRoundingDirection()));
Waypoint waypoint = new WaypointImpl(courseElement.getLeftMark(),
getNauticalSide(courseElement.getRoundingDirection()));
waypoints.add(waypoint);
}
@@ -365,19 +351,19 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
NauticalSide side = null;
if (roundingDirection.name().equalsIgnoreCase(NauticalSide.PORT.name())) {
side = NauticalSide.PORT;
} else if (roundingDirection.name().equalsIgnoreCase(NauticalSide.STARBOARD.name())){
} else if (roundingDirection.name().equalsIgnoreCase(NauticalSide.STARBOARD.name())) {
side = NauticalSide.STARBOARD;
}
return side;
}
private RoundingDirection getRoundingDirection(NauticalSide passingSide) {
RoundingDirection direction = null;
if (passingSide == null) {
direction = RoundingDirection.None;
} else if (passingSide.name().equalsIgnoreCase(RoundingDirection.Port.name())) {
direction = RoundingDirection.Port;
} else if (passingSide.name().equalsIgnoreCase(RoundingDirection.Starboard.name())){
} else if (passingSide.name().equalsIgnoreCase(RoundingDirection.Starboard.name())) {
direction = RoundingDirection.Starboard;
} else {
direction = RoundingDirection.Gate;
@@ -400,18 +386,18 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
@Override
public void onClick(DialogInterface dialog, int which) {
//do nothing
// do nothing
}
});
builder.create();
builder.show();
}
protected void copyPreviousToNewCourseDesign() {
courseElements.clear();
courseElements.addAll(previousCourseElements);
courseElementAdapter.notifyDataSetChanged();
}
}
protected void onMarkClickedOnGrid(Mark mark) {
if (courseElements.isEmpty()) {
@@ -426,10 +412,11 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
}
}
}
private CourseListDataElement getFirstGateCourseElementWithoutRightMark() {
for (CourseListDataElement courseElement : courseElements) {
if (courseElement.getRoundingDirection().equals(RoundingDirection.Gate) && courseElement.getRightMark() == null) {
if (courseElement.getRoundingDirection().equals(RoundingDirection.Gate)
&& courseElement.getRightMark() == null) {
return courseElement;
}
}
@@ -448,15 +435,13 @@ public class ESSCourseDesignDialogFragment extends RaceDialogFragment {
directions.add(direction.name());
}
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.pick_a_rounding_direction)
.setItems(R.array.rounding_directions, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int position) {
// for i18n we expect following order in array: 1. Port, 2. Starboard, 3. Gate
//String[] directions = getActivity().getResources().getStringArray(R.array.rounding_directions);
RoundingDirection pickedDirection = RoundingDirection.relevantValues()[position];
onRoundingDirectionPicked(courseElement, pickedDirection);
}
});
builder.setTitle(R.string.pick_a_rounding_direction).setItems(R.array.rounding_directions,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int position) {
RoundingDirection pickedDirection = RoundingDirection.relevantValues()[position];
onRoundingDirectionPicked(courseElement, pickedDirection);
}
});
builder.create();
builder.show();
}
@@ -2,7 +2,7 @@ package com.sap.sailing.racecommittee.app.ui.fragments.dialogs;
import android.app.Fragment;
public abstract class FragmentAttachedDialogFragment extends BaseDialogFragment {
public abstract class FragmentAttachedDialogFragment extends AttachedDialogFragment {
@Override
protected DialogListenerHost getHost() {
@@ -0,0 +1,55 @@
package com.sap.sailing.racecommittee.app.ui.fragments.dialogs;
import android.app.DialogFragment;
import android.os.Bundle;
import com.sap.sailing.racecommittee.app.logging.LifecycleLogger;
/**
* Simple base class for {@link DialogFragment}'s to add some logging.
*/
public abstract class LoggableDialogFragment extends DialogFragment {
private LifecycleLogger lifeLogger;
public LoggableDialogFragment() {
this.lifeLogger = new LifecycleLogger();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifeLogger.onCreate(this);
}
@Override
public void onStart() {
super.onStart();
lifeLogger.onStart(this);
}
@Override
public void onResume() {
super.onResume();
lifeLogger.onResume(this);
}
@Override
public void onPause() {
super.onPause();
lifeLogger.onPause(this);
}
@Override
public void onStop() {
super.onStop();
lifeLogger.onStop(this);
}
@Override
public void onDestroy() {
super.onDestroy();
lifeLogger.onDestroy(this);
}
}
@@ -9,6 +9,7 @@ import java.util.List;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Loader;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -19,6 +20,7 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import com.mobeta.android.dslv.DragSortController;
import com.mobeta.android.dslv.DragSortListView;
@@ -29,6 +31,7 @@ import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
import com.sap.sailing.racecommittee.app.domain.impl.DomainFactoryImpl;
import com.sap.sailing.racecommittee.app.logging.ExLog;
@@ -37,28 +40,28 @@ import com.sap.sailing.racecommittee.app.ui.adapters.finishing.CompetitorsAdapte
import com.sap.sailing.racecommittee.app.ui.comparators.NaturalNamedComparator;
public class PositioningFragment extends RaceDialogFragment {
private DragSortListView positioningListView;
private ListView competitorListView;
private DragSortController dragSortController;
private CompetitorsAdapter competitorsAdapter;
private CompetitorPositioningListAdapter positioningAdapter;
protected List<Competitor> competitors;
protected List<Triple<Serializable, String, MaxPointsReason>> positionedCompetitors;
protected Comparator<Named> competitorComparator;
private int dragStartMode = DragSortController.ON_DRAG;
private boolean removeEnabled = true;
private int removeMode = DragSortController.FLING_REMOVE;
private boolean sortEnabled = true;
private boolean dragEnabled = true;
private boolean isDialog() {
return getDialog() != null;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (isDialog()) {
@@ -66,11 +69,11 @@ public class PositioningFragment extends RaceDialogFragment {
}
return inflater.inflate(R.layout.race_positioning_view, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (isDialog()) {
Button okButton = (Button) getView().findViewById(R.id.buttonPositionOk);
okButton.setVisibility(Button.VISIBLE);
@@ -82,19 +85,20 @@ public class PositioningFragment extends RaceDialogFragment {
}
});
}
competitors = new ArrayList<Competitor>();
Util.addAll(getRace().getCompetitors(), competitors);
competitorComparator = new NaturalNamedComparator();
Collections.sort(competitors, competitorComparator);
competitorsAdapter = new CompetitorsAdapter(getActivity(), R.layout.welter_grid_competitor_cell, competitors);
loadCompetitors();
positionedCompetitors = initializeFinishPositioningList();
deletePositionedCompetitorsFromUnpositionedList();
positioningAdapter = new CompetitorPositioningListAdapter(getActivity(), R.layout.welter_positioning_item, positionedCompetitors);
positioningAdapter = new CompetitorPositioningListAdapter(getActivity(), R.layout.welter_positioning_item,
positionedCompetitors);
positioningListView = (DragSortListView) getView().findViewById(R.id.dragSortListPositioning);
positioningListView.setAdapter(positioningAdapter);
positioningListView.setDropListener(new DragSortListView.DropListener() {
@@ -108,9 +112,9 @@ public class PositioningFragment extends RaceDialogFragment {
}
}
});
positioningListView.setRemoveListener(new DragSortListView.RemoveListener() {
@Override
public void remove(int toBeRemoved) {
Triple<Serializable, String, MaxPointsReason> item = positioningAdapter.getItem(toBeRemoved);
@@ -124,9 +128,9 @@ public class PositioningFragment extends RaceDialogFragment {
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
createMaxPointsReasonSelectionDialog(positionedCompetitors.get(position));
return true;
}
}
});
dragSortController = buildDragSortController(positioningListView);
positioningListView.setFloatViewManager(dragSortController);
positioningListView.setOnTouchListener(dragSortController);
@@ -139,18 +143,13 @@ public class PositioningFragment extends RaceDialogFragment {
public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) {
Competitor competitor = (Competitor) competitorsAdapter.getItem(position);
ExLog.i("RaceFinishingFragment", "Grid is clicked at position " + position + " for competitor " + competitor.getName());
ExLog.i("RaceFinishingFragment", "Grid is clicked at position " + position + " for competitor "
+ competitor.getName());
onCompetitorClickedOnGrid(competitor);
}
});
}
@Override
public void onResume() {
super.onResume();
onLoadCompetitorsSucceeded(getRace().getCompetitors());
}
/**
* Creates a DragSortController and thereby defines the behaviour of the drag sort list
*/
@@ -167,37 +166,57 @@ public class PositioningFragment extends RaceDialogFragment {
}
private void loadCompetitors() {
getActivity().setProgressBarIndeterminateVisibility(true);
ReadonlyDataManager dataManager = OnlineDataManager.create(getActivity());
Loader<?> competitorLoaders = getLoaderManager().initLoader(0, null,
dataManager.createCompetitorsLoader(getRace(), new LoadClient<Collection<Competitor>>() {
OnlineDataManager.create(getActivity()).loadCompetitors(getRace(), new LoadClient<Collection<Competitor>>() {
@Override
public void onLoadFailed(Exception reason) {
getActivity().setProgressBarIndeterminateVisibility(false);
Toast.makeText(getActivity(), String.format("Competitors: %s", reason.toString()),
Toast.LENGTH_LONG).show();
}
@Override
public void onLoadFailed(Exception reason) {
onLoadCompetitorsSucceeded(getRace().getCompetitors());
}
@Override
public void onLoadSucceded(Collection<Competitor> data, boolean isCached) {
if (!isCached) {
getActivity().setProgressBarIndeterminateVisibility(false);
}
onLoadCompetitorsSucceeded(data);
}
@Override
public void onLoadSucceded(Collection<Competitor> data) {
onLoadCompetitorsSucceeded(data);
}
});
}));
// Force load to get non-cached remote competitors...
competitorLoaders.forceLoad();
}
protected void onLoadCompetitorsSucceeded(Collection<Competitor> data) {
competitors.clear();
competitors.addAll(data);
Collections.sort(competitors, competitorComparator);
deleteObsoleteCompetitorsFromPositionedList(data);
deletePositionedCompetitorsFromUnpositionedList();
competitorsAdapter.notifyDataSetChanged();
}
private void deleteObsoleteCompetitorsFromPositionedList(Collection<Competitor> validCompetitors) {
List<Triple<Serializable, String, MaxPointsReason>> toBeDeleted = new ArrayList<Util.Triple<Serializable, String, MaxPointsReason>>();
for (Triple<Serializable, String, MaxPointsReason> positionedItem : positionedCompetitors) {
if (!validCompetitors.contains(DomainFactoryImpl.INSTANCE.getExistingCompetitorById(positionedItem.getA()))) {
toBeDeleted.add(positionedItem);
}
}
positionedCompetitors.removeAll(toBeDeleted);
}
private void deletePositionedCompetitorsFromUnpositionedList() {
for (Triple<Serializable, String, MaxPointsReason> positionedItem : positionedCompetitors) {
Competitor competitor = DomainFactoryImpl.INSTANCE.getExistingCompetitorById(positionedItem.getA());
competitors.remove(competitor);
}
}
private List<Triple<Serializable, String, MaxPointsReason>> initializeFinishPositioningList() {
List<Triple<Serializable, String, MaxPointsReason>> positionings;
if (getRace().getState().getFinishPositioningList() != null) {
@@ -207,7 +226,7 @@ public class PositioningFragment extends RaceDialogFragment {
}
return positionings;
}
private void setItemToNewPositioning(int from, int to, Triple<Serializable, String, MaxPointsReason> item) {
positioningAdapter.remove(item);
positioningAdapter.insert(item, to);
@@ -216,16 +235,16 @@ public class PositioningFragment extends RaceDialogFragment {
private void createMaxPointsReasonSelectionDialog(final Triple<Serializable, String, MaxPointsReason> item) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
final CharSequence[] maxPointsReasons = getAllMaxPointsReasons();
builder.setTitle(R.string.select_penalty_reason)
.setItems(maxPointsReasons, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int position) {
setMaxPointsReasonForItem(item, maxPointsReasons[position]);
}
});
builder.setTitle(R.string.select_penalty_reason).setItems(maxPointsReasons,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int position) {
setMaxPointsReasonForItem(item, maxPointsReasons[position]);
}
});
builder.create();
builder.show();
}
private CharSequence[] getAllMaxPointsReasons() {
List<CharSequence> result = new ArrayList<CharSequence>();
for (MaxPointsReason reason : MaxPointsReason.values()) {
@@ -234,19 +253,22 @@ public class PositioningFragment extends RaceDialogFragment {
return result.toArray(new CharSequence[result.size()]);
}
protected void setMaxPointsReasonForItem(Triple<Serializable, String, MaxPointsReason> item, CharSequence maxPointsReasonName) {
protected void setMaxPointsReasonForItem(Triple<Serializable, String, MaxPointsReason> item,
CharSequence maxPointsReasonName) {
MaxPointsReason maxPointsReason = MaxPointsReason.valueOf(maxPointsReasonName.toString());
Triple<Serializable, String, MaxPointsReason> newItem = new Triple<Serializable, String, MaxPointsReason>(item.getA(), item.getB(), maxPointsReason);
Triple<Serializable, String, MaxPointsReason> newItem = new Triple<Serializable, String, MaxPointsReason>(
item.getA(), item.getB(), maxPointsReason);
int currentIndexOfItem = positionedCompetitors.indexOf(item);
replaceItemInPositioningList(currentIndexOfItem, item, newItem);
if (!maxPointsReason.equals(MaxPointsReason.NONE)) {
setCompetitorToBottomOfPositioningList(newItem);
}
getRace().getState().setFinishPositioningListChanged(positionedCompetitors);
}
private void replaceItemInPositioningList(int currentIndexOfItem, Triple<Serializable, String, MaxPointsReason> item, Triple<Serializable, String, MaxPointsReason> newItem) {
private void replaceItemInPositioningList(int currentIndexOfItem,
Triple<Serializable, String, MaxPointsReason> item, Triple<Serializable, String, MaxPointsReason> newItem) {
positioningAdapter.remove(item);
positioningAdapter.insert(newItem, currentIndexOfItem);
}
@@ -270,24 +292,25 @@ public class PositioningFragment extends RaceDialogFragment {
}
private void addNewCompetitorToPositioningList(Competitor competitor) {
positionedCompetitors.add(new Triple<Serializable, String, MaxPointsReason>(competitor.getId(), competitor.getName(), MaxPointsReason.NONE));
positionedCompetitors.add(new Triple<Serializable, String, MaxPointsReason>(competitor.getId(), competitor
.getName(), MaxPointsReason.NONE));
positioningAdapter.notifyDataSetChanged();
}
protected void onCompetitorRemovedFromPositioningList(Triple<Serializable, String, MaxPointsReason> item) {
Competitor competitor = DomainFactoryImpl.INSTANCE.getExistingCompetitorById(item.getA());
addNewCompetitorToCompetitorList(competitor);
removeCompetitorFromPositionings(item);
getRace().getState().setFinishPositioningListChanged(positionedCompetitors);
}
private void addNewCompetitorToCompetitorList(Competitor competitor) {
competitors.add(competitor);
Collections.sort(competitors, competitorComparator);
competitorsAdapter.notifyDataSetChanged();
}
protected void removeCompetitorFromPositionings(Triple<Serializable, String, MaxPointsReason> item) {
positionedCompetitors.remove(item);
positioningAdapter.notifyDataSetChanged();
@@ -28,7 +28,7 @@ import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.impl.BoatClassSeriesFleet;
public class ProtestTimeDialogFragment extends BaseDialogFragment {
public class ProtestTimeDialogFragment extends AttachedDialogFragment {
private static String ARGS_RACE_IDS = ProtestTimeDialogFragment.class.getSimpleName() + ".raceids";
@@ -84,12 +84,12 @@ public class ProtestTimeDialogFragment extends BaseDialogFragment {
public DialogResultListener getListener() {
return new DialogResultListener() {
@Override
public void onDialogNegativeButton(BaseDialogFragment dialog) {
public void onDialogNegativeButton(AttachedDialogFragment dialog) {
// no operation
}
@Override
public void onDialogPositiveButton(BaseDialogFragment dialog) {
public void onDialogPositiveButton(AttachedDialogFragment dialog) {
setAndAnnounceProtestTime();
}
};
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff. Show more