diff --git a/build.gradle b/build.gradle
index a485afff7b7..7440865efce 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,3 +22,23 @@ allprojects {
targetCompatibility = "1.7"
}
}
+
+// dependency versions for all sub modules
+ext {
+ // regular
+ buildTools = "22.0.1"
+ compileSdk = 22
+ minSdk = 14
+ targetSdk = 22
+
+ support = "22.1.1"
+ play_services = "7.3.0"
+
+ // testing - jvm
+ junit = "4.12"
+ mockito = "1.9.5"
+
+ // testing - ui
+ test = "0.2"
+ espresso = "2.1"
+}
\ No newline at end of file
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/racingprocedure/impl/BaseRacingProcedure.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/racingprocedure/impl/BaseRacingProcedure.java
index 8493a7bb1ac..e1bacfe4d6e 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/racingprocedure/impl/BaseRacingProcedure.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/racingprocedure/impl/BaseRacingProcedure.java
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.abstractlog.race.state.racingprocedure.impl;
import java.util.Arrays;
import java.util.Collection;
+import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
@@ -29,6 +30,7 @@ import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProce
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedureChangedListener;
import com.sap.sailing.domain.base.configuration.RacingProcedureConfiguration;
import com.sap.sailing.domain.common.racelog.Flags;
+import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
/**
@@ -40,8 +42,11 @@ import com.sap.sse.common.TimePoint;
*/
public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener implements RacingProcedure,
RaceLogChangedListener {
-
- private final static long individualRecallRemovalTimeout = 4 * 60 * 1000; // minutes * seconds * milliseconds
+ private static final Logger logger = Logger.getLogger(BaseRacingProcedure.class.getName());
+ /**
+ * The time since race start after which the X-ray flag has to go down
+ */
+ private final static Duration individualRecallRemovalTimeout = Duration.ONE_MINUTE.times(4);
private RaceStateEventScheduler scheduler;
@@ -57,9 +62,11 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
private final FinishingTimeFinder finishingTimeFinder;
private final FinishedTimeFinder finishedTimeFinder;
private final RaceLogEventVisitor raceLogListener;
+ private final StartTimeFinder startTimeFinder;
private boolean cachedIsIndividualRecallDisplayed;
+
/**
* When calling me, call {@link BaseRacingProcedure#update()} afterwards!
*
@@ -84,8 +91,7 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
this.recallRemovedFinder = new IndividualRecallRemovedFinder(raceLog);
this.finishingTimeFinder = new FinishingTimeFinder(raceLog);
this.finishedTimeFinder = new FinishedTimeFinder(raceLog);
- // Use the following in order to initialize a start time finder once it's needed for start time-dependent individual recall timeouts
- // this.startTimeFinder = new StartTimeFinder(raceLogResolver, raceLog);
+ this.startTimeFinder = new StartTimeFinder(raceLogResolver, raceLog);
this.raceLogListener = new RaceLogChangedVisitor(this);
this.raceLog.addListener(raceLogListener);
@@ -133,7 +139,7 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
break;
case RUNNING:
if (isIndividualRecallDisplayed()) {
- rescheduleIndividualRecallTimeout(recallDisplayedFinder.analyze());
+ rescheduleIndividualRecallTimeout(getIndividualRecallRemovalTime());
}
break;
default:
@@ -172,7 +178,16 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
}
}
if (displayed != null) {
- return displayed.plus(individualRecallRemovalTimeout);
+ final TimePoint raceStartTime = startTimeFinder.analyze().getStartTime();
+ final TimePoint referenceTimePoint;
+ if (raceStartTime != null) {
+ referenceTimePoint = raceStartTime;
+ } else {
+ logger.warning("An individual recall was set but we don't have a start time set for the race; using individual recall set time "+
+ displayed+" + timeout ("+individualRecallRemovalTimeout+") for implicit termination of individual recall");
+ referenceTimePoint = displayed;
+ }
+ return referenceTimePoint.plus(individualRecallRemovalTimeout);
}
return null;
}
@@ -218,7 +233,7 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
cachedIsIndividualRecallDisplayed = isRecallDisplayed;
if (cachedIsIndividualRecallDisplayed) {
changedListeners.onIndividualRecallDisplayed(this);
- rescheduleIndividualRecallTimeout(recallDisplayedFinder.analyze());
+ rescheduleIndividualRecallTimeout(getIndividualRecallRemovalTime());
} else {
changedListeners.onIndividualRecallRemoved(this);
unscheduleStateEvent(RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT);
@@ -231,9 +246,9 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
changedListeners.onActiveFlagsChanged(this);
}
- private void rescheduleIndividualRecallTimeout(TimePoint displayTime) {
+ private void rescheduleIndividualRecallTimeout(TimePoint removalTime) {
unscheduleStateEvent(RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT);
- scheduleStateEvents(new RaceStateEventImpl(displayTime.plus(individualRecallRemovalTimeout), RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT));
+ scheduleStateEvents(new RaceStateEventImpl(removalTime, RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT));
}
protected void scheduleStateEvents(RaceStateEvent stateEvent) {
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
index bb5ccfd6455..786c4db1a11 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
@@ -1024,7 +1024,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
// competitor has finished race at or before the requested time point; use time point of crossing the finish line
end = markPassings.last().getTimePoint();
} else if ((trackedLegOfCompetitor=getTrackedLeg(competitor, timePoint)) == null ||
- (!trackedLegOfCompetitor.hasFinishedLeg(timePoint)
+ (!trackedLegOfCompetitor.hasFinishedLeg(getEndOfTracking())
&& ((getEndOfTracking() != null && timePoint.after(getEndOfTracking()))
|| getStatus().getStatus() == TrackedRaceStatusEnum.FINISHED))) {
// If the race is no longer tracking and hence no more data can be expected, and the competitor
diff --git a/mobile/com.sap.sailing.android.shared/build.gradle b/mobile/com.sap.sailing.android.shared/build.gradle
index e66fa9ea8df..46512fca61b 100644
--- a/mobile/com.sap.sailing.android.shared/build.gradle
+++ b/mobile/com.sap.sailing.android.shared/build.gradle
@@ -1,12 +1,12 @@
apply plugin: 'com.android.library'
android {
- buildToolsVersion "22.0.1"
- compileSdkVersion 22
+ buildToolsVersion rootProject.ext.buildTools
+ compileSdkVersion rootProject.ext.compileSdk
defaultConfig {
- minSdkVersion 14
- targetSdkVersion 22
+ minSdkVersion rootProject.ext.minSdk
+ targetSdkVersion rootProject.ext.targetSdk
}
sourceSets {
@@ -39,7 +39,7 @@ android {
dependencies {
/* support libraries */
- compile 'com.android.support:appcompat-v7:22.1.1'
+ compile "com.android.support:appcompat-v7:${rootProject.ext.support}"
/* local dependencies */
compile project(':java:com.sap.sailing.domain.common')
@@ -49,7 +49,7 @@ dependencies {
compile project(':java:org.json.simple')
/* Google Play Services */
- compile "com.google.android.gms:play-services-location:7.3.0"
+ compile "com.google.android.gms:play-services-location:${rootProject.ext.play_services}"
/* all local jar-libs */
compile fileTree(dir: "libs", include: ["*.jar"])
diff --git a/mobile/com.sap.sailing.android.tracking.app/build.gradle b/mobile/com.sap.sailing.android.tracking.app/build.gradle
index f788fae789f..ddd01ce0d99 100644
--- a/mobile/com.sap.sailing.android.tracking.app/build.gradle
+++ b/mobile/com.sap.sailing.android.tracking.app/build.gradle
@@ -5,13 +5,13 @@ def packageName = "com.sap.sailing.android.tracking.app"
def verCode = 1
android {
- buildToolsVersion "22.0.1"
- compileSdkVersion 22
+ buildToolsVersion rootProject.ext.buildTools
+ compileSdkVersion rootProject.ext.compileSdk
defaultConfig {
applicationId packageName
- minSdkVersion 14
- targetSdkVersion 22
+ minSdkVersion rootProject.ext.minSdk
+ targetSdkVersion rootProject.ext.targetSdk
versionCode verCode
versionName "$verCode - Altitude"
useJack false // not working on Windows yet
@@ -69,25 +69,25 @@ android {
dependencies {
/* support libraries */
- compile "com.android.support:appcompat-v7:22.1.1"
+ compile "com.android.support:appcompat-v7:${rootProject.ext.support}"
/* Google Play Services */
- compile "com.google.android.gms:play-services-gcm:7.3.0"
- compile "com.google.android.gms:play-services-location:7.3.0"
+ compile "com.google.android.gms:play-services-gcm:${rootProject.ext.play_services}"
+ compile "com.google.android.gms:play-services-location:${rootProject.ext.play_services}"
/* local dependencies */
compile project(":mobile:com.sap.sailing.android.shared")
/* JVM tests */
- testCompile 'junit:junit:4.12'
- testCompile "org.mockito:mockito-core:1.9.5"
+ testCompile "junit:junit:${rootProject.ext.junit}"
+ testCompile "org.mockito:mockito-core:${rootProject.ext.mockito}"
/* UI testing with espresso */
- androidTestCompile "com.android.support:support-annotations:22.1.1"
- androidTestCompile "com.android.support.test:runner:0.2"
- androidTestCompile "com.android.support.test:rules:0.2"
- androidTestCompile "com.android.support.test.espresso:espresso-core:2.1"
- androidTestCompile ("com.android.support.test.espresso:espresso-contrib:2.1") {
+ androidTestCompile "com.android.support:support-annotations:${rootProject.ext.support}"
+ androidTestCompile "com.android.support.test:runner:${rootProject.ext.test}"
+ androidTestCompile "com.android.support.test:rules:${rootProject.ext.test}"
+ androidTestCompile "com.android.support.test.espresso:espresso-core:${rootProject.ext.espresso}"
+ androidTestCompile ("com.android.support.test.espresso:espresso-contrib:${rootProject.ext.espresso}") {
exclude group: "com.android.support", module: "appcompat"
exclude group: "com.android.support", module: "support-v4"
exclude module: "recyclerview-v7"
diff --git a/mobile/com.sap.sailing.buoy.positioning/build.gradle b/mobile/com.sap.sailing.buoy.positioning/build.gradle
index 2cc20da075f..79c2d2ffad4 100644
--- a/mobile/com.sap.sailing.buoy.positioning/build.gradle
+++ b/mobile/com.sap.sailing.buoy.positioning/build.gradle
@@ -5,13 +5,13 @@ def packageName = "com.sap.sailing.buoy.positioning.app"
def verCode = 1
android {
- buildToolsVersion "22.0.1"
- compileSdkVersion 22
+ buildToolsVersion rootProject.ext.buildTools
+ compileSdkVersion rootProject.ext.compileSdk
defaultConfig {
applicationId packageName
- minSdkVersion 14
- targetSdkVersion 22
+ minSdkVersion rootProject.ext.minSdk
+ targetSdkVersion rootProject.ext.targetSdk
versionCode verCode
versionName "$verCode - Altitude"
useJack false // not working on Windows yet
@@ -69,24 +69,24 @@ android {
dependencies {
/* support libraries */
- compile "com.android.support:appcompat-v7:22.1.1"
+ compile "com.android.support:appcompat-v7:${rootProject.ext.support}"
/* Google Play Services */
- compile "com.google.android.gms:play-services-location:7.3.0"
+ compile "com.google.android.gms:play-services-location:${rootProject.ext.play_services}"
/* local dependencies */
compile project(":mobile:com.sap.sailing.android.shared")
/* JVM tests */
- testCompile 'junit:junit:4.12'
- testCompile "org.mockito:mockito-core:1.9.5"
+ testCompile "junit:junit:${rootProject.ext.junit}"
+ testCompile "org.mockito:mockito-core:${rootProject.ext.mockito}"
/* UI testing with espresso */
- androidTestCompile "com.android.support:support-annotations:22.1.1"
- androidTestCompile "com.android.support.test:runner:0.2"
- androidTestCompile "com.android.support.test:rules:0.2"
- androidTestCompile "com.android.support.test.espresso:espresso-core:2.1"
- androidTestCompile ("com.android.support.test.espresso:espresso-contrib:2.1") {
+ androidTestCompile "com.android.support:support-annotations:${rootProject.ext.support}"
+ androidTestCompile "com.android.support.test:runner:${rootProject.ext.test}"
+ androidTestCompile "com.android.support.test:rules:${rootProject.ext.test}"
+ androidTestCompile "com.android.support.test.espresso:espresso-core:${rootProject.ext.espresso}"
+ androidTestCompile ("com.android.support.test.espresso:espresso-contrib:${rootProject.ext.espresso}") {
exclude group: "com.android.support", module: "appcompat"
exclude group: "com.android.support", module: "support-v4"
exclude module: "recyclerview-v7"
diff --git a/mobile/com.sap.sailing.racecommittee.app/build.gradle b/mobile/com.sap.sailing.racecommittee.app/build.gradle
index 84e5765a0bf..ceeea7934e1 100644
--- a/mobile/com.sap.sailing.racecommittee.app/build.gradle
+++ b/mobile/com.sap.sailing.racecommittee.app/build.gradle
@@ -5,13 +5,13 @@ def packageName = "com.sap.sailing.racecommittee.app"
def verCode = 7
android {
- buildToolsVersion "22.0.1"
- compileSdkVersion 22
+ buildToolsVersion rootProject.ext.buildTools
+ compileSdkVersion rootProject.ext.compileSdk
defaultConfig {
applicationId packageName
- minSdkVersion 14
- targetSdkVersion 22
+ minSdkVersion rootProject.ext.minSdk
+ targetSdkVersion rootProject.ext.targetSdk
versionCode verCode
versionName "$verCode - Laser"
useJack false // not working on Windows yet
@@ -75,11 +75,11 @@ dependencies {
compile "com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.6.2"
/* support libraries */
- compile "com.android.support:appcompat-v7:22.1.1"
- compile "com.android.support:recyclerview-v7:22.1.1"
+ compile "com.android.support:appcompat-v7:${rootProject.ext.support}"
+ compile "com.android.support:recyclerview-v7:${rootProject.ext.support}"
/* Google Play Services */
- compile "com.google.android.gms:play-services-location:7.3.0"
+ compile "com.google.android.gms:play-services-location:${rootProject.ext.play_services}"
/* local dependencies */
compile project(":mobile:com.sap.sailing.android.shared")
@@ -88,15 +88,15 @@ dependencies {
compile fileTree(dir: "libs", include: ["*.jar"])
/* JVM tests */
- testCompile 'junit:junit:4.12'
- testCompile "org.mockito:mockito-core:1.9.5"
+ testCompile "junit:junit:${rootProject.ext.junit}"
+ testCompile "org.mockito:mockito-core:${rootProject.ext.mockito}"
/* UI testing with espresso */
- androidTestCompile "com.android.support:support-annotations:22.1.1"
- androidTestCompile "com.android.support.test:runner:0.2"
- androidTestCompile "com.android.support.test:rules:0.2"
- androidTestCompile "com.android.support.test.espresso:espresso-core:2.1"
- androidTestCompile ("com.android.support.test.espresso:espresso-contrib:2.1") {
+ androidTestCompile "com.android.support:support-annotations:${rootProject.ext.support}"
+ androidTestCompile "com.android.support.test:runner:${rootProject.ext.test}"
+ androidTestCompile "com.android.support.test:rules:${rootProject.ext.test}"
+ androidTestCompile "com.android.support.test.espresso:espresso-core:${rootProject.ext.espresso}"
+ androidTestCompile ("com.android.support.test.espresso:espresso-contrib:${rootProject.ext.espresso}") {
exclude group: "com.android.support", module: "appcompat"
exclude group: "com.android.support", module: "support-v4"
exclude module: "recyclerview-v7"
diff --git a/mobile/com.sap.sailing.racecommittee.app/res/layout/race_time_picker.xml b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_time_picker.xml
index 438a48240cd..b2594e2d1bb 100644
--- a/mobile/com.sap.sailing.racecommittee.app/res/layout/race_time_picker.xml
+++ b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_time_picker.xml
@@ -40,13 +40,12 @@
Angegebene Zeit verwenden
Keine Daten
-
+
Entwickler Optionen
Offline Modus
Deaktiviert die Verbindung zum Server und benutzt statische Daten
@@ -405,7 +405,7 @@
Die Änderung am Theme wird nach einem Neustart der App wirksam.
Die Server URL wurde geändert
Bitte melden Sie sich vom aktuellen Event ab, um die Daten des neuen Servers zu laden.
- XRAY wird eingeholt in #TIME#
+ XRAY wird eingeholt in %s
Fotos senden
Fehler
Bitte geben Sie einen Wert zwischen 0 und 360 an.
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 27af1f79ac4..98b5172b823 100644
--- a/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml
+++ b/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml
@@ -323,7 +323,7 @@
kn
Enter Address
-
+
No Data
Up in #TIME#
@@ -426,7 +426,7 @@
Changes to the theme will take action after an app restart.
Server URL Changed
Please logout from the current event to load the data from the new server.
- XRAY will be removed in #TIME#
+ XRAY will be removed in %s
Send Photos
Error
Please provide a value between 0 and 360
diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java
index a703dd2e1a6..62417f003ae 100755
--- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java
+++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java
@@ -1,5 +1,6 @@
package com.sap.sailing.racecommittee.app.services;
+import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -8,6 +9,7 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Pair;
@@ -259,11 +261,16 @@ public class RaceStateService extends Service {
intent.putExtra(AppConstants.EXTRAS_RACE_STATE_EVENT, event);
return PendingIntent.getService(this, alarmManagerRequestCode++, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
-
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
public void setAlarm(ManagedRace race, RaceStateEvent event) {
PendingIntent intent = createAlarmPendingIntent(race, event);
managedIntents.get(race.getId()).add(Pair.create(intent, event.getEventName()));
- alarmManager.set(AlarmManager.RTC_WAKEUP, event.getTimePoint().asMillis(), intent);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ alarmManager.setExact(AlarmManager.RTC_WAKEUP, event.getTimePoint().asMillis(), intent);
+ } else {
+ alarmManager.set(AlarmManager.RTC_WAKEUP, event.getTimePoint().asMillis(), intent);
+ }
ExLog.i(this, TAG, "The alarm " + event.getEventName() + " will be fired at " + event.getTimePoint());
}
diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/racelist/ManagedRaceListAdapter.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/racelist/ManagedRaceListAdapter.java
index 8822960b0da..da14da34b7b 100644
--- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/racelist/ManagedRaceListAdapter.java
+++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/racelist/ManagedRaceListAdapter.java
@@ -1,15 +1,10 @@
package com.sap.sailing.racecommittee.app.ui.adapters.racelist;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
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.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.view.LayoutInflater;
@@ -19,10 +14,11 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-
+import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.ViewHolder;
import com.sap.sailing.domain.abstractlog.race.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.common.racelog.FlagPole;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.racecommittee.app.AppConstants;
@@ -32,11 +28,17 @@ import com.sap.sailing.racecommittee.app.ui.adapters.racelist.RaceFilter.FilterS
import com.sap.sailing.racecommittee.app.ui.utils.FlagsResources;
import com.sap.sailing.racecommittee.app.utils.BitmapHelper;
import com.sap.sailing.racecommittee.app.utils.ThemeHelper;
+import com.sap.sailing.racecommittee.app.utils.TimeUtils;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
+import java.text.SimpleDateFormat;
+import java.util.List;
+
public class ManagedRaceListAdapter extends ArrayAdapter implements FilterSubscriber {
+ private final static String TAG = ManagedRaceListAdapter.class.getName();
+ private final static int FLAG_SIZE = 48;
private final Object mLockObject = new Object();
private List mAllViewItems;
private RaceFilter mFilter;
@@ -71,15 +73,6 @@ public class ManagedRaceListAdapter extends ArrayAdapter imple
dateFormat = new SimpleDateFormat("HH:mm", getContext().getResources().getConfiguration().locale);
}
- public String fill2(int value) {
- String erg = String.valueOf(value);
-
- if (erg.length() < 2) {
- erg = "0" + erg;
- }
- return erg;
- }
-
@Override
public int getCount() {
synchronized (mLockObject) {
@@ -87,46 +80,6 @@ public class ManagedRaceListAdapter extends ArrayAdapter imple
}
}
- public String getDuration(Date date1, Date date2) {
- TimeUnit timeUnit = TimeUnit.SECONDS;
-
- long diffInMilli = date2.getTime() - date1.getTime();
- long s = timeUnit.convert(diffInMilli, TimeUnit.MILLISECONDS);
-
- long days = s / (24 * 60 * 60);
- long rest = s - (days * 24 * 60 * 60);
- long hrs = rest / (60 * 60);
- long rest1 = rest - (hrs * 60 * 60);
- long min = rest1 / 60;
- long sec = s % 60;
-
- String dates = "";
- if (days < 0 || hrs < 0 || min < 0 || sec < 0) {
- dates += "-";
- if (days < 0) {
- days *= -1;
- }
- if (hrs < 0) {
- hrs *= -1;
- }
- if (min < 0) {
- min *= -1;
- }
- if (sec < 0) {
- sec *= -1;
- }
- }
- if (days != 0) {
- dates = days + ":";
- }
-
- dates += (days != 0 || hrs != 0) ? fill2((int) hrs) + ":" : "";
- dates += fill2((int) min) + ":";
- dates += fill2((int) sec);
-
- return dates;
- }
-
@Override
public RaceFilter getFilter() {
if (mFilter == null) {
@@ -157,6 +110,7 @@ public class ManagedRaceListAdapter extends ArrayAdapter imple
raceListElement = getItem(position);
int type = getItemViewType(position);
+ TimePoint now = MillisecondsTimePoint.now();
if (convertView == null) {
if (type == ViewType.HEADER.index) {
@@ -186,7 +140,7 @@ public class ManagedRaceListAdapter extends ArrayAdapter imple
fleetSeries += header.getSeries().getName();
}
fleet_series.setText(fleetSeries);
- protest_image.setImageDrawable(FlagsResources.getFlagDrawable(getContext(), Flags.BRAVO.name(), 48));
+ protest_image.setImageDrawable(FlagsResources.getFlagDrawable(getContext(), Flags.BRAVO.name(), FLAG_SIZE));
protest_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -220,7 +174,7 @@ public class ManagedRaceListAdapter extends ArrayAdapter imple
String startTime = mResources.getString(startRes, dateFormat.format(state.getStartTime().asDate()));
race_started.setText(startTime);
if (state.getFinishedTime() == null) {
- String duration = getDuration(state.getStartTime().asDate(), Calendar.getInstance().getTime());
+ String duration = TimeUtils.formatDuration(now, state.getStartTime());
time.setText(duration);
float textSize = getContext().getResources().getDimension(R.dimen.textSize_40);
if (!TextUtils.isEmpty(duration) && duration.length() >= 6) {
@@ -248,7 +202,7 @@ public class ManagedRaceListAdapter extends ArrayAdapter imple
update_badge.setVisibility(View.VISIBLE);
}
- updateFlag(race.getRace());
+ updateFlag(race.getRace(), now);
}
return convertView;
}
@@ -323,32 +277,86 @@ public class ManagedRaceListAdapter extends ArrayAdapter imple
}
}
- private void updateFlag(ManagedRace race) {
+ private void updateFlag(ManagedRace race, TimePoint now) {
RaceState state = race.getState();
if (state == null || state.getStartTime() == null) {
return;
}
- FlagPoleState flagPoleState = state.getTypedRacingProcedure()
- .getActiveFlags(state.getStartTime(), MillisecondsTimePoint.now());
- List flagChanges = flagPoleState.computeUpcomingChanges();
- if (!flagChanges.isEmpty()) {
- TimePoint changeAt = flagPoleState.getNextStateValidFrom();
- FlagPole changePole = FlagPoleState.getMostInterestingFlagPole(flagChanges);
- if (changeAt != null) {
- current_flag.setImageDrawable(
- FlagsResources.getFlagDrawable(getContext(), changePole.getUpperFlag().name(), 48));
- String text = getDuration(changeAt.asDate(), Calendar.getInstance().getTime());
- flag_timer.setText(text.replace("-", ""));
- Drawable arrow;
- if (changePole.isDisplayed()) {
- arrow = BitmapHelper.getAttrDrawable(getContext(), R.attr.arrow_up);
- } else {
- arrow = BitmapHelper.getAttrDrawable(getContext(), R.attr.arrow_down);
+ RacingProcedure procedure = state.getTypedRacingProcedure();
+ LayerDrawable flag = null;
+ Drawable arrow = null;
+ String timer = null;
+ if (!procedure.isIndividualRecallDisplayed()) {
+ FlagPoleState poleState = state.getRacingProcedure().getActiveFlags(state.getStartTime(), now);
+ List currentState = poleState.getCurrentState();
+ List upcoming = poleState.computeUpcomingChanges();
+ FlagPole nextPole = FlagPoleState.getMostInterestingFlagPole(upcoming);
+ TimePoint change = poleState.getNextStateValidFrom();
+ Flags currentFlag;
+
+ if (change != null) {
+ for (FlagPole pole : currentState) {
+ int isNext = 0;
+
+ currentFlag = pole.getUpperFlag();
+ if (isNextFlag(currentFlag, nextPole)) {
+ isNext = 1;
+ } else {
+ currentFlag = pole.getLowerFlag();
+ if (!Flags.NONE.equals(currentFlag)) {
+ if (isNextFlag(currentFlag, nextPole)) {
+ isNext = 2;
+ }
+ }
+ }
+
+ if (isNext != 0) {
+ flag = FlagsResources.getFlagDrawable(getContext(), currentFlag.name(), FLAG_SIZE);
+ switch (isNext) {
+ case 1:
+ if (nextPole.isDisplayed()) {
+ arrow = BitmapHelper.getAttrDrawable(getContext(), R.attr.arrow_up);
+ } else {
+ arrow = BitmapHelper.getAttrDrawable(getContext(), R.attr.arrow_down);
+ }
+ break;
+
+ case 2:
+ arrow = BitmapHelper.getAttrDrawable(getContext(), R.attr.arrow_up);
+ break;
+
+ default:
+ ExLog.i(getContext(), TAG, "unknown flag");
+ }
+ timer = TimeUtils.formatDuration(now, poleState.getNextStateValidFrom());
+ }
}
- arrow_direction.setImageDrawable(arrow);
- race_flag.setVisibility(View.VISIBLE);
}
+ } else {
+ TimePoint flagDown = procedure.getIndividualRecallRemovalTime();
+ if (now.before(flagDown)) {
+ flag = FlagsResources.getFlagDrawable(getContext(), Flags.XRAY.name(), FLAG_SIZE);
+ arrow = BitmapHelper.getAttrDrawable(getContext(), R.attr.arrow_down);
+ timer = TimeUtils.formatDuration(now, flagDown);
+ }
+ }
+ if (timer != null) {
+ timer = timer.replace("-", "");
+ }
+ showFlag(flag, arrow, timer);
+ }
+
+ private boolean isNextFlag(Flags flag, FlagPole pole) {
+ return pole != null && flag.equals(pole.getUpperFlag());
+ }
+
+ private void showFlag(LayerDrawable flag, Drawable arrow, String timer) {
+ if (flag != null && arrow != null && timer != null) {
+ current_flag.setImageDrawable(flag);
+ arrow_direction.setImageDrawable(arrow);
+ flag_timer.setText(timer);
+ race_flag.setVisibility(View.VISIBLE);
}
}
diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/panels/SetupPanelFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/panels/SetupPanelFragment.java
index 07e4be45d06..82cc95b96b7 100644
--- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/panels/SetupPanelFragment.java
+++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/panels/SetupPanelFragment.java
@@ -154,9 +154,7 @@ public class SetupPanelFragment extends BasePanelFragment {
Wind wind = getRaceState().getWindFix();
if (mWindValue != null && wind != null) {
- String sensorData = getString(R.string.wind_panel);
- sensorData = sensorData.replace("#FROM#", String.format("%.0f", wind.getFrom().getDegrees()));
- sensorData = sensorData.replace("#SPEED#", String.format("%.1f", wind.getKnots()));
+ String sensorData = getString(R.string.wind_panel, wind.getFrom().getDegrees(), wind.getKnots());
mWindValue.setText(sensorData);
}
}
diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceFlagViewerFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceFlagViewerFragment.java
index 7328fe944c7..bc673b5e18a 100755
--- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceFlagViewerFragment.java
+++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceFlagViewerFragment.java
@@ -9,6 +9,8 @@ import android.widget.*;
import com.sap.sailing.android.shared.util.ViewHolder;
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.abstractlog.race.state.racingprocedure.ReadonlyRacingProcedure;
+import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.impl.BaseRacingProcedureChangedListener;
import com.sap.sailing.domain.common.racelog.FlagPole;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.racecommittee.app.R;
@@ -32,8 +34,12 @@ public class RaceFlagViewerFragment extends BaseFragment {
private TextView mXrayCountdown;
private Button mXrayButton;
- public RaceFlagViewerFragment() {
+ private ProcedureChangedListener mProcedureListener;
+ private FlagsCache mFlagCache;
+ public RaceFlagViewerFragment() {
+ mProcedureListener = new ProcedureChangedListener();
+ mFlagCache = null;
}
public static RaceFlagViewerFragment newInstance() {
@@ -69,6 +75,20 @@ public class RaceFlagViewerFragment extends BaseFragment {
return layout;
}
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ getRaceState().getRacingProcedure().addChangedListener(mProcedureListener);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ getRaceState().getRacingProcedure().removeChangedListener(mProcedureListener);
+ }
+
@Override
public void notifyTick(TimePoint now) {
super.notifyTick(now);
@@ -76,7 +96,7 @@ public class RaceFlagViewerFragment extends BaseFragment {
updateFlags(now);
}
- private void updateFlags(TimePoint timePoint) {
+ private void updateFlags(TimePoint now) {
if (getRace() == null || getRaceState() == null || getRaceState().getStartTime() == null) {
return;
}
@@ -87,42 +107,57 @@ public class RaceFlagViewerFragment extends BaseFragment {
if (mRecall != null) {
mRecall.setVisibility(View.GONE);
}
+
RacingProcedure procedure = getRaceState().getTypedRacingProcedure();
if (!procedure.isIndividualRecallDisplayed()) {
if (mLayout != null) {
mLayout.setVisibility(View.VISIBLE);
- }
- FlagPoleState poleState = getRaceState().getRacingProcedure().getActiveFlags(getRaceState().getStartTime(), timePoint);
- List currentState = poleState.getCurrentState();
- List upcoming = poleState.computeUpcomingChanges();
- FlagPole nextPole = FlagPoleState.getMostInterestingFlagPole(upcoming);
- mLayout.removeAllViews();
- int size = 0;
- Flags flag;
- for (FlagPole flagPole : currentState) {
- size++;
- flag = flagPole.getUpperFlag();
- mLayout.addView(renderFlag(timePoint, poleState, flag, isNextFlag(flag, nextPole), currentState.size() == size, flagPole.isDisplayed(), UPPER_FLAG));
- if (!flagPole.getLowerFlag().equals(Flags.NONE)) {
- flag = flagPole.getLowerFlag();
- mLayout.addView(renderFlag(timePoint, poleState, flag, isNextFlag(flag, nextPole), currentState.size() == size, false, LOWER_FLAG));
+ if (mFlagCache == null) {
+ mLayout.removeAllViews();
+ FlagPoleState poleState = getRaceState().getRacingProcedure().getActiveFlags(getRaceState().getStartTime(), now);
+ List currentState = poleState.getCurrentState();
+ List upcoming = poleState.computeUpcomingChanges();
+ FlagPole nextPole = FlagPoleState.getMostInterestingFlagPole(upcoming);
+ mFlagCache = new FlagsCache(poleState.getNextStateValidFrom());
+ int size = 0;
+ Flags flag;
+ boolean nextFlag;
+ for (FlagPole flagPole : currentState) {
+ size++;
+ flag = flagPole.getUpperFlag();
+ nextFlag = isNextFlag(flag, nextPole);
+ if (nextFlag) {
+ mFlagCache.nextFlag = size;
+ }
+ mLayout.addView(createFlagView(now, poleState, flag, nextFlag, currentState.size() == size, flagPole.isDisplayed(), UPPER_FLAG));
+ if (!flagPole.getLowerFlag().equals(Flags.NONE)) {
+ flag = flagPole.getLowerFlag();
+ nextFlag = isNextFlag(flag, nextPole);
+ if (nextFlag) {
+ mFlagCache.nextFlag = size;
+ }
+ mLayout.addView(createFlagView(now, poleState, flag, nextFlag, currentState.size() == size, false, LOWER_FLAG));
+ }
+ }
+ } else {
+ if (mLayout != null) {
+ int nextFlag = 0;
+ for (int i = 0; i < mLayout.getChildCount(); i++) {
+ if (mFlagCache.nextFlag == ++nextFlag) {
+ updateFlagView(now, mLayout.getChildAt(i));
+ }
+ }
+ }
}
}
} else {
- String time = getString(R.string.until_xray_removed);
- if (mXrayCountdown != null) {
+ if (mXrayCountdown != null && mRecall != null) {
TimePoint flagDown = procedure.getIndividualRecallRemovalTime();
- if (timePoint.before(flagDown)) {
- time = time.replace("#TIME#", TimeUtils.formatDurationUntil(flagDown.minus(timePoint.asMillis()).asMillis()).replace("-", ""));
- mXrayCountdown.setText(time);
- } else {
- procedure.removeIndividualRecall(timePoint);
+ if (now.before(flagDown)) {
+ mRecall.setVisibility(View.VISIBLE);
+ mXrayCountdown.setText(getString(R.string.until_xray_removed, TimeUtils.formatDuration(now, flagDown, false)));
}
}
-
- if (mRecall != null) {
- mRecall.setVisibility(View.VISIBLE);
- }
}
}
@@ -130,17 +165,28 @@ public class RaceFlagViewerFragment extends BaseFragment {
return pole != null && flag.equals(pole.getUpperFlag());
}
- private RelativeLayout renderFlag(TimePoint timePoint, FlagPoleState poleState, final Flags flag, boolean isNext, boolean lastEntry, boolean isDisplayed, int flagType) {
+ private View updateFlagView(TimePoint now, View flagView) {
+ TimePoint change = mFlagCache.mChange;
+ if (change != null) {
+ TextView flag_text = ViewHolder.get(flagView, R.id.flag_text);
+ String timer = TimeUtils.formatDuration(now, change, false);
+ flag_text.setText(timer);
+ }
+ return flagView;
+ }
+
+ private RelativeLayout createFlagView(TimePoint now, FlagPoleState poleState, final Flags flag, boolean isNext, boolean lastEntry,
+ boolean isDisplayed, int flagType) {
RelativeLayout layout = (RelativeLayout) getActivity().getLayoutInflater().inflate(R.layout.race_flag, mLayout, false);
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT, 1.0f);
+ LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, 1.0f);
layout.setLayoutParams(layoutParams);
TimePoint changeAt = poleState.getNextStateValidFrom();
- ImageView flagView = (ImageView) layout.findViewById(R.id.flag);
- TextView textView = (TextView) layout.findViewById(R.id.flag_text);
- View downView = layout.findViewById(R.id.arrow_down);
- View upView = layout.findViewById(R.id.arrow_up);
- View line = layout.findViewById(R.id.line);
+ ImageView flagView = ViewHolder.get(layout, R.id.flag);
+ TextView textView = ViewHolder.get(layout, R.id.flag_text);
+ View downView = ViewHolder.get(layout, R.id.arrow_down);
+ View upView = ViewHolder.get(layout, R.id.arrow_up);
+ View line = ViewHolder.get(layout, R.id.line);
if (lastEntry) {
line.setVisibility(View.GONE);
}
@@ -159,12 +205,7 @@ public class RaceFlagViewerFragment extends BaseFragment {
textView.setText("");
if (changeAt != null && isNext) {
textView.setVisibility(View.VISIBLE);
- String timer;
- if (changeAt.after(timePoint)) {
- timer = TimeUtils.formatDurationUntil(changeAt.minus(timePoint.asMillis()).asMillis());
- } else {
- timer = TimeUtils.formatDurationSince(timePoint.minus(changeAt.asMillis()).asMillis());
- }
+ String timer = TimeUtils.formatDuration(now, changeAt);
textView.setText(timer.replace("-", ""));
if (flagType == UPPER_FLAG) {
if (isDisplayed) {
@@ -192,7 +233,27 @@ public class RaceFlagViewerFragment extends BaseFragment {
}
private int getFleetColorId() {
- Util.Triple rgb = getRace().getFleet().getColor() == null ? new Util.Triple<>(0, 0, 0) : getRace().getFleet().getColor().getAsRGB();
+ Util.Triple rgb =
+ getRace().getFleet().getColor() == null ? new Util.Triple<>(0, 0, 0) : getRace().getFleet().getColor().getAsRGB();
return Color.rgb(rgb.getA(), rgb.getB(), rgb.getC());
}
+
+ private class ProcedureChangedListener extends BaseRacingProcedureChangedListener {
+
+ @Override
+ public void onActiveFlagsChanged(ReadonlyRacingProcedure racingProcedure) {
+ super.onActiveFlagsChanged(racingProcedure);
+
+ mFlagCache = null;
+ }
+ }
+
+ private class FlagsCache {
+ private TimePoint mChange;
+ private int nextFlag = 0;
+
+ public FlagsCache(TimePoint change) {
+ mChange = change;
+ }
+ }
}
diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceTimeChangeFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceTimeChangeFragment.java
index d5047b0ce46..b26983987a6 100644
--- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceTimeChangeFragment.java
+++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceTimeChangeFragment.java
@@ -26,13 +26,16 @@ public class RaceTimeChangeFragment extends BaseFragment implements View.OnClick
public final static int FINISHING_TIME_MODE = 1;
public final static int FINISHED_TIME_MODE = 2;
private final static String START_MODE = "startMode";
- private static final int FUTURE_DAYS = 3;
- private static final int PAST_DAYS = -3;
+ private static final int FUTURE_DAYS_DEFAULT = 3;
+ private static final int PAST_DAYS_DEFAULT = -3;
private NumberPicker mDatePicker;
private TimePicker mTimePicker;
private NumberPicker mSecondPicker;
+ private int mFutureDays = FUTURE_DAYS_DEFAULT;
+ private int mPastDays = PAST_DAYS_DEFAULT;
+
public static RaceTimeChangeFragment newInstance(int mode) {
RaceTimeChangeFragment fragment = new RaceTimeChangeFragment();
Bundle args = new Bundle();
@@ -81,10 +84,37 @@ public class RaceTimeChangeFragment extends BaseFragment implements View.OnClick
mDatePicker = (NumberPicker) layout.findViewById(R.id.date_picker);
if (mDatePicker != null) {
+ Calendar start = (Calendar) calendar.clone();
+ Calendar finishing = (Calendar) calendar.clone();
+ Calendar finished = (Calendar) calendar.clone();
+ switch (getArguments().getInt(START_MODE, 0)) {
+ case START_TIME_MODE:
+ finishing.setTime(getRaceState().getFinishingTime().asDate());
+ mFutureDays = TimeUtils.daysBetween(finishing, calendar);
+ break;
+
+ case FINISHING_TIME_MODE:
+ start.setTime(getRaceState().getStartTime().asDate());
+ mPastDays = TimeUtils.daysBetween(calendar, start);
+ finished.setTime(getRaceState().getFinishedTime().asDate());
+ mFutureDays = TimeUtils.daysBetween(finished, calendar);
+ break;
+
+ case FINISHED_TIME_MODE:
+ finishing.setTime(getRaceState().getFinishingTime().asDate());
+ mPastDays = TimeUtils.daysBetween(finishing, calendar);
+ break;
+
+ default:
+ // use default values
+ }
ThemeHelper.setPickerTextColor(getActivity(), mDatePicker, ThemeHelper.getColor(getActivity(), R.attr.white));
- TimeUtils.initDatePicker(getActivity(), mDatePicker, calendar, PAST_DAYS, FUTURE_DAYS, false);
- mDatePicker.setValue(Math.abs(PAST_DAYS));
+ TimeUtils.initDatePicker(getActivity(), mDatePicker, calendar, mPastDays, mFutureDays, false);
+ mDatePicker.setValue(Math.abs(mPastDays));
mDatePicker.setTag(calendar);
+ if (mDatePicker.getMinValue() == mDatePicker.getMaxValue()) {
+ mDatePicker.setVisibility(View.GONE);
+ }
}
mTimePicker = (TimePicker) layout.findViewById(R.id.time_picker);
@@ -174,7 +204,7 @@ public class RaceTimeChangeFragment extends BaseFragment implements View.OnClick
private TimePoint getPickerTime() {
Calendar calendar = (Calendar) mDatePicker.getTag();
- calendar.add(Calendar.DAY_OF_MONTH, mDatePicker.getValue() + PAST_DAYS);
+ calendar.add(Calendar.DAY_OF_MONTH, mDatePicker.getValue() + mPastDays);
calendar.set(Calendar.HOUR_OF_DAY, mTimePicker.getCurrentHour());
calendar.set(Calendar.MINUTE, mTimePicker.getCurrentMinute());
calendar.set(Calendar.SECOND, mSecondPicker.getValue());
diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/views/CompassView.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/views/CompassView.java
index 8d11ed6bc88..c6977379610 100644
--- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/views/CompassView.java
+++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/views/CompassView.java
@@ -86,6 +86,9 @@ public class CompassView extends RelativeLayout {
return false;
}
});
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
}
private void generateAndShowAlert() {
@@ -166,7 +169,7 @@ public class CompassView extends RelativeLayout {
if (Math.abs(degree) == 360) {
degree = 0;
}
- degreeView.clearFocus();
+ requestFocus();
changeListener.onDirectionChanged(degree);
}
}
diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/utils/TimeUtils.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/utils/TimeUtils.java
index e6fcfb30372..9efdcc5da97 100644
--- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/utils/TimeUtils.java
+++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/utils/TimeUtils.java
@@ -1,166 +1,190 @@
-package com.sap.sailing.racecommittee.app.utils;
-
-import android.content.Context;
-import android.os.Build;
-import android.text.format.DateFormat;
-import android.view.View;
-import android.widget.NumberPicker;
-import android.widget.TextView;
-import com.sap.sailing.racecommittee.app.R;
-import com.sap.sse.common.TimePoint;
-import com.sap.sse.common.impl.MillisecondsTimePoint;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-
-public class TimeUtils {
-
- private TimeUtils() {
- // only static methods
- }
-
- public static long timeUntil(TimePoint targetTime) {
- return targetTime.asMillis() - MillisecondsTimePoint.now().asMillis();
- }
-
- /**
- * Formats your time to 'kk:mm:ss'.
- *
- * @param timePoint timestamp to format
- */
- public static String formatTime(TimePoint timePoint) {
- return formatTime(timePoint, "kk:mm:ss");
- }
-
- /**
- * Formats your time with the help of {@link DateFormat}.
- *
- * @param timePoint timestamp to format
- * @param format format as defined by {@link DateFormat}
- * @return timestamp formatted as {@link String}
- */
- public static String formatTime(TimePoint timePoint, String format) {
- return DateFormat.format(format, timePoint.asDate()).toString();
- }
-
- public static String formatDurationSince(long milliseconds) {
- int secondsTillStart = (int) Math.floor(milliseconds / 1000f);
- return formatDuration(secondsTillStart);
- }
-
- public static String formatDurationUntil(long milliseconds) {
- int secondsTillStart = (int) Math.ceil(milliseconds / 1000f);
- return formatDuration(secondsTillStart);
- }
-
- public static String calcDuration(Calendar from, Calendar to) {
- String retValue;
-
- long millis = to.getTimeInMillis() - from.getTimeInMillis();
-
- long min = millis / (1000 * 60);
- long sec = (millis - (min * 60 * 1000)) / 1000;
-
- retValue = String.valueOf(sec) + "\"";
- if (retValue.length() == 2) {
- retValue = "0" + retValue;
- }
- if (min > 0) {
- retValue = String.valueOf(min) + "' " + retValue;
- }
-
- return retValue;
- }
-
- public static Calendar floorTime(Calendar calendar) {
- if (calendar == null) {
- calendar = Calendar.getInstance();
- }
- calendar.set(Calendar.MILLISECOND, 0);
-
- return calendar;
- }
-
- public static int daysBetween(Calendar day1, Calendar day2) {
- Calendar dayOne = (Calendar) day1.clone();
- Calendar dayTwo = (Calendar) day2.clone();
-
- if (dayOne.get(Calendar.YEAR) == dayTwo.get(Calendar.YEAR)) {
- return dayOne.get(Calendar.DAY_OF_YEAR) - dayTwo.get(Calendar.DAY_OF_YEAR);
- } else {
- if (dayTwo.get(Calendar.YEAR) > dayOne.get(Calendar.YEAR)) {
- //swap them
- Calendar temp = dayOne;
- dayOne = dayTwo;
- dayTwo = temp;
- }
- int extraDays = 0;
-
- while (dayOne.get(Calendar.YEAR) > dayTwo.get(Calendar.YEAR)) {
- dayOne.add(Calendar.YEAR, -1);
- // getActualMaximum() important for leap years
- extraDays += dayOne.getActualMaximum(Calendar.DAY_OF_YEAR);
- }
-
- return extraDays - dayTwo.get(Calendar.DAY_OF_YEAR) + dayOne.get(Calendar.DAY_OF_YEAR);
- }
- }
-
- private static String formatDuration(int secondsTillStart) {
- int hours = secondsTillStart / 3600;
- int minutes = (secondsTillStart % 3600) / 60;
- int seconds = (secondsTillStart % 60);
- boolean negative = (hours < 0 || minutes < 0 || seconds < 0);
- String timePattern = ((negative) ? "-" : "") + "%s:%s:%s";
- String secondsString = seconds < 10 ? "0" + Math.abs(seconds) : "" + Math.abs(seconds);
- String minutesString = minutes < 10 ? "0" + Math.abs(minutes) : "" + Math.abs(minutes);
- String hoursString = hours < 10 ? "0" + Math.abs(hours) : "" + Math.abs(hours);
- return String.format(timePattern, hoursString, minutesString, secondsString);
- }
-
- public static void initDatePicker(Context context, NumberPicker datePicker, Calendar time, int pastDays, int futureDays) {
- initDatePicker(context, datePicker, time, pastDays, futureDays, true);
- }
-
- public static void initDatePicker(Context context, NumberPicker datePicker, Calendar time, int pastDays, int futureDays, boolean useWords) {
- java.text.DateFormat dateFormat = java.text.DateFormat.getDateInstance();
- ArrayList dates = new ArrayList<>();
- Calendar calendar = Calendar.getInstance();
- calendar.setTime(time.getTime());
- // Past
- calendar.add(Calendar.DAY_OF_MONTH, pastDays);
- for (int i = pastDays; i < -1; i++) {
- dates.add(dateFormat.format(calendar.getTime()));
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- }
- if (useWords) {
- dates.add(context.getString(R.string.yesterday));
- dates.add(context.getString(R.string.today));
- dates.add(context.getString(R.string.tomorrow));
- calendar.add(Calendar.DAY_OF_MONTH, 3);
- } else {
- for (int i = 0; i < 3; i++) {
- dates.add(dateFormat.format(calendar.getTime()));
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- }
- }
- // Future
- for (int i = 3; i <= futureDays + 1; i++) {
- dates.add(dateFormat.format(calendar.getTime()));
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- }
- datePicker.setDisplayedValues(dates.toArray(new String[dates.size()]));
- datePicker.setMinValue(0);
- datePicker.setMaxValue(dates.size() - 1);
- datePicker.setWrapSelectorWheel(false);
- datePicker.setValue(TimeUtils.daysBetween(calendar, Calendar.getInstance()) + Math.abs(pastDays));
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
- TextView date = new TextView(context);
- date.setText(dateFormat.format(calendar.getTime()));
- int padding = context.getResources().getDimensionPixelSize(R.dimen.default_padding_half);
- date.setPadding(padding, padding, padding, padding);
- date.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- datePicker.getLayoutParams().width = date.getMeasuredWidth();
- }
- }
+package com.sap.sailing.racecommittee.app.utils;
+
+import android.content.Context;
+import android.os.Build;
+import android.text.format.DateFormat;
+import android.view.View;
+import android.widget.NumberPicker;
+import android.widget.TextView;
+import com.sap.sailing.racecommittee.app.R;
+import com.sap.sse.common.TimePoint;
+import com.sap.sse.common.impl.MillisecondsTimePoint;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+
+public class TimeUtils {
+
+ private TimeUtils() {
+ // only static methods
+ }
+
+ public static long timeUntil(TimePoint targetTime) {
+ return targetTime.asMillis() - MillisecondsTimePoint.now().asMillis();
+ }
+
+ /**
+ * Formats your time to 'kk:mm:ss'.
+ *
+ * @param timePoint timestamp to format
+ */
+ public static String formatTime(TimePoint timePoint) {
+ return formatTime(timePoint, "kk:mm:ss");
+ }
+
+ /**
+ * Formats your time with the help of {@link DateFormat}.
+ *
+ * @param timePoint timestamp to format
+ * @param format format as defined by {@link DateFormat}
+ * @return timestamp formatted as {@link String}
+ */
+ public static String formatTime(TimePoint timePoint, String format) {
+ return DateFormat.format(format, timePoint.asDate()).toString();
+ }
+
+ public static String formatDuration(TimePoint now, TimePoint timePoint) {
+ return formatDuration(now, timePoint, true);
+ }
+
+ public static String formatDuration(TimePoint now, TimePoint timePoint, boolean inclSign) {
+ String duration;
+ if (timePoint.after(now)) {
+ String sign = (inclSign) ? "-" : "";
+ duration = sign + TimeUtils.formatDurationUntil(timePoint.minus(now.asMillis()).asMillis(), false);
+ } else {
+ duration = TimeUtils.formatDurationSince(now.minus(timePoint.asMillis()).asMillis(), false);
+ }
+ return duration;
+ }
+
+ public static String formatDurationSince(long milliseconds) {
+ return formatDurationSince(milliseconds, true);
+ }
+
+ public static String formatDurationSince(long milliseconds, boolean emptyHours) {
+ int secondsTillStart = (int) Math.floor(milliseconds / 1000f);
+ return formatDuration(secondsTillStart, emptyHours);
+ }
+
+ public static String formatDurationUntil(long milliseconds) {
+ return formatDurationUntil(milliseconds, true);
+ }
+
+ public static String formatDurationUntil(long milliseconds, boolean emptyHours) {
+ int secondsTillStart = (int) Math.ceil(milliseconds / 1000f);
+ return formatDuration(secondsTillStart, emptyHours);
+ }
+
+ public static String calcDuration(Calendar from, Calendar to) {
+ String retValue;
+
+ long millis = to.getTimeInMillis() - from.getTimeInMillis();
+
+ long min = millis / (1000 * 60);
+ long sec = (millis - (min * 60 * 1000)) / 1000;
+
+ retValue = String.valueOf(sec) + "\"";
+ if (retValue.length() == 2) {
+ retValue = "0" + retValue;
+ }
+ if (min > 0) {
+ retValue = String.valueOf(min) + "' " + retValue;
+ }
+
+ return retValue;
+ }
+
+ public static Calendar floorTime(Calendar calendar) {
+ if (calendar == null) {
+ calendar = Calendar.getInstance();
+ }
+ calendar.set(Calendar.MILLISECOND, 0);
+
+ return calendar;
+ }
+
+ public static int daysBetween(Calendar day1, Calendar day2) {
+ Calendar dayOne = (Calendar) day1.clone();
+ Calendar dayTwo = (Calendar) day2.clone();
+
+ if (dayOne.get(Calendar.YEAR) == dayTwo.get(Calendar.YEAR)) {
+ return dayOne.get(Calendar.DAY_OF_YEAR) - dayTwo.get(Calendar.DAY_OF_YEAR);
+ } else {
+ if (dayTwo.get(Calendar.YEAR) > dayOne.get(Calendar.YEAR)) {
+ //swap them
+ Calendar temp = dayOne;
+ dayOne = dayTwo;
+ dayTwo = temp;
+ }
+ int extraDays = 0;
+
+ while (dayOne.get(Calendar.YEAR) > dayTwo.get(Calendar.YEAR)) {
+ dayOne.add(Calendar.YEAR, -1);
+ // getActualMaximum() important for leap years
+ extraDays += dayOne.getActualMaximum(Calendar.DAY_OF_YEAR);
+ }
+
+ return extraDays - dayTwo.get(Calendar.DAY_OF_YEAR) + dayOne.get(Calendar.DAY_OF_YEAR);
+ }
+ }
+
+ private static String formatDuration(int secondsTillStart, boolean emptyHours) {
+ int hours = secondsTillStart / 3600;
+ int minutes = (secondsTillStart % 3600) / 60;
+ int seconds = (secondsTillStart % 60);
+ boolean negative = (hours < 0 || minutes < 0 || seconds < 0);
+ String timePattern = ((negative) ? "-" : "") + "%s%s:%s";
+ String secondsString = seconds < 10 ? "0" + Math.abs(seconds) : "" + Math.abs(seconds);
+ String minutesString = minutes < 10 ? "0" + Math.abs(minutes) : "" + Math.abs(minutes);
+ String hoursString = hours < 10 ? "0" + Math.abs(hours) : "" + Math.abs(hours);
+ hoursString = !emptyHours && hoursString.equals("00") ? "" : hoursString + ":";
+ return String.format(timePattern, hoursString, minutesString, secondsString);
+ }
+
+ public static void initDatePicker(Context context, NumberPicker datePicker, Calendar time, int pastDays, int futureDays) {
+ initDatePicker(context, datePicker, time, pastDays, futureDays, true);
+ }
+
+ public static void initDatePicker(Context context, NumberPicker datePicker, Calendar time, int pastDays, int futureDays, boolean useWords) {
+ java.text.DateFormat dateFormat = java.text.DateFormat.getDateInstance();
+ ArrayList dates = new ArrayList<>();
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(time.getTime());
+ // Past
+ calendar.add(Calendar.DAY_OF_MONTH, pastDays);
+ for (int i = pastDays; i < -1; i++) {
+ dates.add(dateFormat.format(calendar.getTime()));
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ if (useWords) {
+ dates.add(context.getString(R.string.yesterday));
+ dates.add(context.getString(R.string.today));
+ dates.add(context.getString(R.string.tomorrow));
+ calendar.add(Calendar.DAY_OF_MONTH, 3);
+ } else {
+ for (int i = (pastDays == 0 ? pastDays : -1); i < (futureDays < 2 ? futureDays + 1 : 2); i++) {
+ dates.add(dateFormat.format(calendar.getTime()));
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ }
+ // Future
+ for (int i = 2; i <= futureDays; i++) {
+ dates.add(dateFormat.format(calendar.getTime()));
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ datePicker.setDisplayedValues(dates.toArray(new String[dates.size()]));
+ datePicker.setMinValue(0);
+ datePicker.setMaxValue(dates.size() - 1);
+ datePicker.setWrapSelectorWheel(false);
+ datePicker.setValue(TimeUtils.daysBetween(calendar, Calendar.getInstance()) + Math.abs(pastDays));
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ TextView date = new TextView(context);
+ date.setText(dateFormat.format(calendar.getTime()));
+ int padding = context.getResources().getDimensionPixelSize(R.dimen.default_padding_half);
+ date.setPadding(padding, padding, padding, padding);
+ date.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ datePicker.getLayoutParams().width = date.getMeasuredWidth();
+ }
+ }
}