Merge branch 'master' into bug5239

This commit is contained in:
Dennis Aulenbacher committed 2020-09-02 17:00:49 +02:00
commit f7d4fa34aa
48 files changed
+395 -528

No files matched your search

@@ -0,0 +1,62 @@
#!/bin/bash
# You need an installation of the 2020-06 release of "Eclipse IDE for Eclipse Committers" matching you OS and JDK (32 vs 64 Bit):
# https://www.eclipse.org/downloads/packages/release/2020-06/r/eclipse-ide-eclipse-committers
if [[ $1 == "" ]]; then
echo "You need to specify the Eclipse installation directory"
exit 1
fi
installPath="$1"
installPlugins() {
"$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -installIU $2
echo ""
}
updatePlugins() {
"$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -uninstallIU $2 -installIU $2
echo ""
}
uninstallPlugins() {
"$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -uninstallIU $1
echo ""
}
# Not necessary if using Eclipse for JEE developers
echo "Installing webtools (WTP/WST/JPT) and m2e that are required by the GWT plugin..."
installPlugins http://download.eclipse.org/releases/2020-06/ org.eclipse.wst.web_ui.feature.feature.group,org.eclipse.jst.web_ui.feature.feature.group,org.eclipse.wst.xml_ui.feature.feature.group,org.eclipse.jpt.common.feature.feature.group,org.eclipse.jpt.jpa.feature.feature.group,org.eclipse.m2e.feature.feature.group,org.eclipse.m2e.wtp.feature.feature.group
echo "Installing GWT plugin..."
installPlugins http://storage.googleapis.com/gwt-eclipse-plugin/v3/release com.gwtplugins.eclipse.suite.v3.feature.feature.group
echo "Installing Mission Control plugin..."
installPlugins http://download.oracle.com/technology/products/missioncontrol/updatesites/base/6.0.0/eclipse com.oracle.jmc.feature.ide.feature.group,com.oracle.jmc.feature.flightrecorder.feature.group,com.oracle.jmc.feature.core.feature.group,com.oracle.jmc.feature.console.feature.group,com.oracle.jmc.feature.core.feature.group,com.oracle.jmc.feature.core.feature.group
echo "Installing GWT SDM debug bridge..."
installPlugins http://p2.sapsailing.com/p2/sdbg com.github.sdbg.feature.feature.group
echo "Installing EasyShell..."
installPlugins http://anb0s.github.io/EasyShell de.anbos.eclipse.easyshell.feature.feature.group
echo "Installing Memory Analyzer..."
installPlugins http://download.eclipse.org/releases/2020-06 org.eclipse.mat.feature.feature.group,org.eclipse.mat.chart.feature.feature.group
echo "Installing SAP JVM Tools (profiler) ..."
installPlugins https://tools.hana.ondemand.com/oxygen com.sap.jvm.profiling.feature.group
echo "Installing UMLet ..."
installPlugins https://www.umlet.com/umlet_latest/repository/ umlet-eclipse-feature.feature.group
echo "Installing javax.xml.bind for news feed polling (see https://stackoverflow.com/questions/52528693/eclipse-internal-error-polling-news-feeds)..."
installPlugins http://download.eclipse.org/tools/orbit/downloads/drops/R20200224183213/repository javax.xml.bind
echo "Installing Enhanced Class Decompiler"
installPlugins https://ecd-plugin.github.io/update org.sf.feeling.decompiler.feature.group,org.sf.feeling.decompiler.cfr.feature.group,org.sf.feeling.decompiler.jad.feature.group,org.sf.feeling.decompiler.jd.feature.group,org.sf.feeling.decompiler.procyon.feature.group
echo "Uninstalling Wild Web Developer"
uninstallPlugins org.eclipse.wildwebdeveloper.feature.feature.group
echo "Installation completed!"
@@ -1,8 +1,4 @@
/* InfoPlist.strings
SAPTracker
Created by Raimund Wege on 04.11.16.
Copyright © 2016 com.sap.sailing. All rights reserved. */
/* InfoPlist.strings\n SAPTracker\n \n Created by Raimund Wege on 04.11.16.\n Copyright © 2016 com.sap.sailing. All rights reserved. */
"NSLocationAlwaysUsageDescription" = "Para poder realizar el rastreo de toda una carrera, es necesario que su teléfono continúe rastreando cuando apague o bloquee la pantalla, lo que ayuda a ahorrar batería y evita tener que parar el proceso de rastreo de forma involuntaria.";
"NSLocationWhenInUseUsageDescription" = "Para poder realizar el rastreo de toda una carrera, es necesario que su teléfono continúe rastreando cuando apague o bloquee la pantalla, lo que ayuda a ahorrar batería y evita tener que parar el proceso de rastreo de forma involuntaria.";
@@ -1,8 +1,4 @@
/* InfoPlist.strings
SAPTracker
Created by Raimund Wege on 04.11.16.
Copyright © 2016 com.sap.sailing. All rights reserved. */
/* InfoPlist.strings\n SAPTracker\n \n Created by Raimund Wege on 04.11.16.\n Copyright © 2016 com.sap.sailing. All rights reserved. */
"NSLocationAlwaysUsageDescription" = "Pour pouvoir suivre l'intégralité de votre course, votre téléphone doit continuer le suivi même si vous l'éteignez ou verrouillez l'écran (pour économiser de l'énergie). Ainsi, vous évitez un arrêt non voulu du suivi.";
"NSLocationWhenInUseUsageDescription" = "Pour pouvoir suivre l'intégralité de votre course, votre téléphone doit continuer le suivi même si vous l'éteignez ou verrouillez l'écran (pour économiser de l'énergie). Ainsi, vous évitez un arrêt non voulu du suivi.";
@@ -1,8 +1,4 @@
/* InfoPlist.strings
SAPTracker
Created by Raimund Wege on 04.11.16.
Copyright © 2016 com.sap.sailing. All rights reserved. */
/* InfoPlist.strings\n SAPTracker\n \n Created by Raimund Wege on 04.11.16.\n Copyright © 2016 com.sap.sailing. All rights reserved. */
"NSLocationAlwaysUsageDescription" = "Per tracciare l'intera gara il tuo telefono deve continuare il tracciamento anche quando disattivi o blocchi lo schermo per risparmiare la batteria ed evitare di arrestare involontariamente il processo di tracciamento.";
"NSLocationWhenInUseUsageDescription" = "Per tracciare l'intera gara il tuo telefono deve continuare il tracciamento anche quando disattivi o blocchi lo schermo per risparmiare la batteria ed evitare di arrestare involontariamente il processo di tracciamento.";
@@ -1,8 +1,4 @@
/* InfoPlist.strings
SAPTracker
Created by Raimund Wege on 04.11.16.
Copyright © 2016 com.sap.sailing. All rights reserved. */
/* InfoPlist.strings\n SAPTracker\n \n Created by Raimund Wege on 04.11.16.\n Copyright © 2016 com.sap.sailing. All rights reserved. */
"NSLocationAlwaysUsageDescription" = "レース全体を追跡するには、画面のオフ時またはロック時にも電話で追跡が続行される必要があり、これは電池の節約や追跡プロセスの意図しない停止の回避に役立ちます。";
"NSLocationWhenInUseUsageDescription" = "レース全体を追跡するには、画面のオフ時またはロック時にも電話で追跡が続行される必要があり、これは電池の節約や追跡プロセスの意図しない停止の回避に役立ちます。";
@@ -1,8 +1,4 @@
/* InfoPlist.strings
SAPTracker
Created by Raimund Wege on 04.11.16.
Copyright © 2016 com.sap.sailing. All rights reserved. */
/* InfoPlist.strings\n SAPTracker\n \n Created by Raimund Wege on 04.11.16.\n Copyright © 2016 com.sap.sailing. All rights reserved. */
"NSLocationAlwaysUsageDescription" = "Para rastrear a corrida completa, seu telefone precisa continuar o rastreamento mesmo quando você desativa ou bloqueia a tela, o que ajuda a poupar bateria e evita a paragem acidental do processo de rastreamento.";
"NSLocationWhenInUseUsageDescription" = "Para rastrear a corrida completa, seu telefone precisa continuar o rastreamento mesmo quando você desativa ou bloqueia a tela, o que ajuda a poupar bateria e evita a paragem acidental do processo de rastreamento.";
@@ -1,8 +1,4 @@
/* InfoPlist.strings
SAPTracker
Created by Raimund Wege on 04.11.16.
Copyright © 2016 com.sap.sailing. All rights reserved. */
/* InfoPlist.strings\n SAPTracker\n \n Created by Raimund Wege on 04.11.16.\n Copyright © 2016 com.sap.sailing. All rights reserved. */
"NSLocationAlwaysUsageDescription" = "Для отслеживания всей гонки ваш телефон должен продолжать отслеживание даже при выключении или блокировке экрана, что помогает экономить заряд аккумулятора и избегать непреднамеренной остановки процесса отслеживания.";
"NSLocationWhenInUseUsageDescription" = "Для отслеживания всей гонки ваш телефон должен продолжать отслеживание даже при выключении или блокировке экрана, что помогает экономить заряд аккумулятора и избегать непреднамеренной остановки процесса отслеживания.";
@@ -29,6 +29,7 @@ FoilingSegmentsSailingDomainRetrieverChain=Apéndices de foil
FoilingSegmentName=Nombre de apéndice de foil
WindFixSailingDomainRetrieverChain=Puntos de viento
MarkPassingSailingDomainRetrieverChain=Superaciones de marca
TrackedRaceResultOfCompetitorRetrieverChain=Carreras
RaceResultSailingDomainRetrieverChain=Resultados de la carrera
RaceOfCompetitorSailingDomainRetrieverChain=Carreras de los competidores
ManeuverSailingDomainRetrieverChain=Maniobra
@@ -29,6 +29,7 @@ FoilingSegmentsSailingDomainRetrieverChain=Tronçons en planing
FoilingSegmentName=Nom du tronçon en planing
WindFixSailingDomainRetrieverChain=Points vent
MarkPassingSailingDomainRetrieverChain=Passages des marques
TrackedRaceResultOfCompetitorRetrieverChain=Courses
RaceResultSailingDomainRetrieverChain=Résultats de course
RaceOfCompetitorSailingDomainRetrieverChain=Courses des concurrents
ManeuverSailingDomainRetrieverChain=Manœuvre
@@ -29,6 +29,7 @@ FoilingSegmentsSailingDomainRetrieverChain=Segmenti percorsi su foil
FoilingSegmentName=Nome del segmento percorso su foil
WindFixSailingDomainRetrieverChain=Punti nave vento
MarkPassingSailingDomainRetrieverChain=Passaggi di segnali
TrackedRaceResultOfCompetitorRetrieverChain=Gare
RaceResultSailingDomainRetrieverChain=Risultati gara
RaceOfCompetitorSailingDomainRetrieverChain=Gare di concorrenti
ManeuverSailingDomainRetrieverChain=Manovra
@@ -29,6 +29,7 @@ FoilingSegmentsSailingDomainRetrieverChain=フォイリングしたセグメン
FoilingSegmentName=フォイリングしたセグメント名
WindFixSailingDomainRetrieverChain=風フィックス
MarkPassingSailingDomainRetrieverChain=マーク通過
TrackedRaceResultOfCompetitorRetrieverChain=レース
RaceResultSailingDomainRetrieverChain=レース結果
RaceOfCompetitorSailingDomainRetrieverChain=競技者のレース
ManeuverSailingDomainRetrieverChain=マニューバー
@@ -29,6 +29,7 @@ FoilingSegmentsSailingDomainRetrieverChain=Segmentos com hidrofólio
FoilingSegmentName=Nome do segmento com hidrofólio
WindFixSailingDomainRetrieverChain=Pontos fixos de vento
MarkPassingSailingDomainRetrieverChain=Passagens na marca
TrackedRaceResultOfCompetitorRetrieverChain=Corridas
RaceResultSailingDomainRetrieverChain=Resultados da corrida
RaceOfCompetitorSailingDomainRetrieverChain=Corridas de competidores
ManeuverSailingDomainRetrieverChain=Manobra
@@ -29,6 +29,7 @@ FoilingSegmentsSailingDomainRetrieverChain=Сегменты в крыльево
FoilingSegmentName=Имя сегмента в крыльевом режиме
WindFixSailingDomainRetrieverChain=Замеры ветра
MarkPassingSailingDomainRetrieverChain=Прохождения отметок
TrackedRaceResultOfCompetitorRetrieverChain=Гонки
RaceResultSailingDomainRetrieverChain=Результаты гонки
RaceOfCompetitorSailingDomainRetrieverChain=Гонки участников
ManeuverSailingDomainRetrieverChain=Маневр
@@ -29,6 +29,7 @@ FoilingSegmentsSailingDomainRetrieverChain=水翼腾空段
FoilingSegmentName=水翼腾空段名称
WindFixSailingDomainRetrieverChain=风力修复
MarkPassingSailingDomainRetrieverChain=绕标
TrackedRaceResultOfCompetitorRetrieverChain=比赛轮次
RaceResultSailingDomainRetrieverChain=比赛轮次结果
RaceOfCompetitorSailingDomainRetrieverChain=参赛队的比赛轮次
ManeuverSailingDomainRetrieverChain=操作
@@ -2,6 +2,11 @@ package com.sap.sailing.declination.impl;
import java.io.IOException;
import java.text.ParseException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -33,34 +38,20 @@ public abstract class DeclinationImporter {
*/
public Declination getDeclination(final Position position, final TimePoint timePoint,
long timeoutForOnlineFetchInMilliseconds) throws IOException, ParseException {
final Declination[] result = new Declination[1];
Thread fetcher = new Thread("Declination fetcher for "+position+"@"+timePoint) {
@Override
public void run() {
try {
Declination fetched = importRecord(position, timePoint);
synchronized (result) {
result[0] = fetched;
result.notifyAll();
}
} catch (IOException | ParserConfigurationException | SAXException e) {
logger.log(Level.INFO, "Exception while trying to load magnetic declination online", e);
synchronized (result) {
result.notifyAll(); // wake up waiter; no result will show up anymore
}
}
}
};
fetcher.start();
synchronized (result) {
if (result[0] == null) {
try {
result.wait(timeoutForOnlineFetchInMilliseconds);
} catch (InterruptedException e) {
// ignore; simply use value from file in this case
}
}
ExecutorService executorService = Executors.newFixedThreadPool(1);
try {
return executorService.submit(
() -> importRecord(position, timePoint)
).get(timeoutForOnlineFetchInMilliseconds, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
logger.log(Level.INFO, "Timeout while getting declination.", e);
// ignore; simply use value from file in this case
} catch (InterruptedException e) {
logger.log(Level.INFO, "InterruptedException while getting declination", e);
// ignore; simply use value from file in this case
} catch (ExecutionException e) {
logger.log(Level.INFO, "Exception while trying to load magnetic declination online", e);
}
return result[0];
return null;
}
}
@@ -13,4 +13,5 @@ Require-Bundle: org.junit;bundle-version="4.8.2",
com.sap.sse.common,
com.sap.sse.security.testsupport,
org.mockito.mockito-core;bundle-version="1.10.14",
org.hamcrest;bundle-version="1.1.0"
org.hamcrest;bundle-version="1.1.0",
org.objenesis;bundle-version="2.1.0"
@@ -43,22 +43,24 @@ public class FixFactory {
JSONArray timePointsMillis = (JSONArray) fixesJson.get("t");
int fixIndex = 0;
for (Object timePointMillis : timePointsMillis) {
TimePoint timePoint = new MillisecondsTimePoint(((Number) timePointMillis).longValue());
Map<Integer, Object> valuesPerSubindex = new HashMap<>();
int i=1;
JSONArray values;
while ((values=(JSONArray) fixesJson.get(""+i)) != null) {
valuesPerSubindex.put(i, values.get(fixIndex));
i++;
}
Sensor sensor = new SensorImpl(deviceSerialNumber,
fixTypeAndOptionalColonSeparatedSensorsSubId.length < 2 ? 0
: Long.valueOf(fixTypeAndOptionalColonSeparatedSensorsSubId[1]));
final Type ft = Type.valueOf(fixType);
if (ft != null) {
Fix fix = createFix(sensor, ft, timePoint, valuesPerSubindex);
result.add(fix);
fixIndex++;
if (timePointMillis != null) {
TimePoint timePoint = new MillisecondsTimePoint(((Number) timePointMillis).longValue());
Map<Integer, Object> valuesPerSubindex = new HashMap<>();
int i=1;
JSONArray values;
while ((values=(JSONArray) fixesJson.get(""+i)) != null) {
valuesPerSubindex.put(i, values.get(fixIndex));
i++;
}
Sensor sensor = new SensorImpl(deviceSerialNumber,
fixTypeAndOptionalColonSeparatedSensorsSubId.length < 2 ? 0
: Long.valueOf(fixTypeAndOptionalColonSeparatedSensorsSubId[1]));
final Type ft = Type.valueOf(fixType);
if (ft != null) {
Fix fix = createFix(sensor, ft, timePoint, valuesPerSubindex);
result.add(fix);
fixIndex++;
}
}
}
}
@@ -369,7 +369,7 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
params.getDelayToLiveInMillis(), WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND,
boatClass.getApproximateManeuverDurationInMilliseconds(), null, /*useMarkPassingCalculator*/ true, raceLogResolver,
/* Not needed because the RaceTracker is not active on a replica */ Optional.empty(),
new TrackingConnectorInfoImpl(TrackingConnectorType.RaceLog, /* webUrl */ null));
new TrackingConnectorInfoImpl(TrackingConnectorType.RaceLog, /* webUrl */ null, /* uuid */ null));
notifyRaceCreationListeners();
logger.info(String.format("Started tracking race-log race (%s)", raceLog));
// this wakes up all waiting race handles
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking;
import java.io.Serializable;
import java.net.URL;
import java.util.UUID;
import com.sap.sailing.domain.common.tracking.TrackingConnectorType;
@@ -21,4 +22,9 @@ public interface TrackingConnectorInfo extends Serializable {
* may be {@code null} if the API of the respective Tracking-Service does not provide a URL;
*/
URL getWebUrl();
/**
* gets a unique identifier for the tracked race matching the one given by the provider
*/
UUID getUuid();
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.tracking.impl;
import java.net.URL;
import java.util.UUID;
import com.sap.sailing.domain.common.tracking.TrackingConnectorType;
import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
@@ -9,11 +10,13 @@ public class TrackingConnectorInfoImpl implements TrackingConnectorInfo {
private static final long serialVersionUID = 7970268841592389145L;
private final TrackingConnectorType trackingConnectorType;
private final URL webUrl;
private final UUID uuid;
public TrackingConnectorInfoImpl(TrackingConnectorType trackingConnectorType, URL webUrl) {
public TrackingConnectorInfoImpl(TrackingConnectorType trackingConnectorType, URL webUrl, UUID itemUUID) {
super();
this.trackingConnectorType = trackingConnectorType;
this.webUrl = webUrl;
this.uuid = itemUUID;
}
public TrackingConnectorType getTrackingConnectorType() {
@@ -23,6 +26,10 @@ public class TrackingConnectorInfoImpl implements TrackingConnectorInfo {
public URL getWebUrl() {
return webUrl;
}
public UUID getUuid() {
return uuid;
}
@Override
public int hashCode() {
@@ -30,6 +37,7 @@ public class TrackingConnectorInfoImpl implements TrackingConnectorInfo {
int result = 1;
result = prime * result + ((trackingConnectorType == null) ? 0 : trackingConnectorType.hashCode());
result = prime * result + ((webUrl == null) ? 0 : webUrl.hashCode());
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
return result;
}
@@ -52,6 +60,11 @@ public class TrackingConnectorInfoImpl implements TrackingConnectorInfo {
return false;
} else if (!webUrl.equals(other.webUrl))
return false;
if (uuid == null) {
if (other.uuid != null)
return false;
} else if (!uuid.equals(other.uuid))
return false;
return true;
}
}
@@ -540,7 +540,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
}
}, useInternalMarkPassingAlgorithm, raceLogResolver,
/* Not needed because the RaceTracker is not active on a replica */ Optional.empty(),
new TrackingConnectorInfoImpl(TrackingConnectorType.SwissTiming, /*no api connection to query the webUrl*/ null));
new TrackingConnectorInfoImpl(TrackingConnectorType.SwissTiming, /*no api connection to query the webUrl*/ null, /* uuid */ null));
addUpdateHandlers();
notifyRaceCreationListeners();
logger.info("Created SwissTiming RaceDefinition and TrackedRace for "+race.getName());
@@ -417,7 +417,7 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i
/* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
/* raceDefinitionSetToUpdate */ null, useInternalMarkPassingAlgorithm, raceLogResolver,
/* Not needed because the RaceTracker is not active on a replica */ Optional.empty(),
new TrackingConnectorInfoImpl(TrackingConnectorType.SwissTiming, /*no api connection to query the webUrl*/ null));
new TrackingConnectorInfoImpl(TrackingConnectorType.SwissTiming, /*no api connection to query the webUrl*/ null, /* uuid */ null));
trackedRace.onStatusChanged(this, new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, 0));
TimePoint bestStartTimeKnownSoFar = bestStartTimePerRaceID.get(currentRaceID);
if (bestStartTimeKnownSoFar != null) {
@@ -661,7 +661,7 @@ public class DomainFactoryImpl implements DomainFactory {
trackedRegatta.getRegatta().addRace(raceDefinition);
TrackingConnectorInfo trackingConnectorInfo = null;
if (tractracRace != null) {
trackingConnectorInfo = new TrackingConnectorInfoImpl(TrackingConnectorType.TracTrac, tractracRace.getEvent().getWebURL());
trackingConnectorInfo = new TrackingConnectorInfoImpl(TrackingConnectorType.TracTrac, tractracRace.getEvent().getWebURL(), tractracRace.getId());
}
trackedRace = createTrackedRace(trackedRegatta, raceDefinition, sidelines, windStore,
delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate, ignoreTracTracMarkPassings,
@@ -276,7 +276,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<IControlRoute,
raceDefinitionSetToUpdate, useInternalMarkPassingAlgorithm, raceLogResolver,
/* ThreadLocalTransporter not needed because the RaceTracker is not active on a replica */ Optional
.empty(),
new TrackingConnectorInfoImpl(TrackingConnectorType.TracTrac, tractracRace.getEvent().getWebURL()));
new TrackingConnectorInfoImpl(TrackingConnectorType.TracTrac, tractracRace.getEvent().getWebURL(), tractracRace.getId()));
if (runAfterCreatingTrackedRace != null) {
runAfterCreatingTrackedRace.accept(trackedRace);
}
@@ -532,7 +532,7 @@ actionAddEvent=Añadir evento
publicationUrl=URL de publicación
suppress=Suprimir
isListedOnHomepage=Enumerado en la página de inicio
raceSelection=Selección de carrera
raceSelection=Selección de la carrera
unsuppress=Anular la eliminación
suppressedCompetitors=Competidores suprimidos
mediaPanel=Audio y vídeo
@@ -532,7 +532,7 @@ actionAddEvent=Aggiungi evento
publicationUrl=URL pubblicazione
suppress=Elimina
isListedOnHomepage=è elencato nella pagina principale
raceSelection=Selezione della gara
raceSelection=Selezione gara
unsuppress=Annulla eliminazione
suppressedCompetitors=Concorrenti eliminati
mediaPanel=Audio e Video
@@ -532,7 +532,7 @@ actionAddEvent=Добавить событие
publicationUrl=URL-адрес публикации
suppress=Подавлять
isListedOnHomepage=Отображается на начальной странице
raceSelection=выбор расы
raceSelection=Выбор гонки
unsuppress=Отменить подавление
suppressedCompetitors=Подавляемые участники
mediaPanel=Аудио и видео
@@ -532,7 +532,7 @@ actionAddEvent=添加活动
publicationUrl=出版 URL
suppress=禁止
isListedOnHomepage=列在主页中
raceSelection=種族選擇
raceSelection=竞赛选择
unsuppress=取消禁止
suppressedCompetitors=禁止的参赛队
mediaPanel=音频和视频
@@ -2,6 +2,7 @@ package com.sap.sailing.server.gateway.deserialization.impl;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -28,8 +29,16 @@ public class TrackingConnectorInfoJsonDeserializer implements JsonDeserializer<T
logger.log(Level.WARNING, "Error while parsing webUrl of TrackingConnectorInfo", e);
webUrl = null;
}
final String uuidString = (String) object.get(TrackingConnectorInfoJsonSerializer.FIELD_UUID);
UUID uuid;
try {
uuid = uuidString == null ? null : UUID.fromString(uuidString);
} catch (Exception e) {
logger.log(Level.WARNING, "Error while parsing uuid of TrackingConnectorInfo", e);
uuid = null;
}
final TrackingConnectorInfo trackingConnectorInfo = new TrackingConnectorInfoImpl(trackingConnectorType,
webUrl);
webUrl, uuid);
return trackingConnectorInfo;
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.server.gateway.serialization.impl;
import java.net.URL;
import java.util.UUID;
import org.json.simple.JSONObject;
@@ -10,6 +11,7 @@ import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public class TrackingConnectorInfoJsonSerializer implements JsonSerializer<TrackingConnectorInfo> {
public static final String FIELD_TRACKING_CONNECTOR_TYPE = "trackingConnectorType";
public static final String FIELD_WEB_URL = "webUrl";
public static final String FIELD_UUID = "uuid";
@Override
public JSONObject serialize(TrackingConnectorInfo trackingConnectorInfo) {
@@ -19,6 +21,10 @@ public class TrackingConnectorInfoJsonSerializer implements JsonSerializer<Track
if (webUrl != null) {
result.put(FIELD_WEB_URL, webUrl.toString());
}
UUID uuid = trackingConnectorInfo.getUuid();
if (uuid != null) {
result.put(FIELD_UUID, uuid.toString());
}
return result;
}
}
@@ -220,6 +220,7 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource {
jsonRaceColumn.put("regattaName",
trackedRace.getTrackedRegatta().getRegatta().getName());
jsonRaceColumn.put("trackedRaceName", trackedRace.getRace().getName());
jsonRaceColumn.put("trackedRaceUuid", trackedRace.getTrackingConnectorInfo().getUuid());
final JSONObject raceBoardURLsByEventID = new JSONObject();
for (Event event : eventsReferencingLeaderboardGroup) {
if (Util.containsAny(event.getVenue().getCourseAreas(), leaderboard.getCourseAreas())) {
@@ -95,7 +95,7 @@ public class MessageSendingService extends Service implements MessageSendingList
* Can be used to keep messages from being sent; the race manager app, for example, uses this to avoid sending
* messages back to the server that were received from the server in the first place.
*/
private Set<Serializable> suppressedMessageIds = new HashSet<Serializable>();
private Set<Serializable> suppressedMessageIds = new HashSet<>();
private APIConnectivityListener apiConnectivityListener;
@@ -243,24 +243,12 @@ public class MessageSendingService extends Service implements MessageSendingList
CharSequence name = getString(R.string.service_info);
NotificationHelper.createNotificationChannel(this, NotificationHelper.getNotificationChannelId(), name);
startForeground(NotificationHelper.getNotificationId(), NotificationHelper.getNotification(this, NotificationHelper.getNotificationChannelId()));
ExLog.i(this, TAG, "Sending Service on Create.");
}
@Override
public void onDestroy() {
stopForeground(true);
stopSelf();
ExLog.i(this, TAG, "Message Sending Service is being destroyed.");
super.onDestroy();
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
stopForeground(true);
stopSelf();
ExLog.i(this, TAG, "Message Sending Service is being removed.");
}
@Override
@@ -270,17 +258,17 @@ public class MessageSendingService extends Service implements MessageSendingList
return START_STICKY;
}
ExLog.i(this, TAG, "Sending Service is called by following intent: " + intent.getAction());
handleCommand(intent, startId);
handleCommand(intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
private void handleCommand(Intent intent, int startId) {
private void handleCommand(Intent intent) {
String action = intent.getAction();
if (action.equals(getString(R.string.intent_send_saved_intents))) {
if (getString(R.string.intent_send_saved_intents).equals(action)) {
handleDelayedMessages();
} else if (action.equals(getString(R.string.intent_send_message))) {
} else if (getString(R.string.intent_send_message).equals(action)) {
handleSendMessages(intent);
}
}
@@ -444,7 +432,7 @@ public class MessageSendingService extends Service implements MessageSendingList
public static String getRaceLogEventSendAndReceiveUrl(Context context, final String raceGroupName,
final String raceName, final String fleetName) throws UnsupportedEncodingException {
String url = String.format(
return String.format(
"%s/sailingserver/rc/racelog?" + RaceLogServletConstants.PARAMS_LEADERBOARD_NAME + "=%s&"
+ RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME + "=%s&"
+ RaceLogServletConstants.PARAMS_RACE_FLEET_NAME + "=%s&"
@@ -453,7 +441,6 @@ public class MessageSendingService extends Service implements MessageSendingList
R.string.preference_server_url_default),
URLEncoder.encode(raceGroupName, charsetName), URLEncoder.encode(raceName, charsetName),
URLEncoder.encode(fleetName, charsetName), uuid);
return url;
}
/**
@@ -494,9 +481,9 @@ public class MessageSendingService extends Service implements MessageSendingList
* Listener interface for reporting of connectivity and number of unsent GPS-fixes.
*/
public interface APIConnectivityListener {
public void apiConnectivityUpdated(APIConnectivity apiConnectivity);
void apiConnectivityUpdated(APIConnectivity apiConnectivity);
public void setUnsentGPSFixesCount(int count);
void setUnsentGPSFixesCount(int count);
}
/**
@@ -521,11 +508,10 @@ public class MessageSendingService extends Service implements MessageSendingList
public static String getRacePositionsUrl(Context context, final String regattaName, final String raceName)
throws UnsupportedEncodingException {
String url = String.format("%s/sailingserver/api/v1/regattas/%s/races/%s/marks/positions",
return String.format("%s/sailingserver/api/v1/regattas/%s/races/%s/marks/positions",
PrefUtils.getString(context, R.string.preference_server_url_key,
R.string.preference_server_url_default),
URLEncoder.encode(regattaName, charsetName).replace("+", "%20"),
URLEncoder.encode(raceName, charsetName.replace("+", "%20")));// ,
return url;
}
}
@@ -1,26 +1,26 @@
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;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.GeomagneticField;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
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.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.shared.util.NotificationHelper;
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;
import com.sap.sailing.android.tracking.app.utils.AppPreferences;
import com.sap.sailing.android.tracking.app.utils.DatabaseHelper;
import com.sap.sailing.android.tracking.app.valueobjects.EventInfo;
@@ -31,24 +31,19 @@ import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
import com.sap.sse.common.impl.DegreeBearingImpl;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
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.widget.Toast;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
public class TrackingService extends Service implements LocationListener {
@@ -71,7 +66,6 @@ public class TrackingService extends Service implements LocationListener {
public final static int UPDATE_INTERVAL_IN_MILLIS_DEFAULT = 1000;
public final static String GPS_DISABLED_MESSAGE = "gpsDisabled";
private float minLocationUpdateDistanceInMeters = 0f;
private String checkinDigest;
private EventInfo event;
@@ -110,15 +104,6 @@ public class TrackingService extends Service implements LocationListener {
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
/**
* prior to JellyBean, the minimum time for location updates parameter MIGHT be ignored, so providing a
* minimum distance value greater than 0 is recommended
*/
minLocationUpdateDistanceInMeters = .5f;
}
locationsQueuedBasedOnSendingInterval = new LinkedHashMap<>();
prefs = new AppPreferences(this);
@@ -158,10 +143,8 @@ public class TrackingService extends Service implements LocationListener {
@SuppressWarnings("MissingPermission")
private void startTracking() {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, UPDATE_INTERVAL_IN_MILLIS_DEFAULT,
minLocationUpdateDistanceInMeters, this);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, UPDATE_INTERVAL_IN_MILLIS_DEFAULT, 0f, this);
ExLog.i(this, TAG, "Started Tracking");
showNotification();
prefs.setTrackerIsTracking(true);
prefs.setTrackerIsTrackingCheckinDigest(checkinDigest);
@@ -344,18 +327,9 @@ public class TrackingService extends Service implements LocationListener {
@Override
public void onDestroy() {
stopTracking();
stopForeground(true);
Toast.makeText(this, R.string.tracker_stopped, Toast.LENGTH_SHORT).show();
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
stopTracking();
stopForeground(true);
ExLog.i(this, TAG, "Tracking Service is being removed.");
}
/**
* Methods implemented through LocationManager
*/
@@ -395,20 +369,6 @@ public class TrackingService extends Service implements LocationListener {
// provider (GPS) (re)enabled by the user while tracking
}
private void showNotification() {
// Starting in Android 8.0 (API level 26), all notifications must be assigned to a channel
CharSequence name = getText(R.string.service_info);
NotificationHelper.createNotificationChannel(this, NotificationHelper.getNotificationChannelId(), name);
Intent intent = new Intent(this, TrackingActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = NotificationHelper.getNotification(this, NotificationHelper.getNotificationChannelId(), getText(R.string.app_name),
getString(R.string.tracking_notification_text, event.name), pendingIntent);
startForeground(NotificationHelper.getNotificationId(), notification);
}
public void registerGPSQualityListener(GPSQualityListener listener) {
gpsQualityListener = listener;
}
@@ -435,8 +395,7 @@ public class TrackingService extends Service implements LocationListener {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NO_LOCATION:
if (msg.what == NO_LOCATION) {
ExLog.i(getApplicationContext(), TAG, "No Location");
Location location = (Location) msg.obj;
if (location == null) {
@@ -444,10 +403,6 @@ public class TrackingService extends Service implements LocationListener {
}
reportGPSQualityBearingAndSpeed(NO_DISTANCE, location.getBearing(), location.getSpeed(),
location.getLatitude(), location.getLongitude(), location.getAltitude());
break;
default:
super.handleMessage(msg);
}
}
}
@@ -74,7 +74,8 @@
<service
android:name=".services.polling.RaceLogPollingService"
android:exported="false">
android:exported="false"
android:stopWithTask="true">
<intent-filter>
<action android:name="com.sap.sailing.racecommittee.app.action.stopPoller" />
<action android:name="com.sap.sailing.racecommittee.app.action.addRace" />
@@ -84,7 +85,8 @@
</service>
<service
android:name=".services.RaceStateService"
android:exported="false">
android:exported="false"
android:stopWithTask="true">
<intent-filter>
<action android:name="com.sap.sailing.racecommittee.app.action.startProcedureSpecificAction" />
</intent-filter>
@@ -22,8 +22,9 @@ public class AppConstants {
private final static String INTENT_ACTION = PACKAGE_NAME + ".action";
public final static String INTENT_ACTION_RESET = INTENT_ACTION + ".reset";
public final static String INTENT_ACTION_REGISTER_RACE = INTENT_ACTION + ".registerRace";
public final static String INTENT_ACTION_UNREGISTER_RACE = INTENT_ACTION + ".unregisterRace";
public final static String INTENT_ACTION_CLEAR_RACES = INTENT_ACTION + ".clearRaces";
public final static String INTENT_ACTION_CLEANUP_RACES = INTENT_ACTION + ".cleanupRaces";
public final static String INTENT_ACTION_ALARM_ACTION = INTENT_ACTION + ".alarmAction";
public final static String INTENT_ACTION_RELOAD_RACES = INTENT_ACTION + ".reloadRaces";
public final static String INTENT_ACTION_START_PROCEDURE_SPECIFIC_ACTION = INTENT_ACTION
@@ -1,13 +1,12 @@
package com.sap.sailing.racecommittee.app.data;
import android.content.Context;
import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.AppPreferences;
import com.sap.sailing.racecommittee.app.services.RaceStateService;
import android.content.Context;
import android.content.Intent;
/**
* Base class for all data managers. Use {@link DataManager#create(Context)} for creating your {@link DataManager}.
*/
@@ -15,7 +14,6 @@ public abstract class DataManager implements ReadonlyDataManager {
public static ReadonlyDataManager create(Context context) {
DataStore dataStore = InMemoryDataStore.INSTANCE;
dataStore.setContext(context);
if (AppPreferences.on(context).isOfflineMode()) {
return new OfflineDataManager(context, dataStore, dataStore.getDomainFactory());
}
@@ -43,30 +41,13 @@ public abstract class DataManager implements ReadonlyDataManager {
}
/**
* {@link InMemoryDataStore#reset() Resets} the data store properly and, by means of calling
* {@link #unloadAllRaces()}, fires the {@link AppConstants#INTENT_ACTION_CLEAR_RACES} intent which is expected to
* {@link InMemoryDataStore#clearRaces(Context)}, fires the {@link AppConstants#INTENT_ACTION_CLEAR_RACES} intent which is expected to
* be handled by the {@link RaceStateService} which responds by unregistering all its races, stopping to listen on
* and poll their race logs and by clearing the data store's races collection.
* {@link InMemoryDataStore#reset()} resets the data store properly.
*/
public void resetAll() {
unloadAllRaces();
InMemoryDataStore.INSTANCE.clearRaces(context);
InMemoryDataStore.INSTANCE.reset();
}
/**
* Properly unloading of all races by first unloading / unregistering them from the race state service. The
* {@link RaceStateService} then in turn clears the races on the data store.
*/
public void unloadAllRaces() {
// It's not needed to call getDataStore().getRaces().clear() because it's already called by the service
// internally.
Intent intent = new Intent(context, RaceStateService.class);
intent.setAction(AppConstants.INTENT_ACTION_CLEAR_RACES);
context.startService(intent);
}
public void stopService() {
Intent intent = new Intent(context, RaceStateService.class);
context.stopService(intent);
}
}
@@ -1,9 +1,6 @@
package com.sap.sailing.racecommittee.app.data;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
import java.util.UUID;
import android.content.Context;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.base.CourseArea;
@@ -14,12 +11,13 @@ import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import android.content.Context;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
import java.util.UUID;
public interface DataStore {
void setContext(Context context);
void reset();
SharedDomainFactory getDomainFactory();
@@ -46,7 +44,7 @@ public interface DataStore {
void addRace(int index, ManagedRace race);
void removeRace(ManagedRace race);
void removeRace(Context context, ManagedRace race);
ManagedRace getRace(String id);
@@ -56,7 +54,9 @@ public interface DataStore {
boolean hasRace(SimpleRaceLogIdentifier id);
void registerRaces(Collection<ManagedRace> races);
void registerRaces(Context context, Collection<ManagedRace> races);
void clearRaces(Context context);
Collection<Mark> getMarks(RaceGroup raceGroup);
@@ -1,12 +1,7 @@
package com.sap.sailing.racecommittee.app.data;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import android.content.Context;
import android.content.Intent;
import com.sap.sailing.android.shared.util.CollectionUtils;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
@@ -18,22 +13,24 @@ import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.domain.base.impl.SharedDomainFactoryImpl;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier;
import com.sap.sailing.racecommittee.app.domain.impl.FleetIdentifierImpl;
import com.sap.sailing.racecommittee.app.services.RaceStateService;
import com.sap.sse.common.Util.Triple;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public enum InMemoryDataStore implements DataStore {
INSTANCE;
private Context mContext;
private LinkedHashMap<Serializable, EventBase> eventsById;
private LinkedHashMap<SimpleRaceLogIdentifier, ManagedRace> managedRaceById;
private LinkedHashMap<RaceGroup, LinkedHashMap<Serializable, Mark>> marksData;
@@ -43,22 +40,10 @@ public enum InMemoryDataStore implements DataStore {
private Serializable eventUUID;
private UUID courseAreaId;
private RaceStateService mService;
private boolean mBound;
InMemoryDataStore() {
reset();
}
@Override
public void setContext(Context context) {
if (mContext == null) {
mContext = context.getApplicationContext();
}
Intent intent = new Intent(mContext, RaceStateService.class);
mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
public void reset() {
eventsById = new LinkedHashMap<>();
@@ -69,11 +54,6 @@ public enum InMemoryDataStore implements DataStore {
eventUUID = null;
courseAreaId = null;
if (mContext != null && mBound) {
mContext.unbindService(mConnection);
mBound = false;
}
}
@Override
@@ -132,9 +112,12 @@ public enum InMemoryDataStore implements DataStore {
@Override
public CourseArea getCourseArea(Serializable id) {
for (EventBase event : eventsById.values()) {
for (CourseArea courseArea : getCourseAreas(event)) {
if (courseArea.getId().equals(id)) {
return courseArea;
final Collection<CourseArea> courseAreas = getCourseAreas(event);
if (courseAreas != null) {
for (CourseArea courseArea : courseAreas) {
if (courseArea.getId().equals(id)) {
return courseArea;
}
}
}
}
@@ -144,9 +127,12 @@ public enum InMemoryDataStore implements DataStore {
@Override
public boolean hasCourseArea(Serializable id) {
for (EventBase event : eventsById.values()) {
for (CourseArea courseArea : getCourseAreas(event)) {
if (courseArea.getId().equals(id)) {
return true;
final Collection<CourseArea> courseAreas = getCourseAreas(event);
if (courseAreas != null) {
for (CourseArea courseArea : courseAreas) {
if (courseArea.getId().equals(id)) {
return true;
}
}
}
}
@@ -204,8 +190,8 @@ public enum InMemoryDataStore implements DataStore {
}
@Override
public void removeRace(ManagedRace race) {
unregisterRace(race);
public void removeRace(Context context, ManagedRace race) {
unregisterRace(context, race);
managedRaceById.remove(convertManagedRaceIdentifierToSimpleRaceLogIdentifier(race.getIdentifier()));
}
@@ -300,7 +286,7 @@ public enum InMemoryDataStore implements DataStore {
@Override
public Set<RaceGroup> getRaceGroups() {
Set<RaceGroup> raceGroups = new HashSet<RaceGroup>();
Set<RaceGroup> raceGroups = new HashSet<>();
for (ManagedRace race : getRaces()) {
raceGroups.add(race.getRaceGroup());
}
@@ -337,34 +323,28 @@ public enum InMemoryDataStore implements DataStore {
courseAreaId = uuid;
}
public void registerRaces(Collection<ManagedRace> races) {
//TODO Log
if (mBound) {
for (ManagedRace race : races) {
mService.registerRace(race);
}
@Override
public void registerRaces(Context context, Collection<ManagedRace> races) {
for (ManagedRace race : races) {
final Intent registerIntent = new Intent(context, RaceStateService.class);
registerIntent.setAction(AppConstants.INTENT_ACTION_REGISTER_RACE);
registerIntent.putExtra(AppConstants.INTENT_EXTRA_RACE_ID, race.getId());
context.startService(registerIntent);
}
}
private void unregisterRace(ManagedRace race) {
if (mBound) {
mService.unregisterRace(race);
}
@Override
public void clearRaces(Context context) {
managedRaceById.clear();
final Intent intent = new Intent(context, RaceStateService.class);
intent.setAction(AppConstants.INTENT_ACTION_CLEAR_RACES);
context.startService(intent);
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
//TODO Log
RaceStateService.RaceStateServiceBinder binder = (RaceStateService.RaceStateServiceBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
private void unregisterRace(Context context, ManagedRace race) {
final Intent intent = new Intent(context, RaceStateService.class);
intent.setAction(AppConstants.INTENT_ACTION_UNREGISTER_RACE);
intent.putExtra(AppConstants.INTENT_EXTRA_RACE_ID, race.getId());
context.startService(intent);
}
}
@@ -1,19 +1,16 @@
package com.sap.sailing.racecommittee.app.data.handlers;
import android.content.Context;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult.ResolutionFailed;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult.ResolutionFailed;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.data.OnlineDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.services.polling.RaceLogPollingService;
import android.content.Context;
import android.content.Intent;
public class ManagedRacesDataHandler extends DataHandler<Collection<ManagedRace>> {
private Context context;
@@ -37,18 +34,10 @@ public class ManagedRacesDataHandler extends DataHandler<Collection<ManagedRace>
@Override
public void onResult(Collection<ManagedRace> data, boolean isCached) {
if (!isCached) {
Set<DeleteFromDataStore> deleteList = new HashSet<>();
for (ManagedRace race : manager.getDataStore().getRaces()) {
if (!data.contains(race)) {
deleteList.add(new DeleteFromDataStore(context, manager, race));
}
}
for (DeleteFromDataStore action : deleteList) {
action.run();
}
manager.getDataStore().clearRaces(context);
manager.addRaces(data);
calcRaceState(data);
manager.getDataStore().registerRaces(data);
manager.getDataStore().registerRaces(context, data);
}
}
@@ -78,27 +67,4 @@ public class ManagedRacesDataHandler extends DataHandler<Collection<ManagedRace>
raceStatesWithUnresolvedStartTimes.removeAll(resolved);
} while (oldNumberOfRaceStatesWithUnresolvedStartTimes != raceStatesWithUnresolvedStartTimes.size());
}
private static class DeleteFromDataStore implements Runnable {
private final Context context;
private final OnlineDataManager manager;
private final ManagedRace race;
public DeleteFromDataStore(Context context, OnlineDataManager manager, ManagedRace race) {
this.context = context;
this.manager = manager;
this.race = race;
}
@Override
public void run() {
manager.getDataStore().removeRace(race);
Intent intent = new Intent(context, RaceLogPollingService.class);
intent.setAction(AppConstants.INTENT_ACTION_POLLING_RACE_REMOVE);
intent.putExtra(AppConstants.INTENT_ACTION_EXTRA, race.getId());
context.startService(intent);
}
}
}
@@ -1,12 +1,16 @@
package com.sap.sailing.racecommittee.app.services;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Pair;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.NotificationHelper;
@@ -21,7 +25,6 @@ import com.sap.sailing.domain.abstractlog.race.state.impl.RaceStateEvents;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.data.DataManager;
import com.sap.sailing.racecommittee.app.data.DataStore;
import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.services.polling.RaceLogPollingService;
@@ -32,35 +35,16 @@ import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerialize
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogEventSerializer;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Pair;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class RaceStateService extends Service {
private final static String TAG = RaceStateService.class.getName();
/**
* Binder for this {@link RaceStateService}.
*/
public class RaceStateServiceBinder extends Binder {
public RaceStateService getService() {
return RaceStateService.this;
}
}
private final IBinder mBinder = new RaceStateServiceBinder();
private AlarmManager alarmManager;
private int alarmManagerRequestCode = 0;
@@ -81,10 +65,6 @@ public class RaceStateService extends Service {
this.registeredStateEventSchedulers = new HashMap<>();
this.managedIntents = new HashMap<>();
super.onCreate();
Notification notification = setupNotification(null);
startForeground(NotificationHelper.getNotificationId(), notification);
ExLog.i(this, TAG, "Started.");
}
@@ -105,7 +85,7 @@ public class RaceStateService extends Service {
@Override
public IBinder onBind(Intent intent) {
return mBinder;
return null;
}
@Override
@@ -113,7 +93,7 @@ public class RaceStateService extends Service {
if (intent == null) {
ExLog.i(this, TAG, "Restarted.");
} else {
handleStartCommand(intent);
handleStartCommand(intent, startId);
}
// We want this service to continue running until it is explicitly
@@ -123,18 +103,50 @@ public class RaceStateService extends Service {
@Override
public void onDestroy() {
unregisterAllRaces();
if (!managedIntents.isEmpty()) {
unregisterAllRaces();
}
stopForeground(true);
super.onDestroy();
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
unregisterAllRaces();
stopForeground(true);
stopSelf();
ExLog.i(this, TAG, "Race State Service is being removed.");
private void handleStartCommand(Intent intent, int startId) {
String action = intent.getAction();
ExLog.i(this, TAG, String.format("Command action '%s' received.", action));
if (action == null) {
return;
}
if (AppConstants.INTENT_ACTION_CLEAR_RACES.equals(action)) {
handleClearRaces();
stopSelf(startId);
} else {
String id = intent.getStringExtra(AppConstants.INTENT_EXTRA_RACE_ID);
ManagedRace race = dataManager.getDataStore().getRace(id);
if (race == null) {
ExLog.w(this, TAG, "No race for id " + id);
return;
}
switch (action) {
case AppConstants.INTENT_ACTION_REGISTER_RACE:
registerRace(race);
break;
case AppConstants.INTENT_ACTION_UNREGISTER_RACE:
unregisterRace(race);
break;
case AppConstants.INTENT_ACTION_ALARM_ACTION:
long timePoint = intent.getLongExtra(AppConstants.INTENT_EXTRA_TIMEPOINT_MILLIS, 0);
String eventName = intent.getStringExtra(AppConstants.INTENT_EXTRA_EVENTNAME);
RaceStateEvent event = new RaceStateEventImpl(new MillisecondsTimePoint(timePoint),
RaceStateEvents.valueOf(eventName));
ExLog.i(this, TAG, String.format("Processing %s", event.toString()));
race.getState().processStateEvent(event);
clearAlarmByName(race, event.getEventName());
break;
}
}
}
private void unregisterAllRaces() {
@@ -162,7 +174,7 @@ public class RaceStateService extends Service {
ExLog.i(this, TAG, "All races unregistered.");
}
public void unregisterRace(ManagedRace race) {
private void unregisterRace(ManagedRace race) {
Intent intent = new Intent(this, RaceLogPollingService.class);
intent.setAction(AppConstants.INTENT_ACTION_POLLING_RACE_REMOVE);
intent.putExtra(AppConstants.INTENT_ACTION_EXTRA, race.getId());
@@ -178,7 +190,7 @@ public class RaceStateService extends Service {
}
managedIntents.remove(race.getId());
} else {
ExLog.w(this, TAG, "Couldn't find any managed intents for race "+race.getId());
ExLog.w(this, TAG, "Couldn't find any managed intents for race " + race.getId());
}
ExLog.i(this, TAG, "Race " + race.getId() + " unregistered");
updateNotification();
@@ -197,91 +209,9 @@ public class RaceStateService extends Service {
}
}
private void handleStartCommand(Intent intent) {
String action = intent.getAction();
ExLog.i(this, TAG, String.format("Command action '%s' received.", action));
if (action != null) {
switch (action) {
case AppConstants.INTENT_ACTION_CLEAR_RACES:
handleClearRaces();
break;
case AppConstants.INTENT_ACTION_CLEANUP_RACES:
handleCleanupRaces();
break;
default:
String id = intent.getStringExtra(AppConstants.INTENT_EXTRA_RACE_ID);
ManagedRace race = dataManager.getDataStore().getRace(id);
if (race == null) {
ExLog.w(this, TAG, "No race for id " + id);
return;
}
switch (action) {
case AppConstants.INTENT_ACTION_ALARM_ACTION:
long timePoint = intent.getLongExtra(AppConstants.INTENT_EXTRA_TIMEPOINT_MILLIS, 0);
String eventName = intent.getStringExtra(AppConstants.INTENT_EXTRA_EVENTNAME);
RaceStateEvent event = new RaceStateEventImpl(new MillisecondsTimePoint(timePoint),
RaceStateEvents.valueOf(eventName));
ExLog.i(this, TAG, String.format("Processing %s", event.toString()));
race.getState().processStateEvent(event);
clearAlarmByName(race, event.getEventName());
break;
}
}
}
}
private void handleClearRaces() {
unregisterAllRaces();
DataStore dataStore = dataManager.getDataStore();
dataStore.getRaces().clear();
dataStore.setEventUUID(null);
dataStore.setCourseAreaId(null);
ExLog.i(this, TAG, "handleClearRaces: Cleared all races.");
stopSelf();
}
private class RemoveRaceAction implements Runnable {
private final ManagedRace managedRace;
/* package */ RemoveRaceAction(@NonNull ManagedRace race) {
managedRace = race;
}
@Override
public void run() {
unregisterRace(managedRace);
}
}
private void handleCleanupRaces() {
Set<RemoveRaceAction> actions = new HashSet<>();
for (String raceId : managedIntents.keySet()) {
if (!inDataStore(raceId)) {
for (ManagedRace race : registeredLogListeners.keySet()) {
if (race.getId().equals(raceId)) {
actions.add(new RemoveRaceAction(race));
}
}
}
}
for (RemoveRaceAction action : actions) {
action.run();
}
updateNotification();
}
private boolean inDataStore(String raceId) {
for (ManagedRace race : dataManager.getDataStore().getRaces()) {
if (race.getId().equals(raceId)) {
return true;
}
}
return false;
}
private void updateNotification() {
@@ -291,12 +221,12 @@ public class RaceStateService extends Service {
startForeground(NotificationHelper.getNotificationId(), notification);
}
public void registerRace(ManagedRace race) {
private void registerRace(ManagedRace race) {
ExLog.i(this, TAG, "Trying to register race " + race.getId());
if (!managedIntents.containsKey(race.getId())) {
RaceState state = race.getState();
managedIntents.put(race.getId(), new ArrayList<Pair<PendingIntent, RaceStateEvents>>());
managedIntents.put(race.getId(), new ArrayList<>());
// Register on event additions...
JsonSerializer<RaceLogEvent> eventSerializer = RaceLogEventSerializer
@@ -338,9 +268,12 @@ public class RaceStateService extends Service {
}
@TargetApi(Build.VERSION_CODES.KITKAT)
/* package */ void setAlarm(ManagedRace race, RaceStateEvent event) {
/* package */ void setAlarm(ManagedRace race, RaceStateEvent event) {
PendingIntent intent = createAlarmPendingIntent(race, event);
managedIntents.get(race.getId()).add(Pair.create(intent, event.getEventName()));
final List<Pair<PendingIntent, RaceStateEvents>> intents = managedIntents.get(race.getId());
if (intents != null) {
intents.add(Pair.create(intent, event.getEventName()));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, event.getTimePoint().asMillis(), intent);
} else {
@@ -352,10 +285,12 @@ public class RaceStateService extends Service {
/* package */ void clearAlarmByName(ManagedRace race, RaceStateEvents stateEventName) {
List<Pair<PendingIntent, RaceStateEvents>> intents = managedIntents.get(race.getId());
Pair<PendingIntent, RaceStateEvents> toBeRemoved = null;
for (Pair<PendingIntent, RaceStateEvents> intentPair : intents) {
if (intentPair.second.equals(stateEventName)) {
toBeRemoved = intentPair;
break;
if (intents != null) {
for (Pair<PendingIntent, RaceStateEvents> intentPair : intents) {
if (intentPair.second.equals(stateEventName)) {
toBeRemoved = intentPair;
break;
}
}
}
if (toBeRemoved != null) {
@@ -1,12 +1,14 @@
package com.sap.sailing.racecommittee.app.services.polling;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessageSendingService;
@@ -18,15 +20,13 @@ import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RaceLogPollingService extends Service
implements AppPreferences.PollingActiveChangedListener, RaceLogPollingTask.PollingResultListener {
@@ -60,45 +60,41 @@ public class RaceLogPollingService extends Service
ExLog.i(this, TAG, "Restarted");
} else {
String action = intent.getAction();
String extra = null;
if (intent.getExtras() != null) {
extra = intent.getExtras().getString(AppConstants.INTENT_ACTION_EXTRA);
}
String extra = intent.getStringExtra(AppConstants.INTENT_ACTION_EXTRA);
switch (action) {
case AppConstants.INTENT_ACTION_POLLING_STOP:
stopSelf();
break;
case AppConstants.INTENT_ACTION_POLLING_STOP:
stopSelf(startId);
break;
case AppConstants.INTENT_ACTION_POLLING_RACE_ADD:
if (TextUtils.isEmpty(extra)) {
ExLog.i(this, TAG, "INTENT_ACTION_EXTRA was null for " + action);
} else {
registerRace(extra);
}
break;
case AppConstants.INTENT_ACTION_POLLING_RACE_ADD:
if (TextUtils.isEmpty(extra)) {
ExLog.i(this, TAG, "INTENT_ACTION_EXTRA was null for " + action);
} else {
registerRace(extra);
}
break;
case AppConstants.INTENT_ACTION_POLLING_RACE_REMOVE:
if (TextUtils.isEmpty(extra)) {
ExLog.i(this, TAG, "INTENT_ACTION_EXTRA was null for " + action);
} else {
unregisterRace(extra);
}
break;
case AppConstants.INTENT_ACTION_POLLING_RACE_REMOVE:
if (TextUtils.isEmpty(extra)) {
ExLog.i(this, TAG, "INTENT_ACTION_EXTRA was null for " + action);
} else {
unregisterRace(extra);
}
break;
case AppConstants.INTENT_ACTION_POLLING_POLL:
poll();
break;
case AppConstants.INTENT_ACTION_POLLING_POLL:
poll();
break;
}
}
return super.onStartCommand(intent, flags, startId);
// We want this service to continue running until it is explicitly
// stopped, therefore return sticky.
return START_STICKY;
}
@Override
public void onDestroy() {
ExLog.i(this, TAG, "onDestroy");
stopForeground(true);
stopSelf();
mRaces.clear();
if (mAppPreferences != null) {
mAppPreferences.unregisterPollingActiveChangedListener(this);
@@ -106,15 +102,6 @@ public class RaceLogPollingService extends Service
if (mAlarm != null && mPendingIntent != null) {
mAlarm.cancel(mPendingIntent);
}
super.onDestroy();
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
stopForeground(true);
stopSelf();
ExLog.i(this, TAG, "Race State Service is being removed.");
}
@Override
@@ -200,7 +187,7 @@ public class RaceLogPollingService extends Service
List<Util.Pair<String, URL>> queries = getPollingQueries();
RaceLogPollingTask task = new RaceLogPollingTask(this, this);
@SuppressWarnings("unchecked")
Util.Pair<String, URL>[] param = queries.toArray(new Util.Pair[queries.size()]);
Util.Pair<String, URL>[] param = queries.toArray(new Util.Pair[0]);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, param);
}
}
@@ -221,7 +208,7 @@ public class RaceLogPollingService extends Service
mAlarm.cancel(mPendingIntent);
}
if (mAppPreferences.isPollingActive() && mRaces.size() >= 0) {
if (mAppPreferences.isPollingActive() && !mRaces.isEmpty()) {
long time = MillisecondsTimePoint.now().asMillis() + (1000 * mAppPreferences.getPollingInterval());
Intent intent = new Intent(this, this.getClass());
intent.setAction(AppConstants.INTENT_ACTION_POLLING_POLL);
@@ -59,6 +59,7 @@ public abstract class SendingServiceAwareActivity extends ResilientActivity
SendingServiceAwareActivity activity = mActivity.get();
if (activity != null) {
activity.boundSendingService = false;
activity.sendingServiceConnection = null;
}
}
@@ -89,14 +90,10 @@ public abstract class SendingServiceAwareActivity extends ResilientActivity
private static String TAG = SendingServiceAwareActivity.class.getName();
public SendingServiceAwareActivity() {
sendingServiceConnection = new MessageSendingServiceConnection(this);
}
@Override
public void onStart() {
super.onStart();
sendingServiceConnection = new MessageSendingServiceConnection(this);
Intent intent = new Intent(this, MessageSendingService.class);
ExLog.i(this, TAG, "bindSendingService");
bindService(intent, sendingServiceConnection, Context.BIND_AUTO_CREATE);
@@ -107,8 +104,11 @@ public abstract class SendingServiceAwareActivity extends ResilientActivity
super.onStop();
ExLog.i(this, TAG, "unbindSendingService");
unbindService(sendingServiceConnection);
boundSendingService = false;
if (sendingServiceConnection!=null) {
unbindService(sendingServiceConnection);
boundSendingService = false;
sendingServiceConnection = null;
}
}
protected void updateSendingServiceInformation() {