Merge branch 'bug4935' into master

This commit is contained in:
Robert Benedikt committed 2020-08-28 11:35:27 +02:00
commit 2cf88ca303
11 files changed
+235 -442

No files matched your search

@@ -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() {