Merge branch 'master' into bug3529

This commit is contained in:
Steffen Schaefer committed 2016-12-14 16:38:48 +01:00
commit 756c6741e8
29 files changed
+690 -641

No files matched your search

@@ -9,7 +9,7 @@
<!-- Website header-->
<div class="{res.mediaCss.grid}">
<div class="{local_res.css.siteheader_logo} {res.mediaCss.small2} {res.mediaCss.medium2} {res.mediaCss.large3} {res.mediaCss.columns}">
<a href="http://sapsponsorships.com">
<a href="https://www.sap.com/sponsorships">
<img src="images/home/logo-small@2x.png" alt="{i18n.headerLogo}" class="{local_res.css.siteheader_logo_image}"></img>
<p class="{local_res.css.siteheader_logo_title}">
<ui:text from='{i18n.headerLogo}' />
@@ -18,7 +18,7 @@
<div class="{res.mediaCss.grid}">
<header class="{local_res.css.header} {res.mediaCss.small12} {res.mediaCss.columns}">
<div class="{local_res.css.header_logo}">
<a href="http://sapsponsorships.com" title="SAP sponsorships">
<a href="https://www.sap.com/sponsorships" title="SAP sponsorships">
<img src="images/mobile/logo-small@2x.png" alt="{i18n.headerLogo}" class="{local_res.css.header_logo_image}"></img>
</a>
</div>
@@ -38,7 +38,7 @@ public class LogoAndTitlePanel extends FlowPanel implements RequiresResize {
lastIsSmallWidth = windowSizeDetector.isSmallWidth();
sapLogo = new Anchor(new SafeHtmlBuilder().appendHtmlConstant(
"<img class=\"linkNoBorder\" src=\"/gwt/images/sap_66_transparent.png\"/>").toSafeHtml());
sapLogo.setHref("http://www.sap.com");
sapLogo.setHref("https://www.sap.com/sponsorships");
sapLogo.addStyleName("sapLogo");
this.add(sapLogo);
renderUI();
@@ -2770,7 +2770,7 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
sapLogo.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
Window.open("http://www.sap.com", "_blank", null);
Window.open("https://www.sap.com/sponsorships", "_blank", null);
}
});
sapLogo.setStyleName("raceBoard-Logo");
@@ -4,6 +4,11 @@
<div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sail InSight (Android)</h4>
<div class="innerContent">
<h5 class="articleSubheadline">November 2016</h5>
<ul class="bulletList">
<li>Improved tracking status and accuracy reporting</li>
</ul>
<h5 class="articleSubheadline">October 2016</h5>
<ul class="bulletList">
<li>Improved tracking reliability and accuracy,
@@ -57,4 +57,7 @@
<string name="footer_text">Mehr Informationen auf</string>
<string name="footer_text_link">https://www.sapsailing.com</string>
<string name="yesterday">Gestern</string>
<string name="today">Heute</string>
<string name="tomorrow">Morgen</string>
</resources>
@@ -63,4 +63,9 @@
<color name="normal_button">#f3f3f3</color>
<color name="normal_button_pressed">#bfbfbf</color>
<color name="signal_great">#8ab54e</color>
<color name="signal_good">@color/sap_yellow</color>
<color name="signal_poor">@color/sap_red</color>
<color name="signal_none">#455B27</color>
</resources>
@@ -53,4 +53,6 @@
<dimen name="highlight_text_size">21sp</dimen>
<dimen name="list_header_text_size">18sp</dimen>
<dimen name="default_padding_half">12dp</dimen>
</resources>
@@ -81,4 +81,10 @@
<string name="footer_text">More information on</string>
<string name="footer_text_link">https://www.sapsailing.com</string>
<string name="yesterday">Yesterday</string>
<string name="today">Today</string>
<string name="tomorrow">Tomorrow</string>
<string name="time_ago" translatable="false">%1$dh %2$02d\' %3$02d\"</string>
<string name="date_short" translatable="false">yyyy-MM-dd</string>
</resources>
@@ -0,0 +1,26 @@
package com.sap.sailing.android.shared.ui.customviews;
public enum GPSQuality {
noSignal(0), poor(2), good(3), great(4);
private final int mQuality;
GPSQuality(int quality) {
mQuality = quality;
}
public int toInt() {
return this.mQuality;
}
public static GPSQuality getValue(int value) {
GPSQuality result = null;
for (GPSQuality item : GPSQuality.values()) {
if (item.toInt() == value) {
result = item;
break;
}
}
return result;
}
}
@@ -3,7 +3,6 @@ package com.sap.sailing.android.shared.ui.customviews;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextUtils;
@@ -14,17 +13,19 @@ import android.view.accessibility.AccessibilityEvent;
import com.sap.sailing.android.shared.R;
/**
* Draws one vertical indicator that can is filled green up to a point, depending on the value passed for
* signal-quality;
* Draws one vertical indicator that is colored depending on the signal quality
*
* @author Lukas Zielinski
* @author Peter Siegmund
*/
public class SignalQualityIndicatorView extends View {
private int signalQuality;
private GPSQuality mSignalQuality;
private Paint paint;
private Paint paintDark;
private Paint paintNone;
private Paint paintPoor;
private Paint paintGood;
private Paint paintGreat;
private float height;
private float width;
@@ -35,7 +36,10 @@ public class SignalQualityIndicatorView extends View {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SignalQualityIndicatorView, 0, 0);
try {
signalQuality = a.getInteger(R.styleable.SignalQualityIndicatorView_signalQuality, 0);
mSignalQuality = GPSQuality.getValue(a.getInteger(R.styleable.SignalQualityIndicatorView_signalQuality, 0));
if (mSignalQuality == null) {
mSignalQuality = GPSQuality.noSignal;
}
} finally {
a.recycle();
}
@@ -44,52 +48,69 @@ public class SignalQualityIndicatorView extends View {
initPaint();
}
public Integer getSignalQuality() {
return signalQuality;
public int getSignalQuality() {
return mSignalQuality.toInt();
}
private void setAccessibilityString() {
if (this.signalQuality == 2) {
this.setContentDescription(getContext().getString(R.string.signal_accuracy_indicator_view_description)
+ getContext().getString(R.string.poor));
} else if (this.signalQuality == 3) {
this.setContentDescription(getContext().getString(R.string.signal_accuracy_indicator_view_description)
+ getContext().getString(R.string.good));
} else if (this.signalQuality == 4) {
this.setContentDescription(getContext().getString(R.string.signal_accuracy_indicator_view_description)
+ getContext().getString(R.string.great));
} else {
this.setContentDescription(getContext().getString(R.string.signal_accuracy_indicator_view_description)
+ getContext().getString(R.string.no_signal));
String desc = getContext().getString(R.string.signal_accuracy_indicator_view_description) + " ";
switch (mSignalQuality) {
case poor:
desc += getContext().getString(R.string.poor);
break;
case good:
desc += getContext().getString(R.string.good);
break;
case great:
desc += getContext().getString(R.string.great);
break;
default:
desc += getContext().getString(R.string.no_signal);
}
setContentDescription(desc);
}
private void initPaint() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.parseColor("#8ab54e"));
paintDark = new Paint(Paint.ANTI_ALIAS_FLAG);
paintDark.setColor(Color.parseColor("#455B27"));
paintNone = new Paint(Paint.ANTI_ALIAS_FLAG);
paintNone.setColor(getResources().getColor(R.color.signal_none));
paintPoor = new Paint(Paint.ANTI_ALIAS_FLAG);
paintPoor.setColor(getResources().getColor(R.color.signal_poor));
paintGood = new Paint(Paint.ANTI_ALIAS_FLAG);
paintGood.setColor(getResources().getColor(R.color.signal_good));
paintGreat = new Paint(Paint.ANTI_ALIAS_FLAG);
paintGreat.setColor(getResources().getColor(R.color.signal_great));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float boxHeight = this.height / 4;
// first draw gray background
Paint paint;
switch (mSignalQuality) {
case poor:
paint = paintPoor;
break;
case good:
paint = paintGood;
break;
case great:
paint = paintGreat;
break;
default:
paint = paintNone;
}
rect.top = 0;
rect.left = 0;
rect.right = (int) this.width;
rect.bottom = (int) this.height;
canvas.drawRect(rect, paintDark);
// then draw green box on top
rect.top = (int) (this.height - (signalQuality * boxHeight));
rect.left = 0;
rect.right = (int) this.width;
rect.bottom = (int) this.height;
canvas.drawRect(rect, paint);
}
@@ -116,24 +137,17 @@ public class SignalQualityIndicatorView extends View {
/**
* Must be 0,2,3 or 4, otherwise 1 will be set
*
* @param signalQuality
* @param signalQuality the new signal quality
*/
public void setSignalQuality(Integer signalQuality) {
Integer previousSingalQuality = this.signalQuality;
public void setSignalQuality(GPSQuality signalQuality) {
GPSQuality previousSignalQuality = mSignalQuality;
if (signalQuality == 0) {
this.signalQuality = 0;
} else if (signalQuality == 2) {
this.signalQuality = 2;
} else if (signalQuality == 3) {
this.signalQuality = 3;
} else if (signalQuality == 4) {
this.signalQuality = 4;
} else {
this.signalQuality = 0;
mSignalQuality = signalQuality;
if (mSignalQuality == null) {
mSignalQuality = GPSQuality.noSignal;
}
if (this.signalQuality != previousSingalQuality) {
if (mSignalQuality != previousSignalQuality) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
}
@@ -155,13 +169,24 @@ public class SignalQualityIndicatorView extends View {
* generate some accessibility info
*/
public String getAccessibilityText() {
if (this.signalQuality == 2)
return "poor signal quality";
if (this.signalQuality == 3)
return "good signal quality";
if (this.signalQuality == 4)
return "great signal quality";
return "no signal";
String result;
switch (mSignalQuality) {
case poor:
result = "poor signal quality";
break;
case good:
result = "good signal quality";
break;
case great:
result = "great signal quality";
break;
default:
result = "no signal";
}
return result;
}
@Override
@@ -0,0 +1,230 @@
package com.sap.sailing.android.shared.util;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import com.sap.sailing.android.shared.R;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import android.content.Context;
import android.os.Build;
import android.support.annotation.Nullable;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.NumberPicker;
import android.widget.TextView;
import android.widget.TimePicker;
public class TimeUtils {
protected TimeUtils() {
// only static methods
}
/**
* 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);
}
/**
* Formats milliseconds to a string like: 01h 23'45"
*/
public static String formatTimeAgo(Context context, long milliseconds) {
Calendar time = Calendar.getInstance();
time.setTimeZone(TimeZone.getTimeZone("UTC"));
time.setTimeInMillis(milliseconds);
return context.getString(R.string.time_ago, time.get(Calendar.HOUR_OF_DAY), time.get(Calendar.MINUTE), time.get(Calendar.SECOND));
}
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<String> 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();
}
}
public static ArrayList<String> getDates(Context context, TimePoint start, TimePoint end) {
ArrayList<String> dates = new ArrayList<>();
SimpleDateFormat simpleFormat = new SimpleDateFormat(context.getString(R.string.date_short), Locale.US);
Calendar startDate = Calendar.getInstance();
Calendar endDate = (Calendar) startDate.clone();
startDate.setTime(start.asDate());
endDate.setTime(end.asDate());
int dayDiff = daysBetween(endDate, startDate);
for (int i = 0; i <= dayDiff; i++) {
dates.add(simpleFormat.format(startDate.getTime()));
startDate.add(Calendar.DAY_OF_MONTH, 1);
}
return dates;
}
public static TimePoint getTime(TimePicker timePicker) {
return getTime(timePicker, null);
}
public static TimePoint getTime(TimePicker timePicker, @Nullable NumberPicker secondPicker) {
Calendar time = Calendar.getInstance();
time.set(Calendar.HOUR_OF_DAY, timePicker.getCurrentHour());
time.set(Calendar.MINUTE, timePicker.getCurrentMinute());
time.set(Calendar.SECOND, secondPicker == null ? 0 : secondPicker.getValue());
time.set(Calendar.MILLISECOND, 0);
return new MillisecondsTimePoint(time.getTime());
}
}
@@ -14,7 +14,7 @@ public class UniqueDeviceUuid {
private static String mDeviceId;
@SuppressLint("CommitPrefEdits")
@SuppressLint("ApplySharedPref")
public synchronized static String getUniqueId(Context context) {
if (mDeviceId == null) {
SharedPreferences preferences = context.getSharedPreferences(FILENAME, Context.MODE_PRIVATE);
@@ -128,15 +128,6 @@
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
<receiver
android:name=".receiver.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<category android:name="com.sap.sailing.android.tracking.app"/>
</intent-filter>
</receiver>
<receiver android:name=".receiver.PowerConnectionReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
@@ -301,7 +301,6 @@
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:padding="0dp"
android:text="@string/days"
android:singleLine="true"
android:textColor="@color/white"/>
</LinearLayout>
@@ -332,7 +331,6 @@
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:padding="0dp"
android:text="@string/hours"
android:singleLine="true"
android:textColor="@color/white"/>
</LinearLayout>
@@ -363,7 +361,6 @@
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:padding="0dp"
android:text="@string/minutes"
android:singleLine="true"
android:textColor="@color/white"/>
</LinearLayout>
@@ -305,7 +305,6 @@
android:layout_height="wrap_content"
android:padding="0dp"
android:singleLine="true"
android:text="@string/days"
android:textColor="@color/fiori_text_color"
android:textSize="@dimen/default_text_size"/>
@@ -343,7 +342,6 @@
android:layout_height="wrap_content"
android:padding="0dp"
android:singleLine="true"
android:text="@string/hours"
android:textColor="@color/fiori_text_color"
android:textSize="@dimen/default_text_size"/>
</LinearLayout>
@@ -380,7 +378,6 @@
android:layout_height="wrap_content"
android:padding="0dp"
android:singleLine="true"
android:text="@string/minutes"
android:textColor="@color/fiori_text_color"
android:textSize="@dimen/default_text_size"/>
</LinearLayout>
@@ -22,8 +22,8 @@
<string name="please_confirm">Bitte bestätigen</string>
<string name="do_you_really_want_to_stop_tracking">Soll das Tracking wirklich beendet werden?</string>
<string name="tracking_status_tracking">Tracking läuft</string>
<string name="tracking_status_no_gps_signal">Kein GPS Signal!</string>
<string name="tracking_mode_offline">Offline</string>
<string name="tracking_status_no_gps_signal">Keine GPS Änderungen oder Signal!</string>
<string name="tracking_mode_no_attempt">Initialisierung</string>
<string name="tracking_mode">Modus</string>
<string name="tracking_mode_live">Live</string>
<string name="tracking_mode_battery_saving">Energie sparen</string>
@@ -44,12 +44,18 @@
<string name="error_could_not_complete_operation_on_server_try_again">Konnte Operation nicht durchführen. Bitte versuchen Sie es noch einmal.</string>
<string name="error_sending_team_image_to_server">Fehler: Konnte Team-Foto nicht an den server senden.</string>
<string name="regatta_starts_in">Regatta beginnt in:</string>
<string name="hours">Stunden</string>
<string name="minute">Minute</string>
<string name="minutes">Minuten</string>
<string name="days">Tage</string>
<string name="hour">Stunde</string>
<string name="day">Tag</string>
<plurals name="hour">
<item quantity="one">Stunde</item>
<item quantity="other">Stunden</item>
</plurals>
<plurals name="minute">
<item quantity="one">Minute</item>
<item quantity="other">Minuten</item>
</plurals>
<plurals name="day">
<item quantity="one">Tag</item>
<item quantity="other">Tage</item>
</plurals>
<string name="please_listen_to_announcements">Bitte achten Sie auf die Durchsagen</string>
<string name="no_qr_code">Sie haben keinen QR-Code?</string>
<string name="scan_qr">QR-Code scannen</string>
@@ -35,12 +35,18 @@
<string name="error_could_not_complete_operation_on_server_try_again">Could not complete operation on server. Please try again.</string>
<string name="error_sending_team_image_to_server">Error: Could not send team image to server.</string>
<string name="regatta_starts_in">Regatta starts in:</string>
<string name="hours">Hours</string>
<string name="minutes">Minutes</string>
<string name="days">Days</string>
<string name="hour">Hour</string>
<string name="minute">Minute</string>
<string name="day">Day</string>
<plurals name="hour">
<item quantity="one">Hour</item>
<item quantity="other">Hours</item>
</plurals>
<plurals name="minute">
<item quantity="one">Minute</item>
<item quantity="other">Minutes</item>
</plurals>
<plurals name="day">
<item quantity="one">Day</item>
<item quantity="other">Days</item>
</plurals>
<string name="please_listen_to_announcements">Please listen to announcements</string>
<string name="close">Close</string>
<string name="thank_you_you_successfully_participated">Thank you! You have successfully taken part in the regatta.\n\nYour SAP Sailing Team</string>
@@ -12,11 +12,11 @@
<string name="tracking_unsent_fixes">Unsent Messages</string>
<string name="tracking_mode_caching">Offline</string>
<string name="tracking_mode_api_error">API-Error</string>
<string name="tracking_mode_offline">Offline</string>
<string name="tracking_mode_no_attempt">Initialise</string>
<string name="tracking_time">Tracking Time</string>
<string name="tracking_status" translatable="false">Status</string>
<string name="tracking_status_tracking">Tracking</string>
<string name="tracking_status_no_gps_signal">No GPS Signal!</string>
<string name="tracking_status_no_gps_signal">No GPS Changes or Signal!</string>
<string name="desc_regatta_user_image">Regatta User Image</string>
<string name="desc_add_photo_button">Add Photo Button</string>
<string name="tracking_status_gps_found">GPS signal found!</string>
@@ -1,5 +1,7 @@
package com.sap.sailing.android.tracking.app.services;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -10,10 +12,33 @@ import java.util.UUID;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONObject;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.hardware.GeomagneticField;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessageSendingService;
import com.sap.sailing.android.shared.ui.customviews.GPSQuality;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.ui.activities.TrackingActivity;
@@ -25,26 +50,19 @@ import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.MeterPerSecondSpeedImpl;
import com.sap.sailing.domain.common.tracking.impl.FlatSmartphoneUuidAndGPSFixMovingJsonSerializer;
import com.sap.sse.common.Duration;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.hardware.GeomagneticField;
import android.location.Location;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
public class TrackingService extends Service implements LocationListener {
public class TrackingService extends Service implements android.location.LocationListener {
private static final String TAG = TrackingService.class.getName();
private static final String THREAD_NAME = "Location WatchDog";
private static final int NO_LOCATION = 0;
private static final long NO_LOCATION_CHECK = Duration.ONE_SECOND.times(5).asMillis();
private static final int POOR_DISTANCE = 48;
private static final int GREAT_DISTANCE = 10;
private static final int NO_DISTANCE = 0;
private NotificationManager notificationManager;
private AppPreferences prefs;
@@ -52,8 +70,6 @@ public class TrackingService extends Service implements android.location.Locati
private GPSQualityListener gpsQualityListener;
private final IBinder trackingBinder = new TrackingBinder();
private static final String TAG = TrackingService.class.getName();
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private int NOTIFICATION_ID = R.string.tracker_started;
@@ -61,12 +77,12 @@ public class TrackingService extends Service implements android.location.Locati
public final static int UPDATE_INTERVAL_IN_MILLIS_DEFAULT = 1000;
public final static String GPS_DISABLED_MESSAGE = "gpsDisabled";
private float minLocationUpdateDistanceInMeters = 0f;
private boolean initialLocation;
private String checkinDigest;
private EventInfo event;
protected LocationManager locationManager;
private LocationManager locationManager;
private LocationWatchDog locationWatchDog;
/**
* Must be synchronized upon while modifying the {@link #timerForDelayingSendingMessages} field
@@ -79,7 +95,7 @@ public class TrackingService extends Service implements android.location.Locati
* {@link #locationsQueuedBasedOnSendingInterval} after the send interval, and the next message sending intent
* arriving can be forwarded to the sending service immediately, with the timer being started immediately afterwards
* to delay the sending of messages arriving later until the resend interval has expired. If not {@code null},
* messages that arrive in {@link #enqueueForSending(String, JSONObject)} will be appended to
* messages that arrive in {@link #enqueueForSending(String, Location)} will be appended to
* {@link #locationsQueuedBasedOnSendingInterval}.
* <p>
*
@@ -111,12 +127,8 @@ public class TrackingService extends Service implements android.location.Locati
locationsQueuedBasedOnSendingInterval = new LinkedHashMap<>();
prefs = new AppPreferences(this);
initialLocation = true;
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
}
@Override
@@ -159,6 +171,18 @@ public class TrackingService extends Service implements android.location.Locati
prefs.setTrackerIsTracking(true);
prefs.setTrackerIsTrackingCheckinDigest(checkinDigest);
if (locationWatchDog == null) {
// create new Location WatchDog
HandlerThread thread = new HandlerThread(THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
thread.start();
locationWatchDog = new LocationWatchDog(thread.getLooper());
// start WatchDog
Message msg = locationWatchDog.obtainMessage(NO_LOCATION);
msg.obj = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
locationWatchDog.sendMessageDelayed(msg, NO_LOCATION_CHECK);
}
}
private void stopTracking() {
@@ -167,6 +191,12 @@ public class TrackingService extends Service implements android.location.Locati
prefs.setTrackerIsTracking(false);
prefs.setTrackerIsTrackingCheckinDigest(null);
if (locationWatchDog != null) {
// stop the Location WatchDog
locationWatchDog.getLooper().quit();
locationWatchDog = null;
}
stopSelf();
ExLog.i(this, TAG, "Stopped Tracking");
}
@@ -190,13 +220,15 @@ public class TrackingService extends Service implements android.location.Locati
bearingImpl.add(new DegreeBearingImpl(- geomagneticField.getDeclination()));
}
GPSQuality quality = GPSQuality.noSignal;
if (gpsQualityListener != null) {
if (gpsAccuracy > 48) {
GPSQuality quality = GPSQuality.noSignal;
if (gpsAccuracy <= NO_DISTANCE) {
quality = GPSQuality.noSignal;
} else if (gpsAccuracy > POOR_DISTANCE) {
quality = GPSQuality.poor;
} else if (gpsAccuracy > 10) {
} else if (gpsAccuracy > GREAT_DISTANCE) {
quality = GPSQuality.good;
} else if (gpsAccuracy <= 10) {
} else if (gpsAccuracy <= GREAT_DISTANCE) {
quality = GPSQuality.great;
}
@@ -204,7 +236,6 @@ public class TrackingService extends Service implements android.location.Locati
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private JSONObject createJsonLocationFix(Location location) throws JSONException {
JSONObject fixJson = new JSONObject();
fixJson.put(FlatSmartphoneUuidAndGPSFixMovingJsonSerializer.BEARING_DEG, location.getBearing());
@@ -214,7 +245,7 @@ public class TrackingService extends Service implements android.location.Locati
fixJson.put(FlatSmartphoneUuidAndGPSFixMovingJsonSerializer.LAT_DEG, location.getLatitude());
return fixJson;
}
private JSONArray createJsonLocationFixes(Iterable<Location> locations) throws JSONException {
final JSONArray fixesAsJson = new JSONArray();
for (final Location location : locations) {
@@ -302,16 +333,7 @@ public class TrackingService extends Service implements android.location.Locati
}
};
}
private void storeInitialTrackingTimestamp() {
if (prefs.getTrackingTimerStarted() == 0) {
prefs.setTrackingTimerStarted(System.currentTimeMillis());
}
initialLocation = false;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
@Override
public IBinder onBind(Intent intent) {
return trackingBinder;
@@ -329,13 +351,18 @@ public class TrackingService extends Service implements android.location.Locati
*/
@Override
public void onLocationChanged(Location location) {
if (initialLocation) {
storeInitialTrackingTimestamp();
}
reportGPSQualityBearingAndSpeed(location.getAccuracy(), location.getBearing(), location.getSpeed(),
location.getLatitude(), location.getLongitude(), location.getAltitude());
final String postUrlStr = event.server + prefs.getServerGpsFixesPostPath();
enqueueForSending(postUrlStr, location);
// clear message queue
locationWatchDog.removeMessages(NO_LOCATION);
// add new message with last location
Message msg = locationWatchDog.obtainMessage(NO_LOCATION);
msg.obj = location;
locationWatchDog.sendMessageDelayed(msg, NO_LOCATION_CHECK);
}
@Override
@@ -357,16 +384,17 @@ public class TrackingService extends Service implements android.location.Locati
}
private void showNotification() {
Intent intent = new Intent(this, TrackingActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle(getText(R.string.app_name))
.setContentText(getString(R.string.tracking_notification_text, event.name)).setContentIntent(pi)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setSmallIcon(R.drawable.ic_directions_boat)
.setOngoing(true).build();
Intent intent = new Intent(this, TrackingActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle(getText(R.string.app_name))
.setContentText(getString(R.string.tracking_notification_text, event.name))
.setContentIntent(pi)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setSmallIcon(R.drawable.ic_directions_boat)
.setOngoing(true)
.build();
notificationManager.notify(NOTIFICATION_ID, notification);
}
@@ -384,23 +412,32 @@ public class TrackingService extends Service implements android.location.Locati
}
}
public enum GPSQuality {
noSignal(0), poor(2), good(3), great(4);
private final int gpsQuality;
GPSQuality(int quality) {
this.gpsQuality = quality;
}
public int toInt() {
return this.gpsQuality;
}
}
public interface GPSQualityListener {
void gpsQualityAndAccuracyUpdated(GPSQuality quality, float gpsAccurracy, Bearing gpsBearing,
Speed gpsSpeed);
void gpsQualityAndAccuracyUpdated(GPSQuality quality, float accuracy, Bearing bearing, Speed speed);
}
private class LocationWatchDog extends Handler {
LocationWatchDog(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NO_LOCATION:
ExLog.i(getApplicationContext(), TAG, "No Location");
Location location = (Location) msg.obj;
if (location == null) {
location = new Location(LocationManager.GPS_PROVIDER);
}
reportGPSQualityBearingAndSpeed(NO_DISTANCE, location.getBearing(), location.getSpeed(),
location.getLatitude(), location.getLongitude(), location.getAltitude());
break;
default:
super.handleMessage(msg);
}
}
}
}
@@ -17,7 +17,6 @@ import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.sap.sailing.android.shared.data.LeaderboardInfo;
import com.sap.sailing.android.shared.logging.ExLog;
@@ -25,10 +24,10 @@ import com.sap.sailing.android.shared.services.sending.MessageSendingService;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.APIConnectivity;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.APIConnectivityListener;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.MessageSendingBinder;
import com.sap.sailing.android.shared.ui.customviews.GPSQuality;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.services.TrackingService;
import com.sap.sailing.android.tracking.app.services.TrackingService.GPSQuality;
import com.sap.sailing.android.tracking.app.services.TrackingService.GPSQualityListener;
import com.sap.sailing.android.tracking.app.services.TrackingService.TrackingBinder;
import com.sap.sailing.android.tracking.app.ui.fragments.CompassFragment;
@@ -45,18 +44,17 @@ import com.viewpagerindicator.CirclePageIndicator;
public class TrackingActivity extends BaseActivity implements GPSQualityListener, APIConnectivityListener {
TrackingService trackingService;
private TrackingService trackingService;
private boolean trackingServiceBound;
private boolean gpsReceived;
MessageSendingService messageSendingService;
private MessageSendingService messageSendingService;
boolean messageSendingServiceBound;
private final static String TAG = TrackingActivity.class.getName();
private final static String SIS_TRACKING_FRAGMENT = "savedInstanceTrackingFragment";
private final static String SIS_LAST_VIEWPAGER_ITEM = "instanceStateLastViewPagerItem";
private final static String SIS_LAST_SPEED_TEXT = "instanceStateLastSpeedText";
private final static String SIS_LAST_COMPASS_TEXT = "instanceStateLastCompassText";
private final static String SIS_TRACKING_FRAGMENT = "trackingFragment";
private final static String SIS_LAST_VIEWPAGER_ITEM = "lastViewPagerItem";
private final static String SIS_LAST_SPEED_TEXT = "lastSpeedText";
private final static String SIS_LAST_COMPASS_TEXT = "lastCompassText";
private ViewPager mPager;
private ScreenSlidePagerAdapter mPagerAdapter;
@@ -65,7 +63,6 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
private String checkinDigest;
private TrackingFragment trackingFragment;
private TimerRunnable timer;
private int lastViewPagerItem;
@@ -217,7 +214,6 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
@Override
protected void onPause() {
super.onPause();
timer.stop();
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(null);
@@ -227,39 +223,38 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
protected void onResume() {
super.onResume();
timer = new TimerRunnable();
Thread thread = new Thread(timer);
thread.start();
if (mPagerAdapter == null) {
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
}
mPager.setAdapter(mPagerAdapter);
mPager.setCurrentItem(lastViewPagerItem);
}
@Override
public void gpsQualityAndAccuracyUpdated(GPSQuality quality, float gpsAccurracy, Bearing bearing, Speed speed) {
if (trackingFragment.isAdded()) {
trackingFragment.setGPSQualityAndAcurracy(quality, gpsAccurracy);
}
public void gpsQualityAndAccuracyUpdated(final GPSQuality quality, final float accuracy, final Bearing bearing, final Speed speed) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (trackingFragment.isAdded()) {
trackingFragment.setGPSQualityAndAccuracy(quality, accuracy);
}
ScreenSlidePagerAdapter viewPagerAdapter = getViewPagerAdapter();
ScreenSlidePagerAdapter viewPagerAdapter = getViewPagerAdapter();
if (viewPagerAdapter != null) {
SpeedFragment speedFragment = viewPagerAdapter.getSpeedFragment();
if (speedFragment != null && speedFragment.isAdded()) {
speedFragment.setSpeed(speed);
if (viewPagerAdapter != null) {
SpeedFragment speedFragment = viewPagerAdapter.getSpeedFragment();
if (speedFragment != null && speedFragment.isAdded()) {
speedFragment.setSpeed(speed);
}
CompassFragment compassFragment = viewPagerAdapter.getCompassFragment();
if (compassFragment != null && compassFragment.isAdded()) {
compassFragment.setBearing(bearing);
}
}
}
CompassFragment compassFragment = viewPagerAdapter.getCompassFragment();
if (compassFragment != null && compassFragment.isAdded()) {
compassFragment.setBearing(bearing);
}
}
});
}
@Override
@@ -401,68 +396,4 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
ServiceHelper.getInstance().stopTrackingService(this);
finish();
}
/**
* Update UI with a string containing the time since tracking started, e.g. 01:22:45
*/
public void updateTimer() {
long trackingTimerStarted = prefs.getTrackingTimerStarted();
if (trackingTimerStarted > 0) {
hideWaitForGPSText();
long diff = System.currentTimeMillis() - trackingTimerStarted;
TextView textView = (TextView) findViewById(R.id.tracking_time_label);
if (textView != null) {
textView.setText(getTimeFormatString(diff));
}
}
}
private void hideWaitForGPSText() {
if (!gpsReceived) {
View waitForGPSLabel = findViewById(R.id.wait_for_gps);
if (waitForGPSLabel != null) {
waitForGPSLabel.setVisibility(View.GONE);
gpsReceived = true;
}
}
}
private String getTimeFormatString(long milliseconds) {
int seconds = (int) (milliseconds / 1000) % 60;
int minutes = (int) ((milliseconds / (1000 * 60)) % 60);
int hours = (int) ((milliseconds / (1000 * 60 * 60)) % 24);
return String.format(getResources().getConfiguration().locale, "%02d:%02d:%02d", hours, minutes, seconds);
}
private class TimerRunnable implements Runnable {
private boolean running;
TimerRunnable() {
running = true;
}
@Override
public void run() {
while (running) {
runOnUiThread(new Runnable() {
@Override
public void run() {
trackingFragment.checkLastGPSReceived();
updateTimer();
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
ExLog.e(TrackingActivity.this, TAG, "Interrupted sleep");
}
}
}
public void stop() {
running = false;
}
}
}
@@ -37,6 +37,7 @@ import com.sap.sailing.android.tracking.app.ui.activities.RegattaActivity;
import com.sap.sailing.android.tracking.app.ui.activities.TrackingActivity;
import com.sap.sailing.android.tracking.app.utils.AppPreferences;
import com.sap.sailing.android.tracking.app.valueobjects.EventInfo;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class RegattaFragment extends BaseFragment implements OnClickListener {
@@ -56,6 +57,7 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_regatta, container, false);
Button startTrackingButton = (Button) view.findViewById(R.id.start_tracking);
@@ -333,6 +335,7 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
}
private void startTrackingActivity() {
prefs.setTrackingTimerStarted(MillisecondsTimePoint.now().asMillis());
RegattaActivity regattaActivity = (RegattaActivity) getActivity();
Intent intent = new Intent(getActivity(), TrackingActivity.class);
String checkinDigest = regattaActivity.event.checkinDigest;
@@ -370,27 +373,13 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
TextView hoursTextViewLabel = (TextView) getActivity().findViewById(R.id.starts_in_hours_label);
TextView minutesTextViewLabel = (TextView) getActivity().findViewById(R.id.starts_in_minutes_label);
daysTextViewLabel.setText(getResources().getQuantityText(R.plurals.day, days));
hoursTextViewLabel.setText(getResources().getQuantityText(R.plurals.hour, hours));
minutesTextViewLabel.setText(getResources().getQuantityText(R.plurals.minute, minutes));
daysTextView.setText(String.format("%02d", days));
hoursTextView.setText(String.format("%02d", hours));
minutesTextView.setText(String.format("%02d", minutes));
if (days == 1) {
daysTextViewLabel.setText(R.string.day);
} else {
daysTextViewLabel.setText(R.string.days);
}
if (hours == 1) {
hoursTextViewLabel.setText(R.string.hour);
} else {
hoursTextViewLabel.setText(R.string.hours);
}
if (minutes == 1) {
minutesTextViewLabel.setText(R.string.minute);
} else {
minutesTextViewLabel.setText(R.string.minutes);
}
}
public void setFragmentWatcher(FragmentWatcher fWatcher) {
@@ -1,11 +1,11 @@
package com.sap.sailing.android.tracking.app.ui.fragments;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
@@ -14,40 +14,51 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.APIConnectivity;
import com.sap.sailing.android.shared.ui.customviews.GPSQuality;
import com.sap.sailing.android.shared.ui.customviews.SignalQualityIndicatorView;
import com.sap.sailing.android.shared.util.LocationHelper;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.services.TrackingService;
import com.sap.sailing.android.tracking.app.services.TrackingService.GPSQuality;
import com.sap.sailing.android.tracking.app.ui.activities.TrackingActivity;
import com.sap.sailing.android.tracking.app.utils.AppPreferences;
public class TrackingFragment extends BaseFragment {
static final String SIS_MODE = "instanceStateMode";
static final String SIS_STATUS = "instanceStateStatus";
static final String SIS_GPS_QUALITY = "instanceStateGpsQuality";
static final String SIS_GPS_ACCURACY = "instanceStateGpsAccuracy";
static final String SIS_GPS_UNSENT_FIXES = "instanceStateGpsUnsentFixes";
static final String SIS_MODE = "mode";
static final String SIS_STATUS = "status";
static final String SIS_GPS_ACCURACY = "gpsAccuracy";
static final String SIS_GPS_UNSENT_FIXES = "gpsUnsentFixes";
private long lastGPSQualityUpdate;
private BroadcastReceiver gpsDisabledReceiver;
private Toast gpsToast;
private boolean gpsFound = true;
private String TAG = TrackingFragment.class.getName();
private TextView modeText;
private TextView statusText;
private TextView accuracyText;
private TextView unsentFixesText;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
ViewGroup view = (ViewGroup) inflater.inflate(R.layout.fragment_tracking, container, false);
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.fragment_tracking, container, false);
prefs = new AppPreferences(getActivity());
return view;
modeText = (TextView) layout.findViewById(R.id.mode);
statusText = (TextView) layout.findViewById(R.id.tracking_status);
accuracyText = (TextView) layout.findViewById(R.id.gps_accuracy_label);
unsentFixesText = (TextView) layout.findViewById(R.id.tracking_unsent_fixes);
if (savedInstanceState != null) {
modeText.setText(savedInstanceState.getString(SIS_MODE));
statusText.setText(savedInstanceState.getString(SIS_STATUS));
accuracyText.setText(savedInstanceState.getString(SIS_GPS_ACCURACY));
unsentFixesText.setText(savedInstanceState.getString(SIS_GPS_UNSENT_FIXES));
} else {
//initially set quality to "No GPS" on start tracking
updateTrackingStatus(GPSQuality.noSignal);
}
return layout;
}
@Override
@@ -75,62 +86,20 @@ public class TrackingFragment extends BaseFragment {
@Override
public void onStop() {
super.onStop();
getActivity().unregisterReceiver(gpsDisabledReceiver);
};
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
TextView modeText = (TextView) getActivity().findViewById(R.id.mode);
TextView statusText = (TextView) getActivity().findViewById(R.id.tracking_status);
SignalQualityIndicatorView qualityIndicator = (SignalQualityIndicatorView) getActivity().findViewById(
R.id.gps_quality_indicator);
TextView accuracyText = (TextView) getActivity().findViewById(R.id.gps_accuracy_label);
TextView unsentFixesText = (TextView) getActivity().findViewById(R.id.tracking_unsent_fixes);
modeText.setText(savedInstanceState.getString(SIS_MODE));
statusText.setText(savedInstanceState.getString(SIS_STATUS));
qualityIndicator.setSignalQuality(savedInstanceState.getInt(SIS_GPS_QUALITY));
accuracyText.setText(savedInstanceState.getString(SIS_GPS_ACCURACY));
unsentFixesText.setText(savedInstanceState.getString(SIS_GPS_UNSENT_FIXES));
} else {
//initially set quality to "No GPS" on start tracking
updateTrackingStatus(GPSQuality.noSignal);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
TextView modeText = (TextView) getActivity().findViewById(R.id.mode);
TextView statusText = (TextView) getActivity().findViewById(R.id.tracking_status);
SignalQualityIndicatorView qualityIndicator = (SignalQualityIndicatorView) getActivity().findViewById(
R.id.gps_quality_indicator);
TextView accuracyText = (TextView) getActivity().findViewById(R.id.gps_accuracy_label);
TextView unsentFixesText = (TextView) getActivity().findViewById(R.id.tracking_unsent_fixes);
outState.putString(SIS_MODE, modeText.getText().toString());
outState.putString(SIS_STATUS, statusText.getText().toString());
outState.putInt(SIS_GPS_QUALITY, qualityIndicator.getSignalQuality());
outState.putString(SIS_GPS_ACCURACY, accuracyText.getText().toString());
outState.putString(SIS_GPS_UNSENT_FIXES, unsentFixesText.getText().toString());
}
/**
* If last GPS update is too long ago, let's assume there's no signal and set quality to .noSignal
*/
public void checkLastGPSReceived() {
if (System.currentTimeMillis() - lastGPSQualityUpdate > 3000 && !isLocationEnabled(getActivity())) {
if (BuildConfig.DEBUG) {
ExLog.i(getActivity(), TAG,
"Setting GPS Quality to 0 because timeout occurred and location is reported as disabled.");
}
setGPSQualityAndAcurracy(GPSQuality.noSignal, 0);
}
}
/**
* Update tracking status string in UI
*
@@ -138,55 +107,44 @@ public class TrackingFragment extends BaseFragment {
*/
public void updateTrackingStatus(GPSQuality quality) {
if (isAdded()) {
TextView textView = (TextView) getActivity().findViewById(R.id.tracking_status);
if (quality == GPSQuality.noSignal) {
textView.setText(getString(R.string.tracking_status_no_gps_signal));
textView.setTextColor(getResources().getColor(R.color.sap_red));
//either GPS has been found before or tracking just started (but is not available now) --> show noGPS Toast
if (gpsFound || gpsToast == null) {
gpsToast = Toast.makeText(getActivity(),getString(R.string.tracking_status_no_gps_signal), Toast.LENGTH_LONG);
gpsToast.show();
gpsFound = false;
}
statusText.setText(getString(R.string.tracking_status_no_gps_signal));
statusText.setTextColor(getResources().getColor(R.color.sap_red));
} else {
textView.setText(getString(R.string.tracking_status_tracking));
textView.setTextColor(getResources().getColor(R.color.fiori_text_color));
if (gpsToast == null) {
gpsToast.cancel();
}
//GPS was lost before but is available again now --> show gpsFound toast
if (!gpsFound) {
gpsToast = Toast.makeText(getActivity(),getString(R.string.tracking_status_gps_found), Toast.LENGTH_SHORT);
gpsToast.show();
gpsFound = true;
}
statusText.setText(getString(R.string.tracking_status_tracking));
statusText.setTextColor(getResources().getColor(R.color.fiori_text_color));
}
}
}
/**
* Update UI and tell user if app is caching or sending fixes to api
*
* @param apiConnectivity
*/
public void setAPIConnectivityStatus(final APIConnectivity apiConnectivity) {
if (isAdded()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
TextView textView = (TextView) getActivity().findViewById(R.id.mode);
if (apiConnectivity == APIConnectivity.transmissionSuccess) {
textView.setText(getString(R.string.tracking_mode_live));
textView.setTextColor(getResources().getColor(R.color.fiori_text_color));
} else if (apiConnectivity == APIConnectivity.noAttempt) {
textView.setText(getString(R.string.tracking_mode_offline));
textView.setTextColor(getResources().getColor(R.color.fiori_text_color));
} else if (apiConnectivity == APIConnectivity.transmissionError) {
textView.setText(getString(R.string.tracking_mode_api_error));
textView.setTextColor(getResources().getColor(R.color.sap_red));
} else {
textView.setText(getString(R.string.tracking_mode_caching));
textView.setTextColor(getResources().getColor(R.color.fiori_text_color));
switch (apiConnectivity) {
case transmissionSuccess:
modeText.setText(getString(R.string.tracking_mode_live));
modeText.setTextColor(getResources().getColor(R.color.fiori_text_color));
break;
case noAttempt:
modeText.setText(getString(R.string.tracking_mode_no_attempt));
modeText.setTextColor(getResources().getColor(R.color.fiori_text_color));
break;
case transmissionError:
modeText.setText(getString(R.string.tracking_mode_api_error));
modeText.setTextColor(getResources().getColor(R.color.sap_red));
break;
default:
modeText.setText(getString(R.string.tracking_mode_caching));
modeText.setTextColor(getResources().getColor(R.color.fiori_text_color));
}
}
});
@@ -209,7 +167,6 @@ public class TrackingFragment extends BaseFragment {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
} catch (SettingNotFoundException e) {
e.printStackTrace();
}
@@ -219,7 +176,7 @@ public class TrackingFragment extends BaseFragment {
} else {
locationProviders = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return locationProviders.contains("gps");
return locationProviders.contains(LocationManager.GPS_PROVIDER);
}
} else {
return false;
@@ -231,15 +188,19 @@ public class TrackingFragment extends BaseFragment {
activity.showStopTrackingConfirmationDialog();
}
public void setGPSQualityAndAcurracy(GPSQuality quality, float gpsAccurracy) {
public void setGPSQualityAndAccuracy(GPSQuality quality, float accuracy) {
if (isAdded()) {
Activity activity = getActivity();
SignalQualityIndicatorView indicatorView = (SignalQualityIndicatorView) activity.findViewById(R.id.gps_quality_indicator);
indicatorView.setSignalQuality(quality.toInt());
TextView accuracyTextView = (TextView) getActivity().findViewById(R.id.gps_accuracy_label);
accuracyTextView.setText("~ " + String.valueOf(Math.round(gpsAccurracy)) + " m");
updateTrackingStatus(quality);
lastGPSQualityUpdate = System.currentTimeMillis();
View layout = getView();
if (layout != null) {
SignalQualityIndicatorView indicatorView = (SignalQualityIndicatorView) layout.findViewById(R.id.gps_quality_indicator);
indicatorView.setSignalQuality(quality);
updateTrackingStatus(quality);
if (quality != GPSQuality.noSignal) {
accuracyText.setText("~ " + Math.round(accuracy) + " m");
} else {
accuracyText.setText(null);
}
}
}
}
@@ -1,51 +1,120 @@
package com.sap.sailing.android.tracking.app.ui.fragments;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.sap.sailing.android.shared.util.LocationHelper;
import com.sap.sailing.android.shared.util.TimeUtils;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.utils.AppPreferences;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class TrackingTimeFragment extends BaseFragment {
private final static String SIS_TRACKING_TIMER = "savedInstanceTrackingTimer";
private static final String SIS_TRACKING_TIMER = "trackingTimer";
private static final String THREAD_NAME = "Tracking Timer";
private static final int TIMER_SET = 0;
private static final long TIMER_DELAY = Duration.ONE_SECOND.asMillis();
private TextView timerView;
private TextView noGPSView;
private Handler handler;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
ViewGroup view = (ViewGroup) inflater.inflate(R.layout.fragment_time, container, false);
timerView = (TextView) view.findViewById(R.id.tracking_time_label);
noGPSView = (TextView) view.findViewById(R.id.wait_for_gps);
return view;
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.fragment_time, container, false);
timerView = (TextView) layout.findViewById(R.id.tracking_time_label);
noGPSView = (TextView) layout.findViewById(R.id.wait_for_gps);
if (savedInstanceState != null) {
timerView.setText(savedInstanceState.getString(SIS_TRACKING_TIMER));
}
return layout;
}
@Override
public void onResume() {
super.onResume();
if (LocationHelper.isGPSEnabled(getActivity())) {
noGPSView.setVisibility(View.GONE);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (timerView != null) {
outState.putString(SIS_TRACKING_TIMER, timerView.getText().toString());
}
public void onStart() {
super.onStart();
HandlerThread thread = new HandlerThread(THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
thread.start();
handler = new TimerHandler(thread.getLooper(), getActivity(), timerView);
handler.sendEmptyMessageDelayed(TIMER_SET, TIMER_DELAY);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
public void onStop() {
super.onStop();
if (savedInstanceState != null && timerView != null) {
timerView.setText(savedInstanceState.getString(SIS_TRACKING_TIMER));
handler.getLooper().quit();
handler.removeMessages(TIMER_SET);
handler = null;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(SIS_TRACKING_TIMER, timerView.getText().toString());
}
private static class TimerHandler extends Handler {
private AppPreferences prefs;
private TextView timer;
TimerHandler(Looper looper, Context context, TextView timer) {
super(looper);
this.prefs = new AppPreferences(context);
this.timer = timer;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TIMER_SET:
final long trackingTimerStarted = prefs.getTrackingTimerStarted();
if (trackingTimerStarted > 0) {
final TimePoint now = MillisecondsTimePoint.now();
if (timer != null) {
timer.post(new Runnable() {
@Override
public void run() {
timer.setText(TimeUtils.formatDurationSince(now.minus(trackingTimerStarted).asMillis()));
}
});
}
}
sendEmptyMessageDelayed(TIMER_SET, TIMER_DELAY);
break;
default:
super.handleMessage(msg);
}
}
}
}
@@ -33,6 +33,7 @@ import com.sap.sailing.android.buoy.positioning.app.valueobjects.MarkInfo;
import com.sap.sailing.android.buoy.positioning.app.valueobjects.MarkPingInfo;
import com.sap.sailing.android.shared.data.LeaderboardInfo;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.ui.customviews.GPSQuality;
import com.sap.sailing.android.shared.ui.customviews.SignalQualityIndicatorView;
import com.sap.sailing.android.shared.util.LocationHelper;
import com.sap.sailing.android.shared.util.ViewHelper;
@@ -74,7 +75,7 @@ public class BuoyFragment extends BaseFragment
setUpSetPositionButton(layout, clickListener);
signalQualityIndicatorView = ViewHelper.get(layout, R.id.signal_quality_indicator);
signalQualityIndicatorView.setSignalQuality(GPSQuality.noSignal.toInt());
signalQualityIndicatorView.setSignalQuality(GPSQuality.noSignal);
mReceiver = new IntentReceiver();
mBroadcastManager = LocalBroadcastManager.getInstance(inflater.getContext());
@@ -101,7 +102,7 @@ public class BuoyFragment extends BaseFragment
mapFragment.getMap().setMapType(GoogleMap.MAP_TYPE_SATELLITE);
initialLocationUpdate = true;
MarkInfo mark = positioningActivity.getMarkInfo();
signalQualityIndicatorView.setSignalQuality(GPSQuality.noSignal.toInt());
signalQualityIndicatorView.setSignalQuality(GPSQuality.noSignal);
if (mark != null) {
setUpTextUI(lastKnownLocation);
GoogleMap map = mapFragment.getMap();
@@ -217,7 +218,7 @@ public class BuoyFragment extends BaseFragment
//provider (GPS) disabled by the user while tracking
disablePositionButton();
setUpTextUI(null);
signalQualityIndicatorView.setSignalQuality(GPSQuality.noSignal.toInt());
signalQualityIndicatorView.setSignalQuality(GPSQuality.noSignal);
LocationHelper.showNoGPSError(getActivity(), getString(R.string.enable_gps));
}
@@ -257,7 +258,7 @@ public class BuoyFragment extends BaseFragment
} else if (gpsAccuracy <= 10) {
quality = GPSQuality.great;
}
signalQualityIndicatorView.setSignalQuality(quality.toInt());
signalQualityIndicatorView.setSignalQuality(quality);
}
private void handleSuccessfulResponse() {
@@ -268,20 +269,6 @@ public class BuoyFragment extends BaseFragment
pingListener = listener;
}
private enum GPSQuality {
noSignal(0), poor(2), good(3), great(4);
private final int gpsQuality;
GPSQuality(int quality) {
this.gpsQuality = quality;
}
public int toInt() {
return this.gpsQuality;
}
}
public interface pingListener {
void updatePing();
}
@@ -174,7 +174,6 @@
<string name="race_list_filter_show_active">Aktive Rennen</string>
<string name="race_reset_time_schedule_race">Rennen planen</string>
<string name="race_reset_time_startmode_starttime">Startmodus und Startzeit setzen</string>
<string name="today">Heute</string>
<string name="start">Startzeit des Rennens setzen</string>
<string name="nationality">Nationalität</string>
<string name="number">Nummer</string>
@@ -340,7 +339,6 @@
<string name="race_set_time">Zeit setzen</string>
<string name="race_start_time_in">-%s</string>
<string name="race_start_time_ago">%s</string>
<string name="tomorrow">Morgen</string>
<string name="race_begin_finish_header">Beginn des Zieleinlaufs</string>
<string name="race_begin_finish_current">Aktuelle Zeit</string>
<string name="race_begin_finish_custom">Individuelle Zeit</string>
@@ -431,7 +429,6 @@
<string name="author_type_officer_finish">Zielschiff-Wettfahrtleiter</string>
<string name="author_type_shore_control">Regattaleitung an Land</string>
<string name="author_viewer">Demo-Modus</string>
<string name="yesterday">Gestern</string>
<string name="change_proceed">fortfahren</string>
<string name="change_cancel">Abbrechen</string>
<string name="change_message">Nach den Wettfahrtregeln sind diese Daten im aktuellen Rennenstatus nicht veränderbar.\nSie könnten jedoch die falschen Eingaben korrigieren.</string>
@@ -184,8 +184,6 @@
<string name="race_list_filter_show_active">Show active</string>
<string name="race_reset_time_schedule_race">Schedule race</string>
<string name="race_reset_time_startmode_starttime">Select start mode and start time</string>
<string name="today">Today</string>
<string name="tomorrow">Tomorrow</string>
<string name="start">Set start time</string>
<string name="nationality">Nationality</string>
<string name="number">Number</string>
@@ -456,7 +454,6 @@
<string name="author_type_officer_finish">Race Officer on Finish Vessel</string>
<string name="author_type_shore_control">Shore Control</string>
<string name="author_viewer">Viewer</string>
<string name="yesterday">Yesterday</string>
<string name="change_proceed">Proceed</string>
<string name="change_cancel">Cancel</string>
<string name="change_message">According to the race rules this data is not changeable in the current\nrace state. You may proceed nevertheless to correct wrong inputs.</string>
@@ -492,7 +489,6 @@
<string name="longitude_value" translatable="false">%.5f</string>
<string name="accuracy_value" translatable="false"><![CDATA[~ %.0fm]]></string>
<string name="accuracy_timestamp">(since %s)</string>
<string name="time_ago" translatable="false">%1$dh %2$02d\' %3$02d\"</string>
<string name="prev_course">Previous Course</string>
<string name="layout_course">Course Layout</string>
<string name="wind_degree">°</string>
@@ -531,7 +527,6 @@
<string name="preference_heading_with_declination_subtracted_summary_is_disabled">North-based</string>
<string name="preference_heading_with_declination_subtracted_enabled">Heading</string>
<string name="finishing_date">Finishing Date</string>
<string name="date_short" translatable="false">yyyy-MM-dd</string>
<string-array name="sort_by_values">
<item>by Sail Id</item>
<item>by Name</item>
@@ -1,205 +1,17 @@
package com.sap.sailing.racecommittee.app.utils;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import android.content.Context;
import android.os.Build;
import android.support.annotation.Nullable;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.NumberPicker;
import android.widget.TextView;
import android.widget.TimePicker;
import com.sap.sailing.android.shared.util.ViewHelper;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class TimeUtils {
private TimeUtils() {
// only static methods
}
/**
* 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);
}
/**
* Formats milliseconds to a string like: 01h 23'45"
*/
public static String formatTimeAgo(Context context, long milliseconds) {
Calendar time = Calendar.getInstance();
time.setTimeZone(TimeZone.getTimeZone("UTC"));
time.setTimeInMillis(milliseconds);
return context.getString(R.string.time_ago, time.get(Calendar.HOUR_OF_DAY), time.get(Calendar.MINUTE), time.get(Calendar.SECOND));
}
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<String> 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();
}
}
public class TimeUtils extends com.sap.sailing.android.shared.util.TimeUtils {
public static void initTimePickerWithSeconds(Context context, final Calendar time, @Nullable final TimePicker timePicker, @Nullable NumberPicker secondPicker) {
int textColor = ThemeHelper.getColor(context, R.attr.white);
@@ -242,32 +54,4 @@ public class TimeUtils {
});
}
}
public static ArrayList<String> getDates(Context context, TimePoint start, TimePoint end) {
ArrayList<String> dates = new ArrayList<>();
SimpleDateFormat simpleFormat = new SimpleDateFormat(context.getString(R.string.date_short), Locale.US);
Calendar startDate = Calendar.getInstance();
Calendar endDate = (Calendar) startDate.clone();
startDate.setTime(start.asDate());
endDate.setTime(end.asDate());
int dayDiff = daysBetween(endDate, startDate);
for (int i = 0; i <= dayDiff; i++) {
dates.add(simpleFormat.format(startDate.getTime()));
startDate.add(Calendar.DAY_OF_MONTH, 1);
}
return dates;
}
public static TimePoint getTime(TimePicker timePicker) {
return getTime(timePicker, null);
}
public static TimePoint getTime(TimePicker timePicker, @Nullable NumberPicker secondPicker) {
Calendar time = Calendar.getInstance();
time.set(Calendar.HOUR_OF_DAY, timePicker.getCurrentHour());
time.set(Calendar.MINUTE, timePicker.getCurrentMinute());
time.set(Calendar.SECOND, secondPicker == null ? 0 : secondPicker.getValue());
time.set(Calendar.MILLISECOND, 0);
return new MillisecondsTimePoint(time.getTime());
}
}