Merge branch 'ubilabs--rcapp--bug_3011' into bug3622

Conflicts:
	mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/utils/RaceHelper.java

Change-Id: I39313f9a5e9b0c3d079d2f1ad2cf1eadbb0ef844
This commit is contained in:
Axel Uhl committed 2016-04-24 12:48:11 +02:00
commit d9ea36c516
115 files changed
+5793 -4547

No files matched your search

@@ -80,6 +80,10 @@ public class AppConstants {
public final static String INTENT_ACTION_SHOW_MAIN_CONTENT = PACKAGE_NAME + ".action.show.main";
public final static String INTENT_ACTION_SHOW_SUMMARY_CONTENT = PACKAGE_NAME + ".action.show.summary";
/**
* As extra this intent expects the String obtained for a FilterableRace / ManagedRace using
* {@code new RaceGroupSeries(race.getRaceGroup(), race.getSeries()).getDisplayName()}.
*/
public final static String INTENT_ACTION_SHOW_PROTEST = PACKAGE_NAME + ".action.show.protest";
public final static String INTENT_ACTION_REMOVE_PROTEST = PACKAGE_NAME + ".action.remove.protest";
@@ -189,7 +189,7 @@ public enum InMemoryDataStore implements DataStore {
}
private SimpleRaceLogIdentifier convertManagedRaceIdentifierToSimpleRaceLogIdentifier(ManagedRaceIdentifier id) {
return new SimpleRaceLogIdentifierImpl(id.getRaceGroup().getName(), id.getRaceName(), id.getFleet().getName());
return new SimpleRaceLogIdentifierImpl(id.getRaceGroup().getName(), id.getRaceColumnName(), id.getFleet().getName());
}
@Override
@@ -87,8 +87,8 @@ public class OfflineDataManager extends DataManager {
newEvent.getVenue().addCourseArea(new CourseAreaImpl("Stadium", UUID.randomUUID()));
dataStore.addEvent(newEvent);
SeriesWithRows qualifying = new SeriesWithRowsImpl("Qualifying", false, null);
SeriesWithRows medal = new SeriesWithRowsImpl("Medal", true, null);
SeriesWithRows qualifying = new SeriesWithRowsImpl("Qualifying", false, /* isFleetsCanRunInParallel */ true, null);
SeriesWithRows medal = new SeriesWithRowsImpl("Medal", true, /* isFleetsCanRunInParallel */ true, null);
RaceGroup raceGroup = new RaceGroupImpl("ESS", /* displayName */ null, new BoatClassImpl("X40", false), null, Arrays.asList(qualifying,
medal), new EmptyRegattaConfiguration());
@@ -111,7 +111,8 @@ public class OfflineDataManager extends DataManager {
AppPreferences.on(context).getAuthor(), 1, RaceLogRaceStatus.FINISHING));
ManagedRace q1 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("A.B", new FleetImpl("A"), qualifying, raceGroup),
RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration));
RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration),
/* zeroBasedIndexInFleet */ 0);
log = new RaceLogImpl(UUID.randomUUID());
/*
@@ -120,7 +121,8 @@ public class OfflineDataManager extends DataManager {
*/
ManagedRace q2 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("B", new FleetImpl("A.A"), qualifying, raceGroup),
RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration));
RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration),
/* zeroBasedIndexInFleet */ 1);
log = new RaceLogImpl(UUID.randomUUID());
/*
@@ -128,7 +130,8 @@ public class OfflineDataManager extends DataManager {
* RaceLogRaceStatus.FINISHED));
*/
ManagedRace q3 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("Q3", new FleetImpl("Default"), qualifying, raceGroup),
RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration));
RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration),
/* zeroBasedIndexInFleet */ 2);
/*
* ManagedRace m1 = new ManagedRaceImpl( new ManagedRaceIdentifierImpl( "M1", new FleetImpl("Default"), medal,
* raceGroup), null);
@@ -202,7 +202,7 @@ public class OnlineDataManager extends DataManager {
ManagedRaceIdentifier identifier = managedRace.getIdentifier();
// no parameter encoding required here; the UrlHelper.generateUrl call uses Url.Builder which handles encoding
String raceGroupName = identifier.getRaceGroup().getName();
String raceColumnName = identifier.getRaceName();
String raceColumnName = identifier.getRaceColumnName();
String fleetName = identifier.getFleet().getName();
List<Util.Pair<String, Object>> params = new ArrayList<>();
@@ -229,7 +229,7 @@ public class OnlineDataManager extends DataManager {
ManagedRaceIdentifier identifier = managedRace.getIdentifier();
String raceGroupName = identifier.getRaceGroup().getName();
String raceColumnName = identifier.getRaceName();
String raceColumnName = identifier.getRaceColumnName();
String fleetName = identifier.getFleet().getName();
List<Util.Pair<String, Object>> params = new ArrayList<>();
@@ -257,7 +257,7 @@ public class OnlineDataManager extends DataManager {
ManagedRaceIdentifier identifier = managedRace.getIdentifier();
String raceGroupName = identifier.getRaceGroup().getName();
String raceColumnName = identifier.getRaceName();
String raceColumnName = identifier.getRaceColumnName();
String fleetName = identifier.getFleet().getName();
List<Util.Pair<String, Object>> params = new ArrayList<>();
@@ -11,7 +11,6 @@ import org.json.simple.JSONValue;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.configuration.ConfigurationLoader;
import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.base.racegroup.RaceCell;
@@ -29,6 +28,11 @@ import com.sap.sailing.racecommittee.app.utils.ManagedRaceCalculator;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
/**
* Produces an collection of {@link ManagedRace} objects whose order is determined by the order of the {@link RaceGroup}s
* as provided by the JSON object, and within each {@link RaceGroup} by the order of the series in the group, and within
* the series by the order of the "race rows" as provided by the server.
*/
public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace>> {
private final JsonDeserializer<RaceGroup> deserializer;
@@ -45,11 +49,9 @@ public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace
public Collection<ManagedRace> parse(Reader reader) throws Exception {
Object parsedResult = JSONValue.parse(reader);
JSONArray jsonArray = Helpers.toJSONArraySafe(parsedResult);
Collection<ManagedRace> managedRaces = new ArrayList<ManagedRace>();
for (Object element : jsonArray) {
JSONObject json = Helpers.toJSONObjectSafe(element);
RaceGroup group = deserializer.deserialize(json);
addManagedRaces(managedRaces, group);
}
@@ -61,23 +63,25 @@ public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace
for (RaceRow raceRow : series.getRaceRows()) {
Fleet fleet = raceRow.getFleet();
for (RaceCell cell : raceRow.getCells()) {
ManagedRace race = createManagedRace(raceGroup, series, fleet, cell.getName(), cell.getRaceLog(), cell.getFactor(), cell.getExplicitFactor());
ManagedRace race = createManagedRace(raceGroup, series, fleet, cell.getName(), cell.getRaceLog(),
cell.getFactor(), cell.getExplicitFactor(), cell.getZeroBasedIndexInFleet());
target.add(race);
}
}
}
}
private ManagedRace createManagedRace(RaceGroup raceGroup, SeriesBase series, Fleet fleet, String name,
RaceLog raceLog, double factor, Double explicitFactor) {
private ManagedRace createManagedRace(RaceGroup raceGroup, SeriesWithRows series, Fleet fleet, String raceColumnName,
RaceLog raceLog, double factor, Double explicitFactor, int zeroBasedIndexInFleet) {
ConfigurationLoader<RegattaConfiguration> configurationLoader = globalConfigurationLoader;
RegattaConfiguration localConfiguration = raceGroup.getRegattaConfiguration();
if (localConfiguration != null) {
configurationLoader = new MergingRegattaConfigurationLoader(localConfiguration, globalConfigurationLoader);
}
FleetIdentifier fleetIdentifier = new FleetIdentifierImpl(fleet, series, raceGroup);
ManagedRaceIdentifier identifier = new ManagedRaceIdentifierImpl(name, fleetIdentifier);
return new ManagedRaceImpl(identifier, new ManagedRaceCalculator(raceLog, author, configurationLoader), factor, explicitFactor);
ManagedRaceIdentifier identifier = new ManagedRaceIdentifierImpl(raceColumnName, fleetIdentifier);
return new ManagedRaceImpl(identifier, new ManagedRaceCalculator(raceLog, author, configurationLoader),
factor, explicitFactor, zeroBasedIndexInFleet);
}
}
@@ -1,14 +1,14 @@
package com.sap.sailing.racecommittee.app.domain;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
public interface FleetIdentifier {
public Fleet getFleet();
public SeriesBase getSeries();
public SeriesWithRows getSeries();
public RaceGroup getRaceGroup();
@@ -7,13 +7,20 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.racegroup.FilterableRace;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.racecommittee.app.domain.impl.Result;
import com.sap.sse.common.Named;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.WithID;
public interface ManagedRace extends ManagedRaceIdentifier, Named, WithID {
/**
* A managed race's {@link #getName()} is the race column's name.
*
* @author Axel Uhl (d043530)
*
*/
public interface ManagedRace extends FilterableRace, ManagedRaceIdentifier, Named, WithID {
/**
* @return the identifier of the race.
@@ -1,5 +1,5 @@
package com.sap.sailing.racecommittee.app.domain;
public interface ManagedRaceIdentifier extends FleetIdentifier {
public String getRaceName();
public String getRaceColumnName();
}
@@ -1,18 +1,18 @@
package com.sap.sailing.racecommittee.app.domain.impl;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.racecommittee.app.domain.FleetIdentifier;
import com.sap.sse.common.Util.Triple;
public class FleetIdentifierImpl implements FleetIdentifier {
private Fleet fleet;
private SeriesBase series;
private SeriesWithRows series;
private RaceGroup raceGroup;
public FleetIdentifierImpl(Fleet fleet, SeriesBase series, RaceGroup raceGroup) {
public FleetIdentifierImpl(Fleet fleet, SeriesWithRows series, RaceGroup raceGroup) {
this.fleet = fleet;
this.series = series;
this.raceGroup = raceGroup;
@@ -22,7 +22,7 @@ public class FleetIdentifierImpl implements FleetIdentifier {
return fleet;
}
public SeriesBase getSeries() {
public SeriesWithRows getSeries() {
return series;
}
@@ -1,35 +1,35 @@
package com.sap.sailing.racecommittee.app.domain.impl;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.racecommittee.app.domain.FleetIdentifier;
import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier;
public class ManagedRaceIdentifierImpl extends FleetIdentifierImpl implements ManagedRaceIdentifier {
private String raceName;
private String raceColumnName;
public ManagedRaceIdentifierImpl(String raceName, Fleet fleetWithRaceNames, SeriesBase series, RaceGroup raceGroup) {
public ManagedRaceIdentifierImpl(String raceColumnName, Fleet fleetWithRaceNames, SeriesWithRows series, RaceGroup raceGroup) {
super(fleetWithRaceNames, series, raceGroup);
this.raceName = raceName;
this.raceColumnName = raceColumnName;
}
public ManagedRaceIdentifierImpl(String raceName, FleetIdentifier identifier) {
this(raceName, identifier.getFleet(), identifier.getSeries(), identifier.getRaceGroup());
public ManagedRaceIdentifierImpl(String raceColumnName, FleetIdentifier identifier) {
this(raceColumnName, identifier.getFleet(), identifier.getSeries(), identifier.getRaceGroup());
}
public String getRaceName() {
return raceName;
public String getRaceColumnName() {
return raceColumnName;
}
@Override
public String getId() {
return String.format("%s.%s", super.getId(), escapeIdentifierFragment(getRaceName()));
return String.format("%s.%s", super.getId(), escapeIdentifierFragment(getRaceColumnName()));
}
@Override
public String toString() {
return getRaceName() + " - " + getFleet().getName();
return getRaceColumnName() + " - " + getFleet().getName();
}
}
@@ -11,8 +11,9 @@ import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceCell;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.data.AndroidRaceLogResolver;
@@ -21,6 +22,7 @@ import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier;
import com.sap.sailing.racecommittee.app.domain.MapMarker;
import com.sap.sailing.racecommittee.app.utils.ManagedRaceCalculator;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class ManagedRaceImpl implements ManagedRace {
@@ -33,22 +35,39 @@ public class ManagedRaceImpl implements ManagedRace {
private ManagedRaceCalculator calculator;
private double factor;
private Double explicitFactor;
private final int zeroBasedIndexInFleet;
private ManagedRaceImpl(ManagedRaceIdentifier identifier, double factor, Double explicitFactor) {
/**
* @param zeroBasedIndexInFleet
* A Series offers a sequence of RaceColumns, each of them split according to the Fleets modeled for the
* Series. A {@link RaceCell} describes a "slot" in this grid, defined by the series, the fleet and the
* race column. While this object's {@link #getName() name} represents the race column's name, this
* doesn't tell anything about the "horizontal" position in the "grid" or in other words what the index
* is of the race column in which this cell lies.
* <p>
*
* Indices returned by this method start with zero, meaning the first race column in the series. This
* corresponds to what one would get by asking {@link Util#indexOf(Iterable, Object)
* Util.indexOf(series.getRaceColumns(), thisCellsRaceColumn)}, except in case the first race column is a
* "virtual" one that holds a non-discardable carry-forward result. In this case, the second Race Column,
* which is the first "non-virtual" one, receives index 0.
*/
private ManagedRaceImpl(ManagedRaceIdentifier identifier, double factor, Double explicitFactor, int zeroBasedIndexInFleet) {
this.identifier = identifier;
this.competitors = new ArrayList<>();
this.courseOnServer = null;
this.factor = factor;
this.explicitFactor = explicitFactor;
this.zeroBasedIndexInFleet = zeroBasedIndexInFleet;
}
public ManagedRaceImpl(ManagedRaceIdentifier identifier, RaceState state) {
this(identifier, 0, null);
public ManagedRaceImpl(ManagedRaceIdentifier identifier, RaceState state, int zeroBasedIndexInFleet) {
this(identifier, 0, null, zeroBasedIndexInFleet);
this.state = state;
}
public ManagedRaceImpl(ManagedRaceIdentifier identifier, ManagedRaceCalculator calculator, double factor, Double explicitFactor) {
this(identifier, factor, explicitFactor);
public ManagedRaceImpl(ManagedRaceIdentifier identifier, ManagedRaceCalculator calculator, double factor, Double explicitFactor, int zeroBasedIndexInFleet) {
this(identifier, factor, explicitFactor, zeroBasedIndexInFleet);
this.calculator = calculator;
}
@@ -64,11 +83,11 @@ public class ManagedRaceImpl implements ManagedRace {
@Override
public String getName() {
return identifier.getRaceName();
return identifier.getRaceColumnName();
}
@Override
public String getRaceName() {
public String getRaceColumnName() {
return getName();
}
@@ -78,7 +97,7 @@ public class ManagedRaceImpl implements ManagedRace {
}
@Override
public SeriesBase getSeries() {
public SeriesWithRows getSeries() {
return identifier.getSeries();
}
@@ -201,4 +220,31 @@ public class ManagedRaceImpl implements ManagedRace {
public String toString() {
return "ManagedRaceImpl [identifier=" + identifier + "]";
}
@Override
public int getZeroBasedIndexInFleet() {
int result = -1;
if (zeroBasedIndexInFleet != -1) {
// it was properly delivered by a compatible server; that's our result
result = zeroBasedIndexInFleet;
} else {
// we deal with an incompatible server that doesn't know about this field yet;
// try to compute from the surrounding race group:
int i=0;
for (final RaceCell cell : getSeries().getRaceRow(getFleet()).getCells()) {
if (cell.getName().equals(getRaceColumnName())) {
result = i;
break;
}
i++;
}
// if a cell with this race's race column name is not found, leave the index at -1
}
return result;
}
@Override
public int getZeroBasedSeriesIndex() {
return Util.indexOf(getRaceGroup().getSeries(), getSeries());
}
}
@@ -1,137 +0,0 @@
package com.sap.sailing.racecommittee.app.domain.impl;
import android.text.TextUtils;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sse.common.Util;
public class RaceGroupSeriesFleet {
private RaceGroup raceGroup;
private Fleet fleet;
private SeriesBase series;
private int seriesOrder;
private int fleetOrder;
public RaceGroupSeriesFleet(ManagedRace race) {
raceGroup = race.getRaceGroup();
series = race.getSeries();
fleet = race.getFleet();
seriesOrder = getSeriesIndex(race, series);
fleetOrder = getFleetIndex(series.getFleets(), race.getFleet());
}
private static int getSeriesIndex(ManagedRace race, SeriesBase series) {
return Util.indexOf(race.getRaceGroup().getSeries(), series);
}
public RaceGroup getRaceGroup() {
return raceGroup;
}
public SeriesBase getSeries() {
return series;
}
public Fleet getFleet() {
return fleet;
}
public String getRaceGroupName() {
return raceGroup.getName();
}
public String getSeriesName() {
return series.getName();
}
public String getFleetName() {
return fleet.getName();
}
public String getDisplayName() {
return getDisplayName(false);
}
public String getDisplayName(boolean useDisplayName) {
String name = raceGroup.getDisplayName();
if (!useDisplayName || TextUtils.isEmpty(name)) {
name = raceGroup.getName();
}
if (series != null && !series.getName().equals(LeaderboardNameConstants.DEFAULT_SERIES_NAME)) {
name += " - " + series.getName();
}
if (fleet != null && !fleet.getName().equals(LeaderboardNameConstants.DEFAULT_FLEET_NAME)) {
name += " - " + fleet.getName();
}
return name;
}
public int getSeriesOrder() {
return seriesOrder;
}
public int getFleetOrder() {
return fleetOrder;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((raceGroup == null) ? 0 : raceGroup.hashCode());
result = prime * result + ((series == null) ? 0 : series.hashCode());
result = prime * result + ((fleet == null) ? 0 : fleet.hashCode());
result = prime * result + seriesOrder;
result = prime * result + fleetOrder;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RaceGroupSeriesFleet other = (RaceGroupSeriesFleet) obj;
if (raceGroup == null) {
if (other.raceGroup != null) {
return false;
}
} else if (!raceGroup.equals(other.raceGroup)) {
return false;
}
if (fleet == null) {
if (other.fleet != null) {
return false;
}
} else if (!fleet.equals(other.fleet)) {
return false;
}
if (series == null) {
if (other.series != null) {
return false;
}
} else if (!series.equals(other.series)) {
return false;
}
if (seriesOrder != other.seriesOrder) {
return false;
}
if (fleetOrder != other.fleetOrder) {
return false;
}
return true;
}
private int getFleetIndex(Iterable<? extends Fleet> fleets, Fleet fleet) {
return Util.indexOf(fleets, fleet);
}
}
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleet;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.racecommittee.app.AppConstants;
@@ -26,7 +27,6 @@ import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager;
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;
import com.sap.sailing.racecommittee.app.domain.impl.RaceGroupSeriesFleet;
import com.sap.sailing.racecommittee.app.logging.LogEvent;
import com.sap.sailing.racecommittee.app.services.RaceStateService;
import com.sap.sailing.racecommittee.app.ui.adapters.racelist.RaceListDataType;
@@ -689,13 +689,10 @@ public class RacingActivity extends SessionActivity implements RaceListCallbacks
ExLog.i(RacingActivity.this, TAG, "Same races are already loaded...");
} else {
lastSeenRaces = data;
registerOnService(data);
mRaceList.setupOn(data);
Toast.makeText(RacingActivity.this, String.format(getString(R.string.racing_load_success), data.size()), Toast.LENGTH_SHORT).show();
}
setProgressSpinnerVisibility(false);
}
}
@@ -2,22 +2,14 @@ package com.sap.sailing.racecommittee.app.ui.adapters.racelist;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.Map;
import java.util.Set;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.BitmapHelper;
@@ -28,6 +20,10 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderRes
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.FlagPoleState;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.CurrentRaceComparator;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeries;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesComparator;
import com.sap.sailing.domain.common.racelog.FlagPole;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.racecommittee.app.AppConstants;
@@ -43,15 +39,32 @@ import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> implements FilterSubscriber {
private final static String TAG = ManagedRaceListAdapter.class.getName();
private final Object mLockObject = new Object();
private List<RaceListDataType> mAllViewItems;
private RaceFilter mFilter;
private final RaceFilter mFilter;
private LayoutInflater mInflater;
private Resources mResources;
private List<RaceListDataType> mShownViewItems;
private final List<RaceListDataType> mShownViewItems;
private final Map<ManagedRace, RaceListDataTypeRace> viewItemsRaces;
private final Map<SeriesBase, RaceListDataTypeHeader> viewItemsSeriesHeaders;
private ImageView marker;
private ImageView update_badge;
private LinearLayout race_flag;
@@ -67,6 +80,7 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
private TextView boat_class;
private TextView fleet_series;
private ImageView protest_image;
private final Set<ImageView> protestFlagsWithClickListenerSet;
private ImageView has_dependent_races;
private SimpleDateFormat dateFormat;
private RaceListDataType mSelectedRace;
@@ -75,12 +89,17 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
private TextView explicit_factor;
private int flag_size;
private DecimalFormat factor_format;
private final Set<ManagedRace> mAllRaces;
public ManagedRaceListAdapter(Context context, List<RaceListDataType> viewItems) {
public ManagedRaceListAdapter(Context context, Set<ManagedRace> allRaces) {
super(context, 0);
mAllViewItems = viewItems;
mShownViewItems = viewItems;
protestFlagsWithClickListenerSet = new HashSet<>();
mAllRaces = allRaces;
mShownViewItems = new ArrayList<>();
this.viewItemsRaces = new HashMap<>();
this.viewItemsSeriesHeaders = new HashMap<>();
createViewItemsForRacesAndSeries();
mFilter = new RaceFilter(allRaces, this);
mInflater = LayoutInflater.from(getContext());
mResources = getContext().getResources();
dateFormat = new SimpleDateFormat("kk:mm", getContext().getResources().getConfiguration().locale);
@@ -88,6 +107,18 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
factor_format = new DecimalFormat(context.getString(R.string.race_factor_format));
}
private void createViewItemsForRacesAndSeries() {
viewItemsRaces.clear();
viewItemsSeriesHeaders.clear();
for (final ManagedRace race : mAllRaces) {
viewItemsRaces.put(race, new RaceListDataTypeRace(race, mInflater));
final SeriesBase series = race.getSeries();
if (!viewItemsSeriesHeaders.containsKey(series)) {
viewItemsSeriesHeaders.put(series, new RaceListDataTypeHeader(new RaceGroupSeries(race), mInflater));
}
}
}
@Override
public int getCount() {
synchronized (mLockObject) {
@@ -97,9 +128,6 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
@Override
public RaceFilter getFilter() {
if (mFilter == null) {
mFilter = new RaceFilter(mAllViewItems, this);
}
return mFilter;
}
@@ -122,18 +150,13 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
@Override
public View getView(int position, View convertView, ViewGroup parent) {
RaceListDataType raceListElement;
raceListElement = getItem(position);
final RaceListDataType raceListElement = getItem(position);
int type = getItemViewType(position);
TimePoint now = MillisecondsTimePoint.now();
if (convertView == null) {
if (type == ViewType.HEADER.index) {
convertView = mInflater.inflate(R.layout.race_list_area_header, parent, false);
} else if (type == ViewType.RACE.index) {
convertView = mInflater.inflate(R.layout.race_list_area_item, parent, false);
}
convertView = raceListElement.getView(parent);
}
findViews(convertView);
resetValues(convertView);
@@ -145,25 +168,25 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
regatta = header.getRaceGroup().getName();
}
boat_class.setText(regatta);
if (header.isFleetVisible()) {
fleet_series.setText(RaceHelper.getFleetSeries(header.getFleet(), header.getSeries()));
} else {
fleet_series.setText(RaceHelper.getSeriesName(header.getSeries(), ""));
}
fleet_series.setText(RaceHelper.getSeriesName(header.getSeries(), ""));
if (fleet_series.getText().length() == 0) {
fleet_series.setVisibility(View.GONE);
} else {
fleet_series.setVisibility(View.VISIBLE);
}
protest_image.setImageDrawable(FlagsResources.getFlagDrawable(getContext(), Flags.BRAVO.name(), flag_size));
protest_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(AppConstants.INTENT_ACTION_SHOW_PROTEST);
intent.putExtra(AppConstants.INTENT_ACTION_EXTRA, header.toString());
BroadcastManager.getInstance(getContext()).addIntent(intent);
}
});
if (!protestFlagsWithClickListenerSet.contains(protest_image)) {
protestFlagsWithClickListenerSet.add(protest_image);
protest_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(AppConstants.INTENT_ACTION_SHOW_PROTEST);
// TODO don't use toString() to convey semantics and perform check; bug 3617
intent.putExtra(AppConstants.INTENT_ACTION_EXTRA, new RaceGroupSeries(header.getRaceGroup(), header.getSeries()).getDisplayName());
BroadcastManager.getInstance(getContext()).addIntent(intent);
}
});
}
} else if (type == ViewType.RACE.index) {
final RaceListDataTypeRace race = (RaceListDataTypeRace) raceListElement;
@@ -259,13 +282,82 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
}
@Override
public void onResult(List<RaceListDataType> filtered) {
public void onResult(List<ManagedRace> filteredRaces) {
synchronized (mLockObject) {
mShownViewItems = filtered;
mShownViewItems.clear();
if (filteredRaces != null) {
mShownViewItems.addAll(getShownViewItems(filteredRaces));
}
notifyDataSetChanged();
}
}
/**
* For the {@code filteredRaces} ensure that exactly the required view items are in {@link #mShownViewItems}. This
* encompasses the header items for all non-empty series and the race items for all races in their series.
*/
private List<RaceListDataType> getShownViewItems(List<ManagedRace> races) {
final Map<RaceListDataTypeHeader, List<RaceListDataTypeRace>> raceItemsByHeader = getRaceItemsGroupedBySeriesHeader(races);
final List<RaceListDataType> result = serializeItems(raceItemsByHeader);
return result;
}
private List<RaceListDataType> serializeItems(final Map<RaceListDataTypeHeader, List<RaceListDataTypeRace>> raceItemsByHeader) {
final List<RaceListDataType> result = new ArrayList<>();
final List<RaceListDataTypeHeader> headers = new ArrayList<>(raceItemsByHeader.keySet());
Collections.sort(headers, new Comparator<RaceListDataTypeHeader>() {
private final RaceGroupSeriesComparator c = new RaceGroupSeriesComparator();
@Override
public int compare(RaceListDataTypeHeader lhs, RaceListDataTypeHeader rhs) {
return c.compare(lhs.getRegattaSeries(), rhs.getRegattaSeries());
}
});
for (final RaceListDataTypeHeader header : headers) {
result.add(header);
final List<RaceListDataTypeRace> raceItems = new ArrayList<>(raceItemsByHeader.get(header));
Collections.sort(raceItems, new Comparator<RaceListDataTypeRace>() {
final CurrentRaceComparator c = new CurrentRaceComparator();
@Override
public int compare(RaceListDataTypeRace lhs, RaceListDataTypeRace rhs) {
final int result;
if (lhs != null && rhs != null) {
result = c.compare(lhs.getRace(), rhs.getRace());
} else {
Log.e(TAG, "Internal error; found null for NavDrawer item while sorting");
result = 0;
}
return result;
}
});
for (final RaceListDataTypeRace raceItem : raceItems) {
result.add(raceItem);
}
}
return result;
}
private Map<RaceListDataTypeHeader, List<RaceListDataTypeRace>> getRaceItemsGroupedBySeriesHeader(final List<ManagedRace> filteredRaces) {
final Map<RaceListDataTypeHeader, List<RaceListDataTypeRace>> raceItemsByHeader = new HashMap<>();
for (final ManagedRace race : filteredRaces) {
final SeriesBase series = race.getSeries();
final RaceListDataTypeHeader seriesHeader = viewItemsSeriesHeaders.get(series);
List<RaceListDataTypeRace> raceItemsInSeries = raceItemsByHeader.get(seriesHeader);
if (raceItemsInSeries == null) {
raceItemsInSeries = new ArrayList<>();
raceItemsByHeader.put(seriesHeader, raceItemsInSeries);
}
final RaceListDataTypeRace viewItemForRace = viewItemsRaces.get(race);
if (viewItemForRace != null) {
raceItemsInSeries.add(viewItemForRace);
} else {
Log.w(TAG, "A view item for race "+race+" provided by the filter could not be found", new RuntimeException("Here is where it happened"));
}
}
return raceItemsByHeader;
}
public void setSelectedRace(RaceListDataType id) {
mSelectedRace = id;
}
@@ -450,4 +542,11 @@ public class ManagedRaceListAdapter extends ArrayAdapter<RaceListDataType> imple
this.index = index;
}
}
public void onRacesChanged() {
synchronized (mLockObject) {
createViewItemsForRacesAndSeries();
getFilter().refreshRegattaStructures();
}
}
}
@@ -1,33 +1,46 @@
package com.sap.sailing.racecommittee.app.ui.adapters.racelist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import com.sap.sailing.domain.base.racegroup.CurrentRaceFilter;
import com.sap.sailing.domain.base.racegroup.impl.CurrentRaceFilterImpl;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.ui.fragments.RaceListFragment.FilterMode;
import android.widget.Filter;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.racecommittee.app.ui.fragments.RaceListFragment.FilterMode;
/**
* Filters races by status.
*
* For each "run" of unscheduled races only the first will be taken.
* For each "run" of finished races only the last will be taken.
* Filters {@link ManagedRace} objects by status and place in the regatta structure. See {@link CurrentRaceFilter}. If
* the {@link #filterMode} is {@link FilterMode#ALL}, no filtering takes place at all, and all races will be displayed.
* If the {@link #filterMode} is {@link FilterMode#ACTIVE}, the set of races to be displayed is computed using a
* {@link CurrentRaceFilter}.
*/
public class RaceFilter extends Filter {
/**
* A filter that is updated each time an update for {@link #allRaces} is announced by {@link #refreshRegattaStructures()}.
*/
private CurrentRaceFilter<ManagedRace> currentRaceFilter;
public interface FilterSubscriber {
void onResult(List<RaceListDataType> filtered);
void onResult(List<ManagedRace> filtered);
}
protected final FilterSubscriber subscriber;
protected Collection<RaceListDataType> items;
protected FilterMode filterMode;
private final FilterSubscriber subscriber;
private final Set<ManagedRace> allRaces;
private FilterMode filterMode;
public RaceFilter(Collection<RaceListDataType> items, FilterSubscriber resultSubscriber) {
this.items = items;
/**
* @param allRaces
* callers must call {@link #refreshRegattaStructures()} whenever the contents of this set have changed
* @param resultSubscriber
* triggered when the filter results have changed
*/
public RaceFilter(Set<ManagedRace> allRaces, FilterSubscriber resultSubscriber) {
this.allRaces = allRaces;
this.subscriber = resultSubscriber;
refreshRegattaStructures();
}
public void filterByMode(FilterMode filterMode) {
@@ -35,7 +48,7 @@ public class RaceFilter extends Filter {
filter("");
}
protected static FilterResults createResults(Collection<RaceListDataType> result) {
protected static FilterResults createResults(Set<ManagedRace> result) {
FilterResults results = new FilterResults();
results.values = new ArrayList<>(result);
results.count = result.size();
@@ -44,87 +57,26 @@ public class RaceFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
final FilterResults result;
if (filterMode == null || filterMode.equals(FilterMode.ALL)) {
return createResults(items);
result = createResults(allRaces);
} else {
// filter using the CurrentRaceFilter
result = createResults(currentRaceFilter.getCurrentRaces());
}
List<RaceListDataType> filteredItems = new ArrayList<RaceListDataType>();
RaceListDataTypeRace currentUnscheduledItem = null;
RaceListDataTypeRace currentFinishedItem = null;
int finishedItems = 0;
int subItems = 0;
List<RaceListDataTypeHeader> headersToRemove= new ArrayList<>();
RaceListDataTypeHeader currentHeader = null;
List<RaceListDataType> allItems = new ArrayList<RaceListDataType>(items);
for (RaceListDataType item : allItems) {
if (item instanceof RaceListDataTypeHeader) {
if (currentHeader != null && subItems > 0 && subItems == finishedItems) {
headersToRemove.add(currentHeader);
}
RaceListDataTypeHeader headerItem = (RaceListDataTypeHeader) item;
filteredItems.add(headerItem);
// new run for all types!
currentHeader = (RaceListDataTypeHeader) item;
currentUnscheduledItem = null;
currentFinishedItem = null;
finishedItems = 0;
subItems = 0;
} else if (item instanceof RaceListDataTypeRace) {
subItems ++;
RaceListDataTypeRace raceItem = (RaceListDataTypeRace) item;
RaceLogRaceStatus status = raceItem.getCurrentStatus();
if (currentUnscheduledItem == null && status.equals(RaceLogRaceStatus.UNSCHEDULED)) {
filteredItems.add(raceItem);
currentUnscheduledItem = raceItem;
} else if (status.equals(RaceLogRaceStatus.FINISHED)) {
finishedItems ++;
if (filteredItems.contains(currentFinishedItem)) {
filteredItems.remove(currentFinishedItem);
}
filteredItems.add(raceItem);
currentFinishedItem = raceItem;
currentUnscheduledItem = null;
} else if (RaceLogRaceStatus.isActive(status)) {
filteredItems.add(raceItem);
// new run for all types!
currentUnscheduledItem = null;
currentFinishedItem = null;
}
}
}
if (currentHeader != null && subItems > 0 && subItems == finishedItems) {
headersToRemove.add(currentHeader);
}
removeFinishedSeries(filteredItems, headersToRemove);
return createResults(filteredItems);
return result;
}
private void removeFinishedSeries(List<RaceListDataType> filteredItems, List<RaceListDataTypeHeader> headersToRemove) {
List<RaceListDataType> itemsToKeep = new ArrayList<>();
boolean keepItems = false;
for (RaceListDataType item : filteredItems) {
if (item instanceof RaceListDataTypeHeader) {
keepItems = !headersToRemove.contains(item);
if (keepItems) {
itemsToKeep.add(item);
}
} else if (item instanceof RaceListDataTypeRace) {
if (keepItems) {
itemsToKeep.add(item);
}
}
}
filteredItems.clear();
filteredItems.addAll(itemsToKeep);
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
subscriber.onResult((List<RaceListDataType>) results.values);
@SuppressWarnings("unchecked")
final List<ManagedRace> filterResults = (List<ManagedRace>) results.values;
subscriber.onResult(filterResults);
}
public void refreshRegattaStructures() {
this.currentRaceFilter = new CurrentRaceFilterImpl<ManagedRace>(allRaces);
filter("");
}
}
@@ -1,5 +1,7 @@
package com.sap.sailing.racecommittee.app.ui.adapters.racelist;
import android.view.View;
import android.view.ViewGroup;
/*
* author: I065873
@@ -7,6 +9,5 @@ package com.sap.sailing.racecommittee.app.ui.adapters.racelist;
* Data Type for displaying the Race list in the left part of the second activity
*/
public interface RaceListDataType {
View getView(ViewGroup parent);
}
@@ -1,25 +1,29 @@
package com.sap.sailing.racecommittee.app.ui.adapters.racelist;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.IsFleetFragment;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.racecommittee.app.domain.impl.RaceGroupSeriesFleet;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeries;
import com.sap.sailing.racecommittee.app.R;
public class RaceListDataTypeHeader implements RaceListDataType {
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
private RaceGroupSeriesFleet data;
private boolean isFleetVisible;
public class RaceListDataTypeHeader implements RaceListDataType, IsFleetFragment {
private RaceGroupSeries data;
private final LayoutInflater mInflater;
public RaceListDataTypeHeader(RaceGroupSeriesFleet data) {
this(data, true);
public RaceListDataTypeHeader(RaceGroupSeries data, LayoutInflater layoutInflater) {
this(data, true, layoutInflater);
}
public RaceListDataTypeHeader(RaceGroupSeriesFleet data, boolean isFleetVisible) {
public RaceListDataTypeHeader(RaceGroupSeries data, boolean isFleetVisible, LayoutInflater layoutInflater) {
this.data = data;
this.isFleetVisible = isFleetVisible;
this.mInflater = layoutInflater;
}
public RaceGroupSeriesFleet getRegattaSeriesFleet() {
public RaceGroupSeries getRegattaSeries() {
return data;
}
@@ -31,14 +35,14 @@ public class RaceListDataTypeHeader implements RaceListDataType {
return data.getSeries();
}
public Fleet getFleet() {
return data.getFleet();
}
public boolean isFleetVisible() { return isFleetVisible; }
@Override
public String toString() {
return data.getDisplayName();
}
@Override
public View getView(ViewGroup parent) {
return mInflater.inflate(R.layout.race_list_area_header, parent, false);
}
}
@@ -5,11 +5,14 @@ import java.text.SimpleDateFormat;
import java.util.Locale;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleet;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.impl.RaceGroupSeriesFleet;
import com.sap.sse.common.TimePoint;
public class RaceListDataTypeRace implements RaceListDataType {
@@ -23,10 +26,11 @@ public class RaceListDataTypeRace implements RaceListDataType {
private static String finishedTemplate;
private static String unknownTemplate;
private final ManagedRace race;
private final RaceGroupSeriesFleet fleet;
private final LayoutInflater mInflater;
private boolean updateIndicatorVisible = false;
private RaceLogRaceStatus currentStatus = RaceLogRaceStatus.UNKNOWN;
private ManagedRace race;
private RaceGroupSeriesFleet fleet;
private Format scheduleFormatter = new SimpleDateFormat("HH:mm", Locale.US);
@@ -41,14 +45,15 @@ public class RaceListDataTypeRace implements RaceListDataType {
unknownTemplate = fragment.getString(R.string.racelist_unknown);
}
public RaceListDataTypeRace(ManagedRace race) {
this(race, null);
public RaceListDataTypeRace(ManagedRace race, LayoutInflater layoutInflater) {
this(race, null, layoutInflater);
}
public RaceListDataTypeRace(ManagedRace race, RaceGroupSeriesFleet fleet) {
public RaceListDataTypeRace(ManagedRace race, RaceGroupSeriesFleet fleet, LayoutInflater layoutInflater) {
this.race = race;
this.fleet = fleet;
this.currentStatus = race.getStatus();
this.mInflater = layoutInflater;
}
public void onStatusChanged(RaceLogRaceStatus status, boolean allowUpdateIndicator) {
@@ -114,4 +119,9 @@ public class RaceListDataTypeRace implements RaceListDataType {
return scheduleFormatter.format(startTime.asDate());
}
@Override
public View getView(ViewGroup parent) {
return mInflater.inflate(R.layout.race_list_area_item, parent, false);
}
}
@@ -2,13 +2,38 @@ package com.sap.sailing.racecommittee.app.ui.fragments;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.AppUtils;
import com.sap.sailing.android.shared.util.BitmapHelper;
import com.sap.sailing.android.shared.util.BroadcastManager;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.BaseRaceStateChangedListener;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeries;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleet;
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.RaceApplication;
import com.sap.sailing.racecommittee.app.data.DataManager;
import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.ui.activities.SessionActivity;
import com.sap.sailing.racecommittee.app.ui.adapters.racelist.ManagedRaceListAdapter;
import com.sap.sailing.racecommittee.app.ui.adapters.racelist.RaceListDataType;
import com.sap.sailing.racecommittee.app.ui.adapters.racelist.RaceListDataTypeRace;
import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.ProtestTimeDialogFragment;
import com.sap.sailing.racecommittee.app.utils.StringHelper;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
import com.sap.sailing.racecommittee.app.utils.TickListener;
import com.sap.sailing.racecommittee.app.utils.TickSingleton;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import android.app.Activity;
import android.app.FragmentTransaction;
import android.content.BroadcastReceiver;
@@ -41,34 +66,6 @@ import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.AppUtils;
import com.sap.sailing.android.shared.util.BitmapHelper;
import com.sap.sailing.android.shared.util.BroadcastManager;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.BaseRaceStateChangedListener;
import com.sap.sailing.domain.base.SeriesBase;
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.RaceApplication;
import com.sap.sailing.racecommittee.app.data.DataManager;
import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.impl.RaceGroupSeriesFleet;
import com.sap.sailing.racecommittee.app.ui.activities.SessionActivity;
import com.sap.sailing.racecommittee.app.ui.adapters.racelist.ManagedRaceListAdapter;
import com.sap.sailing.racecommittee.app.ui.adapters.racelist.RaceListDataType;
import com.sap.sailing.racecommittee.app.ui.adapters.racelist.RaceListDataTypeHeader;
import com.sap.sailing.racecommittee.app.ui.adapters.racelist.RaceListDataTypeRace;
import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.ProtestTimeDialogFragment;
import com.sap.sailing.racecommittee.app.utils.RaceHelper;
import com.sap.sailing.racecommittee.app.utils.StringHelper;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
import com.sap.sailing.racecommittee.app.utils.TickListener;
import com.sap.sailing.racecommittee.app.utils.TickSingleton;
import com.sap.sse.common.TimePoint;
public class RaceListFragment extends LoggableFragment implements OnItemClickListener, OnItemSelectedListener, TickListener, OnScrollListener {
private final static String TAG = RaceListFragment.class.getName();
@@ -88,8 +85,9 @@ public class RaceListFragment extends LoggableFragment implements OnItemClickLis
private ManagedRace mSelectedRace;
private IntentReceiver mReceiver;
private boolean mUpdateList = true;
private ArrayList<RaceListDataType> mViewItems;
private View mProgress;
private final Set<ManagedRace> mAllRaces;
private BaseRaceStateChangedListener stateListener = new BaseRaceStateChangedListener() {
@Override
public void onStartTimeChanged(ReadonlyRaceState state) {
@@ -106,13 +104,12 @@ public class RaceListFragment extends LoggableFragment implements OnItemClickLis
filterChanged();
}
};
public RaceListFragment() {
mFilterMode = FilterMode.ACTIVE;
mSelectedRace = null;
mManagedRacesById = new LinkedHashMap<>();
mRacesByGroup = new LinkedHashMap<>();
mViewItems = new ArrayList<>();
mAllRaces = new HashSet<>();
}
public static RaceListFragment newInstance(int layout) {
@@ -125,9 +122,7 @@ public class RaceListFragment extends LoggableFragment implements OnItemClickLis
public static void showProtest(Context context, ManagedRace race) {
Intent intent = new Intent(AppConstants.INTENT_ACTION_SHOW_PROTEST);
String extra = race.getRaceGroup().getName();
extra += RaceHelper.getSeriesName(race.getSeries());
extra += RaceHelper.getFleetName(race.getFleet());
String extra = new RaceGroupSeries(race.getRaceGroup(), race.getSeries()).getDisplayName();
intent.putExtra(AppConstants.INTENT_ACTION_EXTRA, extra);
BroadcastManager.getInstance(context).addIntent(intent);
}
@@ -199,11 +194,9 @@ public class RaceListFragment extends LoggableFragment implements OnItemClickLis
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
RaceListDataTypeRace.initializeTemplates(this);
if (mListView != null) {
mAdapter = new ManagedRaceListAdapter(getActivity(), mViewItems);
mAdapter = new ManagedRaceListAdapter(getActivity(), mAllRaces);
mListView.setAdapter(mAdapter);
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
mListView.setOnItemClickListener(this);
@@ -316,9 +309,7 @@ public class RaceListFragment extends LoggableFragment implements OnItemClickLis
@Override
public void onResume() {
super.onResume();
TickSingleton.INSTANCE.registerListener(this);
IntentFilter filter = new IntentFilter();
filter.addAction(AppConstants.INTENT_ACTION_SHOW_PROTEST);
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mReceiver, filter);
@@ -346,7 +337,6 @@ public class RaceListFragment extends LoggableFragment implements OnItemClickLis
@Override
public void onStart() {
super.onStart();
unregisterOnAllRaces();
registerOnAllRaces();
for (ManagedRace race : mManagedRacesById.values()) {
@@ -378,154 +368,57 @@ public class RaceListFragment extends LoggableFragment implements OnItemClickLis
@Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
mUpdateList = false;
}
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
mUpdateList = true;
}
};
mDrawerLayout.post(new Runnable() {
@Override
public void run() {
mDrawerToggle.syncState();
}
});
mDrawerLayout.setDrawerListener(mDrawerToggle);
if (mCourse != null) {
SpannableString text = new SpannableString(course);
StyleSpan spanBold = new StyleSpan(Typeface.BOLD);
text.setSpan(spanBold, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mCourse.setText(text);
}
if (mData != null) {
mData.setText(StringHelper.on(getActivity()).getAuthor(author));
}
}
private void initializeRacesByGroup() {
mRacesByGroup.clear();
for (ManagedRace race : mManagedRacesById.values()) {
RaceGroupSeriesFleet container = new RaceGroupSeriesFleet(race);
if (!mRacesByGroup.containsKey(container)) {
mRacesByGroup.put(container, new LinkedList<ManagedRace>());
}
mRacesByGroup.get(container).add(race);
}
}
private void initializeViewElements() {
// 1. Group races by <boat class, series, fleet>
initializeRacesByGroup();
// 2. Remove previous view items
mViewItems.clear();
// 3. Group active fleets from tree by series
Map<SeriesBase, List<RaceGroupSeriesFleet>> seriesWithFleets = getFleetsGroupedBySeries(mRacesByGroup.keySet());
// 4. Create view elements from series with fleets
for (SeriesBase series : seriesWithFleets.keySet()) {
List<RaceGroupSeriesFleet> fleets = seriesWithFleets.get(series);
if (fleets.size() == 1) { // Default fleet
// ... add the header view for default fleet...
RaceGroupSeriesFleet fleet = fleets.get(0);
mViewItems.add(new RaceListDataTypeHeader(fleet));
List<ManagedRace> races = mRacesByGroup.get(fleet);
for (ManagedRace race : races) {
// ... and add the race view!
mViewItems.add(new RaceListDataTypeRace(race));
}
} else if (fleets.size() > 1) {
// ... add the header view for all following fleets...
mViewItems.add(new RaceListDataTypeHeader(fleets.get(0), false));
// ... and add all the sorted race views!
List<RaceListDataTypeRace> raceListDataTypeRaces = getRaceListDataTypeRaces(mRacesByGroup, fleets);
if (fleetsEqual(fleets)) {
raceListDataTypeRaces = sortRaceList(raceListDataTypeRaces, fleets.size());
}
for (RaceListDataTypeRace raceListDataTypeRace : raceListDataTypeRaces) {
mViewItems.add(raceListDataTypeRace);
}
}
}
}
private boolean fleetsEqual(List<RaceGroupSeriesFleet> fleets) {
if (!fleets.isEmpty()) {
int lastFleetOrder = fleets.get(0).getFleet().getOrdering();
for (RaceGroupSeriesFleet fleet : fleets) {
int currentFleetOrder = fleet.getFleet().getOrdering();
if (currentFleetOrder != lastFleetOrder) {
return false;
}
}
}
return true;
}
private List<RaceListDataTypeRace> sortRaceList(List<RaceListDataTypeRace> raceList, int fleetSize) {
int racePerFleet = raceList.size() / fleetSize;
List<RaceListDataTypeRace> sortedList = new ArrayList<>();
for (int race = 0; race < racePerFleet; race++) {
for (int fleet = 0; fleet < fleetSize; fleet++) {
sortedList.add(raceList.get(race + (fleet * racePerFleet)));
}
}
return sortedList;
}
private static LinkedHashMap<SeriesBase, List<RaceGroupSeriesFleet>> getFleetsGroupedBySeries(Set<RaceGroupSeriesFleet> raceGroupSeriesFleets) {
LinkedHashMap<SeriesBase, List<RaceGroupSeriesFleet>> seriesWithFleets = new LinkedHashMap<>();
for (RaceGroupSeriesFleet key : raceGroupSeriesFleets) {
SeriesBase series = key.getSeries();
List<RaceGroupSeriesFleet> fleets = seriesWithFleets.get(series);
if (fleets == null) {
fleets = new ArrayList<>();
}
fleets.add(key);
seriesWithFleets.put(series, fleets);
}
return seriesWithFleets;
}
private static List<RaceListDataTypeRace> getRaceListDataTypeRaces(LinkedHashMap<RaceGroupSeriesFleet, List<ManagedRace>> racesByGroup,
List<RaceGroupSeriesFleet> fleets) {
List<RaceListDataTypeRace> races = new ArrayList<>();
for (RaceGroupSeriesFleet fleet : fleets) {
for (ManagedRace race : racesByGroup.get(fleet)) {
races.add(new RaceListDataTypeRace(race, fleet));
}
}
return races;
}
public void setupOn(Collection<ManagedRace> races) {
ExLog.i(getActivity(), TAG, String.format("Setting up %s with %d races.", this.getClass().getSimpleName(), races.size()));
public void setupOn(Iterable<ManagedRace> races) {
ExLog.i(getActivity(), TAG, String.format("Setting up %s with %d races.", this.getClass().getSimpleName(), Util.size(races)));
unregisterOnAllRaces();
mManagedRacesById.clear();
mRacesByGroup.clear();
for (ManagedRace managedRace : races) {
mManagedRacesById.put(managedRace.getId(), managedRace);
final RaceGroupSeriesFleet key = new RaceGroupSeriesFleet(managedRace);
List<ManagedRace> raceList = mRacesByGroup.get(key);
if (raceList == null) {
raceList = new ArrayList<>();
mRacesByGroup.put(key, raceList);
}
raceList.add(managedRace);
}
mAllRaces.clear();
Util.addAll(races, mAllRaces);
registerOnAllRaces();
initializeViewElements();
// prepare views and do initial filtering
filterChanged();
// prepare views and do initial filtering; update the adapter first, so it can create the new
// view items for the new races; then trigger the filter
mAdapter.onRacesChanged();
mAdapter.notifyDataSetChanged();
filterChanged();
}
public void openDrawer() {
@@ -550,10 +443,11 @@ public class RaceListFragment extends LoggableFragment implements OnItemClickLis
}
}
private void showProtestTimeDialog(String raceGroupName) {
private void showProtestTimeDialog(String raceGroupSeriesDisplayName) {
// Find the race group for which the
for (RaceGroupSeriesFleet raceGroupSeriesFleet : mRacesByGroup.keySet()) {
Boolean matchingRaceGroup = raceGroupName.equals(raceGroupSeriesFleet.getDisplayName());
Boolean matchingRaceGroup = raceGroupSeriesDisplayName.equals(
new RaceGroupSeries(raceGroupSeriesFleet.getRaceGroup(), raceGroupSeriesFleet.getSeries()).getDisplayName());
if (matchingRaceGroup) {
List<ManagedRace> races = mRacesByGroup.get(raceGroupSeriesFleet);
if (!isRaceListDirty(races)) {
@@ -620,9 +514,9 @@ public class RaceListFragment extends LoggableFragment implements OnItemClickLis
@Override
public void onReceive(Context context, Intent intent) {
if (AppConstants.INTENT_ACTION_SHOW_PROTEST.equals(intent.getAction())) {
String raceGroupName = intent.getExtras().getString(AppConstants.INTENT_ACTION_EXTRA);
if (raceGroupName != null) {
showProtestTimeDialog(raceGroupName);
String raceGroupSeriesDisplayName = intent.getExtras().getString(AppConstants.INTENT_ACTION_EXTRA);
if (raceGroupSeriesDisplayName != null) {
showProtestTimeDialog(raceGroupSeriesDisplayName);
mDrawerLayout.closeDrawers();
} else {
ExLog.e(getActivity(), TAG, "INTENT_ACTION_SHOW_PROTEST does not carry an INTENT_ACTION_EXTRA with the race group name!");
@@ -27,13 +27,13 @@ import com.sap.sailing.android.shared.util.BroadcastManager;
import com.sap.sailing.android.shared.util.ScreenHelper;
import com.sap.sailing.android.shared.util.ViewHelper;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishedTimeFinder;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleet;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.data.DataManager;
import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.impl.RaceGroupSeriesFleet;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
import com.sap.sailing.racecommittee.app.utils.TimeUtils;
import com.sap.sse.common.TimePoint;
@@ -381,7 +381,7 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements
@Override
public String toString() {
return String.format("%s - %s", group.getDisplayName(true), race.getRaceName());
return String.format("%s - %s", group.getDisplayName(true), race.getRaceColumnName());
}
}
}
@@ -33,6 +33,7 @@ import com.sap.sailing.domain.abstractlog.race.impl.SimpleRaceLogIdentifierImpl;
import com.sap.sailing.domain.abstractlog.race.state.RaceStateChangedListener;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.BaseRaceStateChangedListener;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleet;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
@@ -42,7 +43,6 @@ import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.data.DataStore;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.impl.RaceGroupSeriesFleet;
import com.sap.sailing.racecommittee.app.ui.adapters.DependentRaceSpinnerAdapter;
import com.sap.sailing.racecommittee.app.ui.fragments.RaceFragment;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
@@ -428,7 +428,7 @@ public class StartTimeFragment extends BaseFragment
if (races.getFleet().getName().equals(fleet.getA())) {
for (ManagedRace race : mGroupHeaders.get(races)) {
if (!getRace().equals(race) && race.getStatus() != RaceLogRaceStatus.FINISHED) {
Util.Pair<String, String> data = new Util.Pair<>(race.getRaceName(), null);
Util.Pair<String, String> data = new Util.Pair<>(race.getRaceColumnName(), null);
int position = mRaceAdapter.add(data);
if (position >= 0) {
if (mRaceId != null) {
@@ -39,6 +39,7 @@ import com.google.android.gms.location.LocationServices;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.AppUtils;
import com.sap.sailing.android.shared.util.ViewHelper;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleet;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
@@ -50,7 +51,6 @@ import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.impl.RaceGroupSeriesFleet;
import com.sap.sailing.racecommittee.app.ui.activities.RacingActivity;
import com.sap.sailing.racecommittee.app.ui.utils.OnRaceUpdatedListener;
import com.sap.sailing.racecommittee.app.ui.views.AccuracyView;
@@ -15,12 +15,12 @@ import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.gate.GateSt
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleet;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.racecommittee.app.AppPreferences;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.impl.RaceGroupSeriesFleet;
import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.GateStartTimingFragment;
public class RaceHelper {
@@ -36,7 +36,7 @@ public class RaceHelper {
raceName = getRaceGroupName(race);
raceName += getSeriesName(race.getSeries(), delimiter);
raceName += getFleetName(race.getFleet(), delimiter);
raceName += delimiter + race.getRaceName();
raceName += delimiter + race.getRaceColumnName();
}
return raceName;
@@ -51,7 +51,7 @@ public class RaceHelper {
String raceName = "";
if (race != null) {
raceName += race.getRaceName();
raceName += race.getRaceColumnName();
raceName += getFleetName(race.getFleet(), delimiter);
raceName += getSeriesName(race.getSeries(), delimiter);
raceName += delimiter + getRaceGroupName(race);
@@ -69,7 +69,7 @@ public class RaceHelper {
String raceName = "";
if (race != null) {
raceName += race.getRaceName();
raceName += race.getRaceColumnName();
raceName += getFleetName(race.getFleet(), delimiter);
}
@@ -81,7 +81,7 @@ public class RaceHelper {
String raceName = "";
if (race != null) {
int maxElements = 3;
raceName += race.getRaceName();
raceName += race.getRaceColumnName();
String groupName = getRaceGroupName(race);
String seriesName = getSeriesName(race.getSeries(), delimiter);
String fleetName = getFleetName(race.getFleet(), delimiter);
@@ -246,7 +246,7 @@ public class RaceHelper {
public static SimpleRaceLogIdentifier getSimpleRaceLogIdentifier(@Nullable ManagedRace race) {
SimpleRaceLogIdentifier identifier = null;
if (race != null) {
identifier = new SimpleRaceLogIdentifierImpl(race.getRaceGroup().getName(), race.getRaceName(), race.getFleet().getName());
identifier = new SimpleRaceLogIdentifierImpl(race.getRaceGroup().getName(), race.getRaceColumnName(), race.getFleet().getName());
}
return identifier;
}
@@ -85,7 +85,7 @@ public class WindHelper {
String raceName = raceState.getString("raceName");
boolean matchingLeaderboardName = leaderboardName != null && leaderboardName.equals(triple.getA());
boolean matchingFleetName = fleetName != null && fleetName.equals(triple.getC());
boolean matchingRaceName = raceName != null && raceName.equals(race.getRaceName());
boolean matchingRaceName = raceName != null && raceName.equals(race.getRaceColumnName());
if (matchingLeaderboardName && matchingFleetName && matchingRaceName){
if (raceState.getBoolean("trackedRaceLinked")){
isTracked = true;
@@ -7,8 +7,8 @@ import static org.mockito.Mockito.when;
import org.junit.Test;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.domain.base.racegroup.impl.RaceGroupImpl;
import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier;
import com.sap.sse.common.Util.Triple;
@@ -43,7 +43,7 @@ public class SimpleRaceIdentifierEscapingTest {
private String build(final String leaderboardName, final String seriesName, final String fleetName, final String raceColumnName) {
Fleet fleet = mock(Fleet.class);
SeriesBase series = mock(SeriesBase.class);
SeriesWithRows series = mock(SeriesWithRows.class);
RaceGroup raceGroup = mock(RaceGroupImpl.class);
when(fleet.getName()).thenReturn(fleetName);