diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml b/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml index d80170db093..186fc289800 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml @@ -267,6 +267,7 @@ Kurs Zieleinlauf Verschieben + von abhängigen Rennen %2$s / %1$s kn http://maps.googleapis.com/maps/api/geocode/json?address= \?sensor=true diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml b/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml index 82318e4453e..447c41cf5a7 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml @@ -308,6 +308,7 @@ Course Finish Postpone + of dependent races Select a Race Pathfinder diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/RacingActivity.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/RacingActivity.java index b8f2a462cea..4a2c023f8a5 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/RacingActivity.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/RacingActivity.java @@ -21,6 +21,7 @@ import android.os.Build; import android.os.Bundle; import android.support.annotation.IdRes; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.AlertDialog; @@ -40,6 +41,8 @@ import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.shared.util.AppUtils; import com.sap.sailing.android.shared.util.CollectionUtils; import com.sap.sailing.android.shared.util.ViewHelper; +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.common.Wind; @@ -47,6 +50,7 @@ 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.DataStore; import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager; import com.sap.sailing.racecommittee.app.data.clients.LoadClient; import com.sap.sailing.racecommittee.app.domain.ManagedRace; @@ -69,6 +73,7 @@ import com.sap.sailing.racecommittee.app.ui.views.PanelButton; import com.sap.sailing.racecommittee.app.utils.BitmapHelper; import com.sap.sailing.racecommittee.app.utils.RaceHelper; import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; public class RacingActivity extends SessionActivity implements RaceListCallbacks { @@ -86,6 +91,7 @@ public class RacingActivity extends SessionActivity implements RaceListCallbacks private RaceListFragment mRaceList; private ManagedRace mSelectedRace; private TimePoint startTime; + private Collection lastSeenRaces = null; private Serializable getCourseAreaIdFromIntent() { if (getIntent() == null || getIntent().getExtras() == null) { @@ -453,6 +459,31 @@ public class RacingActivity extends SessionActivity implements RaceListCallbacks return null; } + public List getChildRaces(ManagedRace currentRace, @Nullable RaceLogRaceStatus[] state) { + StartTimeFinderResult result; + SimpleRaceLogIdentifier identifier; + ArrayList list = new ArrayList<>(); + DataStore dataStore = dataManager.getDataStore(); + for (ManagedRace race : lastSeenRaces) { + result = race.getState().getStartTimeFinderResult(); + if (result.isDependentStartTime()) { + identifier = Util.get(result.getRacesDependingOn(), 0); + if (dataStore.getRace(identifier).equals(currentRace)) { + if (state == null) { + list.add(race); + } else { + for (RaceLogRaceStatus status : state) { + if (status.equals(race.getStatus())) { + list.add(race); + } + } + } + } + } + } + return list; + } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void setOverflowIcon() { // Required to set overflow icon @@ -472,7 +503,9 @@ public class RacingActivity extends SessionActivity implements RaceListCallbacks TintImageView overflow = (TintImageView) outViews.get(0); overflow.setMinimumWidth(getResources().getDimensionPixelSize(R.dimen.bigger_over_flow_width)); overflow.setMinimumHeight(getResources().getDimensionPixelSize(R.dimen.bigger_over_flow_height)); - Bitmap bitmap = BitmapHelper.decodeSampledBitmapFromResource(getResources(), R.drawable.ic_more_vert_white_48dp, overflow.getMinimumWidth(), overflow.getMinimumHeight()); + Bitmap bitmap = BitmapHelper + .decodeSampledBitmapFromResource(getResources(), R.drawable.ic_more_vert_white_48dp, overflow.getMinimumWidth(), overflow + .getMinimumHeight()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { overflow.setScaleType(ImageView.ScaleType.FIT_END); } @@ -559,7 +592,6 @@ public class RacingActivity extends SessionActivity implements RaceListCallbacks private class RaceLoadClient implements LoadClient> { private CourseArea courseArea; - private Collection lastSeenRaces = null; public RaceLoadClient(CourseArea courseArea) { this.courseArea = courseArea; @@ -570,16 +602,15 @@ public class RacingActivity extends SessionActivity implements RaceListCallbacks setProgressSpinnerVisibility(false); AlertDialog.Builder builder = new AlertDialog.Builder(RacingActivity.this, R.style.AppTheme_AlertDialog); builder.setMessage(String.format(getString(R.string.generic_load_failure), ex.getMessage())).setTitle(getString(R.string.loading_failure)) - .setIcon(R.drawable.ic_warning_grey_600_36dp).setCancelable(true) - .setPositiveButton(getString(R.string.retry), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - setProgressSpinnerVisibility(true); + .setCancelable(true).setPositiveButton(getString(R.string.retry), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + setProgressSpinnerVisibility(true); - ExLog.i(RacingActivity.this, 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() { + ExLog.i(RacingActivity.this, 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(); } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/AbortFlagsFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/AbortFlagsFragment.java index 5aa08c6ddec..50c834c2de9 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/AbortFlagsFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/AbortFlagsFragment.java @@ -1,6 +1,11 @@ package com.sap.sailing.racecommittee.app.ui.fragments.raceinfo; +import java.util.ArrayList; +import java.util.List; + +import android.content.DialogInterface; import android.os.Bundle; +import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -10,14 +15,19 @@ import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.shared.util.ViewHelper; import com.sap.sailing.domain.abstractlog.race.state.RaceState; import com.sap.sailing.domain.common.racelog.Flags; +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.domain.ManagedRace; import com.sap.sailing.racecommittee.app.logging.LogEvent; +import com.sap.sailing.racecommittee.app.ui.activities.RacingActivity; import com.sap.sailing.racecommittee.app.ui.adapters.AbortFlagsAdapter; import com.sap.sailing.racecommittee.app.ui.adapters.AbortFlagsAdapter.AbortFlagItemClick; import com.sap.sailing.racecommittee.app.ui.fragments.RaceFragment; import com.sap.sailing.racecommittee.app.ui.fragments.RaceListFragment; import com.sap.sailing.racecommittee.app.ui.layouts.HeaderLayout; +import com.sap.sailing.racecommittee.app.utils.RaceHelper; +import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint; @@ -69,42 +79,40 @@ public class AbortFlagsFragment extends RaceFragment implements AbortFlagItemCli RaceState state = getRaceState(); Flags mainFlag = Flags.valueOf(getArguments().getString(AppConstants.FLAG_KEY)); switch (mainFlag) { - case AP: - logFlag(flag); - state.setAborted(now, /* postponed */ true, flag); - if (flag != Flags.NONE) { - setProtestTime(); - } - break; + case AP: + logFlag(flag); + state.setAborted(now, /* postponed */ true, flag); + if (flag != Flags.NONE) { + setProtestTime(); + } + state.setAdvancePass(now); + break; - case NOVEMBER: - logFlag(flag); - state.setAborted(now, /* postponed */ false, flag); - if (flag != Flags.NONE) { - setProtestTime(); - } - break; + case NOVEMBER: + logFlag(flag); + abortChildRaces(now, state, flag); + break; - default: - logFlag(flag); - break; + default: + logFlag(flag); + state.setAdvancePass(now); + break; } - state.setAdvancePass(now); } private void logFlag(Flags flag) { switch (flag) { - case ALPHA: - ExLog.i(getActivity(), LogEvent.RACE_CHOOSE_ABORT_ALPHA, getRace().getId()); - break; + case ALPHA: + ExLog.i(getActivity(), LogEvent.RACE_CHOOSE_ABORT_ALPHA, getRace().getId()); + break; - case HOTEL: - ExLog.i(getActivity(), LogEvent.RACE_CHOOSE_ABORT_HOTEL, getRace().getId()); - break; + case HOTEL: + ExLog.i(getActivity(), LogEvent.RACE_CHOOSE_ABORT_HOTEL, getRace().getId()); + break; - default: - ExLog.i(getActivity(), LogEvent.RACE_CHOOSE_ABORT_NONE, getRace().getId()); - break; + default: + ExLog.i(getActivity(), LogEvent.RACE_CHOOSE_ABORT_NONE, getRace().getId()); + break; } } @@ -125,4 +133,80 @@ public class AbortFlagsFragment extends RaceFragment implements AbortFlagItemCli sendIntent(AppConstants.INTENT_ACTION_TIME_SHOW); } + + private void abortChildRaces(final TimePoint now, final RaceState state, final Flags flag) { + RacingActivity activity = (RacingActivity) getActivity(); + final List races = activity + .getChildRaces(getRace(), new RaceLogRaceStatus[] { RaceLogRaceStatus.STARTPHASE, RaceLogRaceStatus.RUNNING }); + + if (races.size() > 0) { + final ArrayList raceList = new ArrayList<>(); + for (ManagedRace race : races) { + raceList.add(RaceHelper.getShortReverseRaceName(race, " / ", getRace())); + } + final ArrayList raceChecked = new ArrayList<>(); + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_AlertDialog); + builder.setTitle(getArguments().getString(HEADER_TEXT, getString(R.string.not_available)) + " " + getString(R.string.flags_dependent_races)); + builder.setMultiChoiceItems(raceList.toArray(new String[raceList.size()]), null, new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int which, boolean isChecked) { + ManagedRace race = races.get(which); + if (raceChecked.contains(race)) { + raceChecked.remove(race); + } + if (isChecked) { + raceChecked.add(race); + } + } + }); + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + for (ManagedRace race : races) { + RaceState raceState = race.getState(); + if (raceChecked.contains(race)) { + Duration startTimeDiff = raceState.getStartTimeFinderResult().getStartTimeDiff(); + raceState.setAborted(now, /* postponed */ false, flag); + raceState.setAdvancePass(now); + raceState.forceNewDependentStartTime(now, startTimeDiff, RaceHelper.getSimpleRaceLogIdentifier(getRace())); + } else { + raceState.forceNewStartTime(now, raceState.getStartTimeFinderResult().getStartTime()); + } + } + abortRace(now, state, flag); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + abortRace(now, state, flag); + } + }); + builder.setCancelable(false); + builder.show(); + } else { + abortRace(now, state, flag); + } + } + + private void abortRace(TimePoint now, RaceState state, Flags flag) { + fixStartTime(now); + state.setAborted(now, /* postponed */ false, flag); + if (flag != Flags.NONE) { + setProtestTime(); + } + state.setAdvancePass(now); + } + + private void fixStartTime(TimePoint now) { + RacingActivity activity = (RacingActivity) getActivity(); + List races = activity + .getChildRaces(getRace(), new RaceLogRaceStatus[] { RaceLogRaceStatus.PRESCHEDULED, RaceLogRaceStatus.SCHEDULED, + RaceLogRaceStatus.FINISHING, RaceLogRaceStatus.FINISHED }); + + for (ManagedRace race : races) { + race.getState().forceNewStartTime(now, race.getState().getStartTimeFinderResult().getStartTime()); + } + } } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/utils/RaceHelper.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/utils/RaceHelper.java index ea868134d18..a2a789b764b 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/utils/RaceHelper.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/utils/RaceHelper.java @@ -1,10 +1,16 @@ package com.sap.sailing.racecommittee.app.utils; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; +import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier; +import com.sap.sailing.domain.abstractlog.race.impl.SimpleRaceLogIdentifierImpl; import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.gate.GateStartRacingProcedure; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.SeriesBase; @@ -17,10 +23,6 @@ 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; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; - public class RaceHelper { public static String getRaceName(@Nullable ManagedRace race) { return getRaceName(race, null); @@ -245,4 +247,12 @@ public class RaceHelper { } return preselectedRaces; } + + public static SimpleRaceLogIdentifier getSimpleRaceLogIdentifier(@Nullable ManagedRace race) { + SimpleRaceLogIdentifier identifier = null; + if (race != null) { + identifier = new SimpleRaceLogIdentifierImpl(race.getRaceGroup().getName(), race.getRaceName(), race.getFleet().getName()); + } + return identifier; + } }