Merge branch 'ubilabs--rcapp--bug-3525' into ubilabs--rcapp--master

This commit is contained in:
Bennet Brunsen committed 2016-06-09 11:48:08 +02:00
commit 72ad93dfd4
13 files changed
+271 -226

No files matched your search

@@ -22,7 +22,7 @@ public class DurationTest {
TimePoint t2 = t1.plus(d); TimePoint t2 = t1.plus(d);
assertEquals(123+234, t2.asMillis()); assertEquals(123+234, t2.asMillis());
} }
@Test @Test
public void testDurationMinus() { public void testDurationMinus() {
TimePoint t1 = new MillisecondsTimePoint(234); TimePoint t1 = new MillisecondsTimePoint(234);
@@ -1,10 +1,14 @@
package com.sap.sailing.domain.common.test; package com.sap.sailing.domain.common.test;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.Calendar;
import java.util.GregorianCalendar;
import org.junit.Test; import org.junit.Test;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
@@ -54,4 +58,29 @@ public class TimePointTest {
assertFalse(t.equals("s")); assertFalse(t.equals("s"));
} }
@Test
public void nearestFullMinute() {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.MILLISECOND, 0);
cal.set(2016, 5, 15, 10, 12, 33); // 10:12:33 on June 15, 2016 in the local time zone
final TimePoint start = new MillisecondsTimePoint(cal.getTime());
cal.set(2016, 5, 15, 10, 8, 35); // 10:08:35 on June 15, 2016 in the local time zone
final TimePoint t1 = new MillisecondsTimePoint(cal.getTime());
cal.set(2016, 5, 15, 10, 8, 31); // 10:08:31 on June 15, 2016 in the local time zone
final TimePoint t2 = new MillisecondsTimePoint(cal.getTime());
cal.set(2016, 5, 15, 10, 15, 31); // 10:15:31 on June 15, 2016 in the local time zone
final TimePoint t3 = new MillisecondsTimePoint(cal.getTime());
cal.set(2016, 5, 15, 10, 15, 37); // 10:15:31 on June 15, 2016 in the local time zone
final TimePoint t4 = new MillisecondsTimePoint(cal.getTime());
assertEquals(new MillisecondsTimePoint(new GregorianCalendar(2016, 5, 15, 10, 12, 35).getTime()), start.getNearestModuloOneMinute(t1));
assertEquals(new MillisecondsTimePoint(new GregorianCalendar(2016, 5, 15, 10, 12, 31).getTime()), start.getNearestModuloOneMinute(t2));
assertEquals(new MillisecondsTimePoint(new GregorianCalendar(2016, 5, 15, 10, 12, 31).getTime()), start.getNearestModuloOneMinute(t3));
assertEquals(new MillisecondsTimePoint(new GregorianCalendar(2016, 5, 15, 10, 12, 37).getTime()), start.getNearestModuloOneMinute(t4));
// and now at least on milliseconds example
cal.set(Calendar.MILLISECOND, 123);
final TimePoint t5 = new MillisecondsTimePoint(cal.getTime());
final Calendar t5Compare = new GregorianCalendar(2016, 5, 15, 10, 12, 37);
t5Compare.set(Calendar.MILLISECOND, 123);
assertEquals(new MillisecondsTimePoint(t5Compare.getTime()), start.getNearestModuloOneMinute(t5));
}
} }
@@ -4,6 +4,14 @@
<div id="mainContent"> <div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sailing Race Manager</h4> <h4 class="articleHeadline">What's New - SAP Sailing Race Manager</h4>
<div class="innerContent"> <div class="innerContent">
<h5 class="articleSubheadline">June 2016</h5>
<ul class="bulletList">
<li>The start time editing screen has now a "Sync to minute" button, which replaces
the both buttons beside the countdown. This button sets the countdown to the nearest
minute and after "Set time" the new start time will be sent to the server.</li>
</ul>
<h5 class="articleSubheadline">May 2016</h5> <h5 class="articleSubheadline">May 2016</h5>
<ul class="bulletList"> <ul class="bulletList">
@@ -47,4 +47,10 @@ public interface Duration extends Serializable, Comparable<Duration> {
Duration plus(long milliseconds); Duration plus(long milliseconds);
Duration plus(Duration duration); Duration plus(Duration duration);
/**
* The "modulo" operation; imagine the operation implemented as this duration's milliseconds
* modulo {@code d}'s milliseconds
*/
Duration mod(Duration d);
} }
@@ -32,4 +32,11 @@ public interface TimePoint extends Comparable<TimePoint>, Serializable {
boolean after(TimePoint other); boolean after(TimePoint other);
boolean before(TimePoint other); boolean before(TimePoint other);
/**
* @return a {@link TimePoint} such that when used to set the time of a calendar, the seconds and milliseconds will
* match that of {@code syncToClosestSecondsAndMillisOf} and the time difference to {@code this} time point is
* minimal. In particular, the time difference will always be less than or equal to 30s.
*/
TimePoint getNearestModuloOneMinute(TimePoint syncToClosestSecondsAndMillisOf);
} }
@@ -116,4 +116,21 @@ public abstract class AbstractTimePoint implements TimePoint {
* as {@link #getDate()}. * as {@link #getDate()}.
*/ */
protected void cacheDate(Date date) {} protected void cacheDate(Date date) {}
@Override
public TimePoint getNearestModuloOneMinute(final TimePoint syncToClosestSecondsAndMillisOf) {
final Duration diffModOneMinute = this.until(syncToClosestSecondsAndMillisOf).mod(Duration.ONE_MINUTE);
final Duration diffToAdd;
if (diffModOneMinute.compareTo(Duration.ONE_SECOND.times(30l)) > 0) {
diffToAdd = Duration.ONE_MINUTE.minus(diffModOneMinute).times(-1l);
} else if (diffModOneMinute.compareTo(Duration.ONE_SECOND.times(-30l)) < 0) {
diffToAdd = Duration.ONE_MINUTE.minus(diffModOneMinute.times(-1l));
} else {
diffToAdd = diffModOneMinute;
}
final TimePoint result = this.plus(diffToAdd);
assert this.until(result).abs().compareTo(Duration.ONE_SECOND.times(30)) <= 0;
assert syncToClosestSecondsAndMillisOf.asMillis() % 60000 == result.asMillis() % 60000;
return result;
}
} }
@@ -117,4 +117,9 @@ public class MillisecondsDurationImpl implements Duration {
} }
} }
@Override
public Duration mod(Duration d) {
return new MillisecondsDurationImpl(asMillis() % d.asMillis());
}
} }
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<RelativeLayout <RelativeLayout
android:id="@+id/header" android:id="@+id/header"
@@ -25,13 +25,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:src="?attr/chevron_left_24dp" android:src="?attr/chevron_left_24dp"
android:visibility="gone" /> android:visibility="gone"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/set_time" android:text="@string/set_time"
android:textAppearance="?attr/textHeader" /> android:textAppearance="?attr/textHeader"/>
</LinearLayout> </LinearLayout>
<View <View
@@ -39,7 +39,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/thin_line" android:layout_height="@dimen/thin_line"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:background="?attr/black" /> android:background="?attr/black"/>
</RelativeLayout> </RelativeLayout>
<LinearLayout <LinearLayout
@@ -63,7 +63,7 @@
android:text="@string/dependent_races_absolute" android:text="@string/dependent_races_absolute"
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="?attr/sap_yellow_1" android:textColor="?attr/sap_yellow_1"
android:textSize="@dimen/textSize_20" /> android:textSize="@dimen/textSize_20"/>
<Button <Button
android:id="@+id/relative" android:id="@+id/relative"
@@ -76,7 +76,7 @@
android:text="@string/dependent_races_relative" android:text="@string/dependent_races_relative"
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="?attr/sap_light_gray" android:textColor="?attr/sap_light_gray"
android:textSize="@dimen/textSize_20" /> android:textSize="@dimen/textSize_20"/>
</LinearLayout> </LinearLayout>
<FrameLayout <FrameLayout
@@ -103,21 +103,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_marginTop="@dimen/default_margin_half"
android:orientation="horizontal"> android:orientation="horizontal">
<Button
android:id="@+id/minute_inc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="?attr/button_filled_bordered"
android:drawableLeft="?attr/chevron_left"
android:paddingRight="@dimen/default_padding_half"
android:textColor="?attr/white"
android:textSize="@dimen/textSize_20"
tools:text="1:00:00" />
<TextView <TextView
android:id="@+id/start_countdown" android:id="@+id/start_countdown"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -126,21 +113,9 @@
android:layout_marginLeft="@dimen/default_margin" android:layout_marginLeft="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin" android:layout_marginRight="@dimen/default_margin"
android:textColor="?attr/white" android:textColor="?attr/white"
android:textSize="@dimen/textSize_20" android:textSize="@dimen/textSize_72"
android:visibility="visible" android:visibility="visible"
tools:text="1:01:00" /> tools:text="1:01:00"/>
<Button
android:id="@+id/minute_dec"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="?attr/button_filled_bordered"
android:drawableRight="?attr/chevron_right"
android:paddingLeft="@dimen/default_padding_half"
android:textColor="?attr/white"
android:textSize="@dimen/textSize_20"
tools:text="1:02:00" />
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
@@ -162,14 +137,21 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:descendantFocusability="blocksDescendants" /> android:descendantFocusability="blocksDescendants"/>
<include <include
android:id="@+id/start_time_picker" android:id="@+id/start_time_picker"
layout="@layout/time_picker" layout="@layout/time_picker"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" /> android:layout_gravity="center_vertical"/>
<NumberPicker
android:id="@+id/start_time_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:descendantFocusability="blocksDescendants"/>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
@@ -178,14 +160,29 @@
android:id="@+id/set_start_time_absolute" android:id="@+id/set_start_time_absolute"
style="?attr/button" style="?attr/button"
android:layout_width="@dimen/default_button_width" android:layout_width="@dimen/default_button_width"
android:layout_height="wrap_content" android:layout_height="@dimen/default_button_height"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_marginBottom="@dimen/side_padding" android:layout_marginBottom="@dimen/side_padding"
android:layout_marginTop="@dimen/side_padding" android:layout_marginTop="@dimen/side_padding"
android:maxWidth="400dp" android:maxWidth="400dp"
android:text="@string/race_set_time" android:text="@string/race_set_time"
android:textSize="@dimen/textSize_18" /> android:textSize="@dimen/textSize_18"/>
<Button
android:id="@+id/sync_to_minute"
android:layout_width="@dimen/default_button_width"
android:layout_height="@dimen/default_button_height"
android:layout_above="@id/set_start_time_absolute"
android:layout_centerHorizontal="true"
android:background="?attr/button_filled_bordered"
android:paddingLeft="@dimen/default_padding_half"
android:paddingRight="@dimen/default_padding_half"
android:text="@string/sync_to_minute"
android:textColor="?attr/white"
android:textSize="@dimen/textSize_18"
android:visibility="gone"
tools:visibility="visible"/>
<TextView <TextView
android:id="@+id/debug_time" android:id="@+id/debug_time"
@@ -195,8 +192,7 @@
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:visibility="gone" android:visibility="gone"
tools:text="12:12:12" /> tools:text="12:12:12"/>
</RelativeLayout> </RelativeLayout>
<RelativeLayout <RelativeLayout
@@ -227,7 +223,7 @@
android:layout_marginLeft="@dimen/default_margin" android:layout_marginLeft="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin" android:layout_marginTop="@dimen/default_margin"
android:scaleX="@integer/picker_scale" android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale" /> android:scaleY="@integer/picker_scale"/>
<LinearLayout <LinearLayout
android:id="@+id/relative_spinner_area" android:id="@+id/relative_spinner_area"
@@ -246,7 +242,7 @@
android:layout_marginTop="@dimen/default_margin_half" android:layout_marginTop="@dimen/default_margin_half"
android:background="?attr/spinner_background" android:background="?attr/spinner_background"
android:dropDownWidth="wrap_content" android:dropDownWidth="wrap_content"
android:spinnerMode="dropdown" /> android:spinnerMode="dropdown"/>
<Spinner <Spinner
android:id="@+id/dependent_fleet" android:id="@+id/dependent_fleet"
@@ -257,7 +253,7 @@
android:dropDownWidth="wrap_content" android:dropDownWidth="wrap_content"
android:spinnerMode="dropdown" android:spinnerMode="dropdown"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible"/>
<Spinner <Spinner
android:id="@+id/dependent_racegroup" android:id="@+id/dependent_racegroup"
@@ -266,7 +262,7 @@
android:layout_marginTop="@dimen/default_margin_half" android:layout_marginTop="@dimen/default_margin_half"
android:background="?attr/spinner_background" android:background="?attr/spinner_background"
android:dropDownWidth="wrap_content" android:dropDownWidth="wrap_content"
android:spinnerMode="dropdown" /> android:spinnerMode="dropdown"/>
</LinearLayout> </LinearLayout>
<TextView <TextView
@@ -277,7 +273,7 @@
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:paddingLeft="@dimen/spinner_padding" android:paddingLeft="@dimen/spinner_padding"
android:text="@string/minutes_after" android:text="@string/minutes_after"
android:textAppearance="?attr/textSmall" /> android:textAppearance="?attr/textSmall"/>
</RelativeLayout> </RelativeLayout>
</RelativeLayout> </RelativeLayout>
@@ -285,14 +281,14 @@
android:id="@+id/set_start_time_relative" android:id="@+id/set_start_time_relative"
style="?attr/button" style="?attr/button"
android:layout_width="@dimen/default_button_width" android:layout_width="@dimen/default_button_width"
android:layout_height="wrap_content" android:layout_height="@dimen/default_button_height"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_marginBottom="@dimen/side_padding" android:layout_marginBottom="@dimen/side_padding"
android:layout_marginTop="@dimen/side_padding" android:layout_marginTop="@dimen/side_padding"
android:maxWidth="400dp" android:maxWidth="400dp"
android:text="@string/race_set_time" android:text="@string/race_set_time"
android:textSize="@dimen/textSize_18" /> android:textSize="@dimen/textSize_18"/>
</RelativeLayout> </RelativeLayout>
</FrameLayout> </FrameLayout>
</RelativeLayout> </RelativeLayout>
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<RelativeLayout <RelativeLayout
android:id="@+id/header" android:id="@+id/header"
@@ -25,13 +25,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:src="?attr/chevron_left_24dp" android:src="?attr/chevron_left_24dp"
android:visibility="gone" /> android:visibility="gone"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/set_time" android:text="@string/set_time"
android:textAppearance="?attr/textHeader" /> android:textAppearance="?attr/textHeader"/>
</LinearLayout> </LinearLayout>
<View <View
@@ -39,7 +39,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/thick_line" android:layout_height="@dimen/thick_line"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:background="?attr/black" /> android:background="?attr/black"/>
</RelativeLayout> </RelativeLayout>
<LinearLayout <LinearLayout
@@ -61,7 +61,7 @@
android:text="@string/dependent_races_absolute" android:text="@string/dependent_races_absolute"
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="?attr/sap_yellow_1" android:textColor="?attr/sap_yellow_1"
android:textSize="@dimen/textSize_26" /> android:textSize="@dimen/textSize_26"/>
<Button <Button
android:id="@+id/relative" android:id="@+id/relative"
@@ -72,7 +72,7 @@
android:text="@string/dependent_races_relative" android:text="@string/dependent_races_relative"
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="?attr/sap_light_gray" android:textColor="?attr/sap_light_gray"
android:textSize="@dimen/textSize_26" /> android:textSize="@dimen/textSize_26"/>
</LinearLayout> </LinearLayout>
<FrameLayout <FrameLayout
@@ -87,7 +87,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/sap_gray_black_30" android:background="?attr/sap_gray_black_30"
android:visibility="gone" android:visibility="gone"
tools:visibility="gone"> tools:visibility="visible">
<RelativeLayout <RelativeLayout
android:id="@+id/buttonBar" android:id="@+id/buttonBar"
@@ -102,18 +102,6 @@
android:layout_marginTop="@dimen/default_margin_half" android:layout_marginTop="@dimen/default_margin_half"
android:orientation="horizontal"> android:orientation="horizontal">
<Button
android:id="@+id/minute_inc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="?attr/button_filled_bordered"
android:drawableLeft="?attr/chevron_left"
android:paddingRight="@dimen/default_padding_half"
android:textAppearance="?attr/textLarge"
android:textColor="?attr/white"
tools:text="1:00:00" />
<TextView <TextView
android:id="@+id/start_countdown" android:id="@+id/start_countdown"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -123,19 +111,7 @@
android:layout_marginRight="@dimen/default_margin" android:layout_marginRight="@dimen/default_margin"
android:textAppearance="?attr/textXLarge" android:textAppearance="?attr/textXLarge"
android:visibility="visible" android:visibility="visible"
tools:text="1:01:00" /> tools:text="1:01:00"/>
<Button
android:id="@+id/minute_dec"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="?attr/button_filled_bordered"
android:drawableRight="?attr/chevron_right"
android:paddingLeft="@dimen/default_padding_half"
android:textAppearance="?attr/textLarge"
android:textColor="?attr/white"
tools:text="1:02:00" />
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
@@ -145,14 +121,33 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true"> android:layout_alignParentBottom="true">
<Button <LinearLayout
android:id="@+id/set_start_time_absolute"
style="?attr/buttonSolidSmall"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_margin="@dimen/default_margin_twice" android:layout_margin="@dimen/default_margin"
android:text="@string/race_set_time" /> android:orientation="horizontal">
<Button
android:id="@+id/sync_to_minute"
style="?attr/buttonSolidSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:background="?attr/button_filled_bordered"
android:text="@string/sync_to_minute"
android:textColor="?attr/white"
android:visibility="gone"
tools:visibility="visible"/>
<Button
android:id="@+id/set_start_time_absolute"
style="?attr/buttonSolidSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:text="@string/race_set_time"/>
</LinearLayout>
</RelativeLayout> </RelativeLayout>
<RelativeLayout <RelativeLayout
@@ -174,14 +169,21 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:descendantFocusability="blocksDescendants" /> android:descendantFocusability="blocksDescendants"/>
<include <include
android:id="@+id/start_time_picker" android:id="@+id/start_time_picker"
layout="@layout/time_picker" layout="@layout/time_picker"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" /> android:layout_gravity="center_vertical"/>
<NumberPicker
android:id="@+id/start_time_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:descendantFocusability="blocksDescendants"/>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
@@ -194,7 +196,7 @@
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:visibility="gone" android:visibility="gone"
tools:text="12:12:12" /> tools:text="12:12:12"/>
</RelativeLayout> </RelativeLayout>
<RelativeLayout <RelativeLayout
@@ -203,7 +205,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/sap_gray_black_30" android:background="?attr/sap_gray_black_30"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"> tools:visibility="gone">
<RelativeLayout <RelativeLayout
android:id="@+id/set_start_time_2" android:id="@+id/set_start_time_2"
@@ -218,13 +220,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_margin="@dimen/default_margin_twice" android:layout_margin="@dimen/default_margin_twice"
android:text="@string/race_set_time" /> android:text="@string/race_set_time"/>
</RelativeLayout> </RelativeLayout>
<RelativeLayout <RelativeLayout
android:layout_above="@id/set_start_time_2"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:layout_above="@id/set_start_time_2">
<LinearLayout <LinearLayout
android:id="@+id/relative_input" android:id="@+id/relative_input"
@@ -239,7 +241,7 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="@dimen/default_margin" android:layout_margin="@dimen/default_margin"
android:scaleX="@integer/picker_scale" android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale" /> android:scaleY="@integer/picker_scale"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -247,7 +249,7 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:padding="@dimen/default_padding_half" android:padding="@dimen/default_padding_half"
android:text="@string/minutes_after" android:text="@string/minutes_after"
android:textAppearance="?attr/textLarge" /> android:textAppearance="?attr/textLarge"/>
<LinearLayout <LinearLayout
android:layout_width="@dimen/unscheduled_spinner_width" android:layout_width="@dimen/unscheduled_spinner_width"
@@ -261,7 +263,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/spinner_background" android:background="?attr/spinner_background"
android:dropDownWidth="wrap_content" android:dropDownWidth="wrap_content"
android:spinnerMode="dropdown" /> android:spinnerMode="dropdown"/>
<Spinner <Spinner
android:id="@+id/dependent_fleet" android:id="@+id/dependent_fleet"
@@ -272,7 +274,7 @@
android:dropDownWidth="wrap_content" android:dropDownWidth="wrap_content"
android:spinnerMode="dropdown" android:spinnerMode="dropdown"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible"/>
<Spinner <Spinner
android:id="@+id/dependent_racegroup" android:id="@+id/dependent_racegroup"
@@ -281,7 +283,7 @@
android:layout_marginTop="@dimen/default_margin_half" android:layout_marginTop="@dimen/default_margin_half"
android:background="?attr/spinner_background" android:background="?attr/spinner_background"
android:dropDownWidth="wrap_content" android:dropDownWidth="wrap_content"
android:spinnerMode="dropdown" /> android:spinnerMode="dropdown"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
@@ -512,5 +512,6 @@
<string name="abort_dismiss">Verwerfen</string> <string name="abort_dismiss">Verwerfen</string>
<string name="set_column_factor">Setze Faktor</string> <string name="set_column_factor">Setze Faktor</string>
<string name="set_factor_error">Beim Setzen des Faktors ist ein Fehler aufgetreten: %s</string> <string name="set_factor_error">Beim Setzen des Faktors ist ein Fehler aufgetreten: %s</string>
<string name="sync_to_minute">Sync zur Minute</string>
</resources> </resources>
@@ -540,5 +540,6 @@
<string name="race_factor_format" translatable="false">x #.####</string> <string name="race_factor_format" translatable="false">x #.####</string>
<string name="set_column_factor">Set Factor</string> <string name="set_column_factor">Set Factor</string>
<string name="set_factor_error">There was an error while setting the factor: %s</string> <string name="set_factor_error">There was an error while setting the factor: %s</string>
<string name="sync_to_minute">Sync to minute</string>
</resources> </resources>
@@ -1,5 +1,6 @@
package com.sap.sailing.racecommittee.app.ui.fragments.raceinfo; package com.sap.sailing.racecommittee.app.ui.fragments.raceinfo;
import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
@@ -9,21 +10,6 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import android.app.FragmentTransaction;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.NumberPicker;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import com.sap.sailing.android.shared.util.AppUtils; import com.sap.sailing.android.shared.util.AppUtils;
import com.sap.sailing.android.shared.util.BitmapHelper; import com.sap.sailing.android.shared.util.BitmapHelper;
import com.sap.sailing.android.shared.util.ViewHelper; import com.sap.sailing.android.shared.util.ViewHelper;
@@ -53,6 +39,21 @@ import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.util.NaturalComparator; import com.sap.sse.common.util.NaturalComparator;
import android.app.FragmentTransaction;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.NumberPicker;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
public class StartTimeFragment extends BaseFragment public class StartTimeFragment extends BaseFragment
implements View.OnClickListener, NumberPicker.OnValueChangeListener, TimePicker.OnTimeChangedListener { implements View.OnClickListener, NumberPicker.OnValueChangeListener, TimePicker.OnTimeChangedListener {
@@ -61,7 +62,6 @@ public class StartTimeFragment extends BaseFragment
private static final int FUTURE_DAYS = 25; private static final int FUTURE_DAYS = 25;
private static final int PAST_DAYS = 3; private static final int PAST_DAYS = 3;
private static final int MAX_DIFF_MIN = 60; private static final int MAX_DIFF_MIN = 60;
private static final String ZERO_TIME = "-00:00:00";
private static final int ABSOLUTE = 0; private static final int ABSOLUTE = 0;
private static final int RELATIVE = 1; private static final int RELATIVE = 1;
@@ -77,14 +77,10 @@ public class StartTimeFragment extends BaseFragment
private Spinner mFleet; private Spinner mFleet;
private Spinner mRace; private Spinner mRace;
private TimePicker mTimePicker; private TimePicker mTimePicker;
private NumberPicker mStartSeconds;
private TextView mCountdown; private TextView mCountdown;
private TextView mDebugTime; private TextView mDebugTime;
private Button mMinuteInc;
private Button mMinuteDec;
private TimePoint mStartTime; private TimePoint mStartTime;
private Calendar mTimeLeft;
private Calendar mTimeRight;
private Calendar mCalendar;
private SimpleRaceLogIdentifier mRaceId; private SimpleRaceLogIdentifier mRaceId;
private Duration mStartTimeOffset; private Duration mStartTimeOffset;
private boolean mListenerIgnore = true; private boolean mListenerIgnore = true;
@@ -99,10 +95,6 @@ public class StartTimeFragment extends BaseFragment
*/ */
private RaceStateChangedListener raceStateChangedListener; private RaceStateChangedListener raceStateChangedListener;
public StartTimeFragment() {
mCalendar = Calendar.getInstance();
}
public static StartTimeFragment newInstance(int startMode) { public static StartTimeFragment newInstance(int startMode) {
StartTimeFragment fragment = new StartTimeFragment(); StartTimeFragment fragment = new StartTimeFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
@@ -155,14 +147,10 @@ public class StartTimeFragment extends BaseFragment
} }
mCountdown = ViewHelper.get(layout, R.id.start_countdown); mCountdown = ViewHelper.get(layout, R.id.start_countdown);
mMinuteInc = ViewHelper.get(layout, R.id.minute_inc);
if (mMinuteInc != null) {
mMinuteInc.setOnClickListener(this);
}
mMinuteDec = ViewHelper.get(layout, R.id.minute_dec); Button syncMinute = ViewHelper.get(layout, R.id.sync_to_minute);
if (mMinuteDec != null) { if (syncMinute != null) {
mMinuteDec.setOnClickListener(this); syncMinute.setOnClickListener(this);
} }
View setStartAbsolute = ViewHelper.get(layout, R.id.set_start_time_absolute); View setStartAbsolute = ViewHelper.get(layout, R.id.set_start_time_absolute);
@@ -179,20 +167,6 @@ public class StartTimeFragment extends BaseFragment
return layout; return layout;
} }
@Override
public void onPause() {
super.onPause();
getRaceState().removeChangedListener(raceStateChangedListener);
}
@Override
public void onResume() {
super.onResume();
getRaceState().addChangedListener(raceStateChangedListener);
}
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
@@ -209,6 +183,7 @@ public class StartTimeFragment extends BaseFragment
View header = ViewHelper.get(getView(), R.id.header); View header = ViewHelper.get(getView(), R.id.header);
View back = ViewHelper.get(getView(), R.id.header_back); View back = ViewHelper.get(getView(), R.id.header_back);
View text = ViewHelper.get(getView(), R.id.header_text); View text = ViewHelper.get(getView(), R.id.header_text);
View sync = ViewHelper.get(getView(), R.id.sync_to_minute);
switch (getArguments().getInt(START_MODE, START_MODE_PRESETUP)) { switch (getArguments().getInt(START_MODE, START_MODE_PRESETUP)) {
case START_MODE_PLANNED: case START_MODE_PLANNED:
if (back != null) { if (back != null) {
@@ -238,6 +213,9 @@ public class StartTimeFragment extends BaseFragment
text.setOnClickListener(this); text.setOnClickListener(this);
} }
} }
if (sync != null) {
sync.setVisibility(View.VISIBLE);
}
StartTimeFinderResult result = getRaceState().getStartTimeFinderResult(); StartTimeFinderResult result = getRaceState().getStartTimeFinderResult();
if (result != null && result.isDependentStartTime()) { if (result != null && result.isDependentStartTime()) {
@@ -258,6 +236,11 @@ public class StartTimeFragment extends BaseFragment
if (syncButtons != null) { if (syncButtons != null) {
syncButtons.setVisibility(View.GONE); syncButtons.setVisibility(View.GONE);
} }
View startSeconds = ViewHelper.get(getView(), R.id.start_time_seconds);
if (startSeconds != null) {
startSeconds.setVisibility(View.GONE);
}
break; break;
} }
} }
@@ -272,6 +255,20 @@ public class StartTimeFragment extends BaseFragment
initViewsRelative(); initViewsRelative();
} }
@Override
public void onPause() {
super.onPause();
getRaceState().removeChangedListener(raceStateChangedListener);
}
@Override
public void onResume() {
super.onResume();
getRaceState().addChangedListener(raceStateChangedListener);
}
private void initViewsRelative() { private void initViewsRelative() {
mTimeOffset = ViewHelper.get(getView(), R.id.time_offset); mTimeOffset = ViewHelper.get(getView(), R.id.time_offset);
if (mTimeOffset != null) { if (mTimeOffset != null) {
@@ -461,7 +458,7 @@ public class StartTimeFragment extends BaseFragment
for (RaceGroupSeriesFleet races : mGroupHeaders.keySet()) { for (RaceGroupSeriesFleet races : mGroupHeaders.keySet()) {
Util.Pair<String, String> leaderBoard = mLeaderBoardAdapter.getItem(mLeaderBoard.getSelectedItemPosition()); Util.Pair<String, String> leaderBoard = mLeaderBoardAdapter.getItem(mLeaderBoard.getSelectedItemPosition());
if (races.getRaceGroup().getName().equals(leaderBoard.getA())) { if (races.getRaceGroup().getName().equals(leaderBoard.getA())) {
if(races.getFleet().getName().equals(fleet)) { if (races.getFleet().getName().equals(fleet)) {
for (ManagedRace race : mGroupHeaders.get(races)) { for (ManagedRace race : mGroupHeaders.get(races)) {
if (!getRace().equals(race) && race.getStatus() != RaceLogRaceStatus.FINISHED) { if (!getRace().equals(race) && race.getStatus() != RaceLogRaceStatus.FINISHED) {
return true; return true;
@@ -506,6 +503,15 @@ public class StartTimeFragment extends BaseFragment
mTimePicker.setCurrentMinute(minutes); mTimePicker.setCurrentMinute(minutes);
mTimePicker.setTag(time.get(Calendar.SECOND)); mTimePicker.setTag(time.get(Calendar.SECOND));
} }
mStartSeconds = ViewHelper.get(getView(), R.id.start_time_seconds);
if (mStartSeconds != null) {
ThemeHelper.setPickerColor(getActivity(), mStartSeconds, ThemeHelper.getColor(getActivity(), R.attr.sap_light_gray), ThemeHelper
.getColor(getActivity(), R.attr.sap_light_gray));
ThemeHelper.setPickerTextSize(getActivity(), mStartSeconds, R.dimen.textSize_14);
mStartSeconds.setEnabled(false);
setSeconds();
}
} }
@Override @Override
@@ -516,33 +522,17 @@ public class StartTimeFragment extends BaseFragment
int resId; int resId;
TimePoint timePoint; TimePoint timePoint;
String time; String time;
String timeLeft;
String timeRight;
if (mStartTime == null) { if (mStartTime == null) {
mStartTime = getPickerTime(); mStartTime = getPickerTime();
} }
if (mStartTime.after(now)) { if (mStartTime.after(now)) {
resId = R.string.race_start_time_in; resId = R.string.race_start_time_in;
timePoint = mStartTime.minus(now.asMillis()); timePoint = mStartTime.minus(now.asMillis());
setButtonTime(timePoint, false);
time = TimeUtils.formatDurationUntil(timePoint.asMillis()); time = TimeUtils.formatDurationUntil(timePoint.asMillis());
timeLeft = TimeUtils.formatDurationUntil(mTimeLeft.getTimeInMillis());
timeRight = TimeUtils.formatDurationUntil(mTimeRight.getTimeInMillis());
if (timeRight.equals(time)) {
mTimeRight.add(Calendar.MINUTE, 1);
timeRight = TimeUtils.formatDurationUntil(mTimeRight.getTimeInMillis());
}
} else { } else {
resId = R.string.race_start_time_ago; resId = R.string.race_start_time_ago;
timePoint = now.minus(mStartTime.asMillis()); timePoint = now.minus(mStartTime.asMillis());
setButtonTime(timePoint, true);
time = TimeUtils.formatDurationSince(timePoint.asMillis()); time = TimeUtils.formatDurationSince(timePoint.asMillis());
timeLeft = TimeUtils.formatDurationSince(mTimeLeft.getTimeInMillis());
timeRight = TimeUtils.formatDurationSince(mTimeRight.getTimeInMillis());
if (timeRight.equals(time)) {
mTimeRight.add(Calendar.MINUTE, -1);
timeRight = TimeUtils.formatDurationSince(mTimeRight.getTimeInMillis());
}
} }
if (mCountdown != null) { if (mCountdown != null) {
@@ -551,22 +541,6 @@ public class StartTimeFragment extends BaseFragment
mCountdown.setTag(resId); mCountdown.setTag(resId);
} }
if (mMinuteDec != null) {
String countdown = getString(resId, timeLeft);
if (ZERO_TIME.equals(countdown)) {
countdown = countdown.substring(1);
}
mMinuteDec.setText(countdown);
}
if (mMinuteInc != null) {
String countdown = getString(resId, timeRight);
if (ZERO_TIME.equals(countdown)) {
countdown = countdown.substring(1);
}
mMinuteInc.setText(countdown);
}
if (mDebugTime != null) { if (mDebugTime != null) {
mDebugTime.setText(mStartTime.asDate().toString()); mDebugTime.setText(mStartTime.asDate().toString());
} }
@@ -622,56 +596,9 @@ public class StartTimeFragment extends BaseFragment
} }
} }
private void setButtonTime(TimePoint timePoint, boolean reverse) {
mCalendar.setTime(timePoint.asDate());
mTimeLeft = getNewTime(mCalendar, (reverse) ? 1 : 0);
mTimeRight = getNewTime(mCalendar, (reverse) ? 0 : 1);
}
private Calendar getNewTime(Calendar calendar, int upDown) {
Calendar newCalendar = (Calendar) calendar.clone();
newCalendar.add(Calendar.MINUTE, upDown);
newCalendar.set(Calendar.SECOND, 0);
newCalendar.set(Calendar.MILLISECOND, 0);
return newCalendar;
}
@Override @Override
public void onClick(View view) { public void onClick(View view) {
switch (view.getId()) { switch (view.getId()) {
case R.id.minute_inc:
case R.id.minute_dec:
mCalendar = Calendar.getInstance();
int msec = mCalendar.get(Calendar.MILLISECOND);
mCalendar.set(Calendar.MILLISECOND, msec);
String button = ((TextView) view).getText().toString();
boolean down = button.substring(0, 1).equals("-");
String[] values = button.split(":");
int hour = Integer.parseInt(values[0]);
int min = Integer.parseInt(values[1]);
if (view.getId() == R.id.minute_dec) { // button right
if (!down) { // time is positive
mCalendar.add(Calendar.HOUR_OF_DAY, -1 * hour);
mCalendar.add(Calendar.MINUTE, -1 * min);
} else {
mCalendar.add(Calendar.HOUR_OF_DAY, -1 * hour);
mCalendar.add(Calendar.MINUTE, min);
}
} else { // button left
if (!down) { // time is positive
mCalendar.add(Calendar.HOUR_OF_DAY, -1 * hour);
mCalendar.add(Calendar.MINUTE, -1 * min);
} else {
mCalendar.add(Calendar.HOUR_OF_DAY, -1 * hour);
mCalendar.add(Calendar.MINUTE, min);
}
}
mStartTime = new MillisecondsTimePoint(mCalendar.getTimeInMillis());
mListenerIgnore = true;
setPickerTime();
break;
case R.id.set_start_time_absolute: case R.id.set_start_time_absolute:
changeFragment(mStartTime); changeFragment(mStartTime);
break; break;
@@ -680,6 +607,10 @@ public class StartTimeFragment extends BaseFragment
changeFragment(MillisecondsTimePoint.now(), new MillisecondsDurationImpl(mTimeOffset.getValue() * 60 * 1000), identifier); changeFragment(MillisecondsTimePoint.now(), new MillisecondsDurationImpl(mTimeOffset.getValue() * 60 * 1000), identifier);
break; break;
case R.id.sync_to_minute:
syncToMinute();
break;
case R.id.header_text: case R.id.header_text:
if (getArguments() != null && getArguments().getInt(START_MODE, START_MODE_PRESETUP) == START_MODE_PRESETUP) { if (getArguments() != null && getArguments().getInt(START_MODE, START_MODE_PRESETUP) == START_MODE_PRESETUP) {
changeFragment(); changeFragment();
@@ -694,10 +625,18 @@ public class StartTimeFragment extends BaseFragment
} }
} }
private void syncToMinute() {
final TimePoint now = MillisecondsTimePoint.now();
mStartTime = mStartTime.getNearestModuloOneMinute(now);
mListenerIgnore = true;
setPickerTime();
}
@Override @Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
if (!mListenerIgnore) { if (!mListenerIgnore) {
mStartTime = new MillisecondsTimePoint(getPickerTime().asMillis()); mStartTime = new MillisecondsTimePoint(getPickerTime().asMillis());
setSeconds();
} }
mListenerIgnore = false; mListenerIgnore = false;
} }
@@ -706,10 +645,26 @@ public class StartTimeFragment extends BaseFragment
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
if (!mListenerIgnore) { if (!mListenerIgnore) {
mStartTime = new MillisecondsTimePoint(getPickerTime().asMillis()); mStartTime = new MillisecondsTimePoint(getPickerTime().asMillis());
setSeconds();
} }
mListenerIgnore = false; mListenerIgnore = false;
} }
private void setSeconds() {
setSeconds(0, 0);
}
private void setSeconds(int sec, int msec) {
if (mStartSeconds != null) {
double seconds = sec + msec / (double) 1000;
DecimalFormat format = new DecimalFormat("0.000");
mStartSeconds.setDisplayedValues(new String[] { format.format(seconds) });
}
}
/**
* sets the time displayed in the time picker to the value of {@link #mStartTime}
*/
private void setPickerTime() { private void setPickerTime() {
Calendar today = Calendar.getInstance(); Calendar today = Calendar.getInstance();
Calendar newTime = (Calendar) today.clone(); Calendar newTime = (Calendar) today.clone();
@@ -724,6 +679,8 @@ public class StartTimeFragment extends BaseFragment
mTimePicker.setCurrentHour(newTime.get(Calendar.HOUR_OF_DAY)); mTimePicker.setCurrentHour(newTime.get(Calendar.HOUR_OF_DAY));
mTimePicker.setCurrentMinute(newTime.get(Calendar.MINUTE)); mTimePicker.setCurrentMinute(newTime.get(Calendar.MINUTE));
} }
setSeconds(newTime.get(Calendar.SECOND), newTime.get(Calendar.MILLISECOND));
} }
private TimePoint getPickerTime() { private TimePoint getPickerTime() {
@@ -9,6 +9,7 @@ import android.graphics.Paint;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.support.annotation.AttrRes; import android.support.annotation.AttrRes;
import android.support.annotation.ColorInt; import android.support.annotation.ColorInt;
import android.support.annotation.DimenRes;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.widget.EditText; import android.widget.EditText;
@@ -105,6 +106,21 @@ public class ThemeHelper {
setPickerDividerColor(context, numberPicker, dividerColor); setPickerDividerColor(context, numberPicker, dividerColor);
} }
public static void setPickerTextSize(Context context, NumberPicker numberPicker, @DimenRes int dimen) {
final int count = numberPicker.getChildCount();
for (int i = 0; i < count; i++) {
View child = numberPicker.getChildAt(i);
if (child instanceof EditText) {
try {
((EditText) child).setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(dimen));
numberPicker.invalidate();
} catch (IllegalArgumentException e) {
ExLog.w(context, TAG, "IllegalArgumentException - " + e.getMessage());
}
}
}
}
public static @ColorInt int getColor(Context context, @AttrRes int colorId) { public static @ColorInt int getColor(Context context, @AttrRes int colorId) {
int color = 0; int color = 0;
TypedValue typedValue = new TypedValue(); TypedValue typedValue = new TypedValue();