# working towards migration of Event Sending Services

This commit is contained in:
Lukas Niemeier committed 2013-03-03 21:12:12 +01:00
1 parent 42223b63f5
commit fdfc27278c
22 files changed
+849 -127

No files matched your search

@@ -13,6 +13,8 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<application
android:icon="@drawable/sap_sailing_app_icon"
@@ -1,4 +1,4 @@
package com.sap.sailing.racecommittee.app.domain.state;
package com.sap.sailing.racecommittee.app.domain.racelog;
import com.sap.sailing.domain.racelog.RaceLogEvent;
@@ -10,77 +10,77 @@ import com.sap.sailing.racecommittee.app.domain.racelog.PassAwareRaceLog;
import com.sap.sailing.racecommittee.app.logging.ExLog;
public class PassAwareRaceLogImpl extends RaceLogImpl implements PassAwareRaceLog {
private static final String TAG = PassAwareRaceLogImpl.class.getName();
private static final long serialVersionUID = -1252381252528365834L;
private static final String ReadWriteLockName = PassAwareRaceLogImpl.class.getName() + ".lock";
public static final int DefaultPassId = 0;
private int currentPassId;
public PassAwareRaceLogImpl(RaceLog raceLog) {
this();
for (RaceLogEvent event : raceLog.getRawFixes()) {
this.add(event);
}
}
public PassAwareRaceLogImpl() {
this(DefaultPassId);
}
private static final String TAG = PassAwareRaceLogImpl.class.getName();
private static final long serialVersionUID = -1252381252528365834L;
private static final String ReadWriteLockName = PassAwareRaceLogImpl.class.getName() + ".lock";
public PassAwareRaceLogImpl(int currentPassId) {
super(ReadWriteLockName);
this.currentPassId = currentPassId;
}
public int getCurrentPassId() {
return currentPassId;
}
public void setCurrentPassId(int newPassId) {
if (newPassId != this.currentPassId) {
ExLog.i(TAG, String.format("Changing pass id to %d", newPassId));
this.currentPassId = newPassId;
}
}
@Override
public NavigableSet<RaceLogEvent> getFixes() {
lockForRead();
try {
return super.getFixes();
} finally {
unlockAfterRead();
}
}
@Override
protected NavigableSet<RaceLogEvent> getInternalFixes() {
public static final int DefaultPassId = 0;
private int currentPassId;
public PassAwareRaceLogImpl(RaceLog raceLog) {
this();
for (RaceLogEvent event : raceLog.getRawFixes()) {
this.add(event);
}
}
public PassAwareRaceLogImpl() {
this(DefaultPassId);
}
public PassAwareRaceLogImpl(int currentPassId) {
super(ReadWriteLockName);
this.currentPassId = currentPassId;
}
public int getCurrentPassId() {
return currentPassId;
}
public void setCurrentPassId(int newPassId) {
if (newPassId != this.currentPassId) {
ExLog.i(TAG, String.format("Changing pass id to %d", newPassId));
this.currentPassId = newPassId;
}
}
@Override
public NavigableSet<RaceLogEvent> getFixes() {
lockForRead();
try {
return super.getFixes();
} finally {
unlockAfterRead();
}
}
@Override
protected NavigableSet<RaceLogEvent> getInternalFixes() {
return new PartialNavigableSetView<RaceLogEvent>(super.getInternalFixes()) {
@Override
protected boolean isValid(RaceLogEvent e) {
return e.getPassId() == getCurrentPassId();
return e.getPassId() == getCurrentPassId();
}
};
}
@Override
public boolean add(RaceLogEvent event) {
boolean isAdded = false;
lockForWrite();
}
@Override
public boolean add(RaceLogEvent event) {
boolean isAdded = false;
lockForWrite();
try {
isAdded = getInternalRawFixes().add(event);
} finally {
unlockAfterWrite();
}
if (isAdded) {
ExLog.i(TAG, String.format("%s (%s) was added to log.", event, event.getClass().getName()));
setCurrentPassId(Math.max(event.getPassId(), this.currentPassId));
notifyListenersAboutReceive(event);
ExLog.i(TAG, String.format("%s (%s) was added to log.", event, event.getClass().getName()));
setCurrentPassId(Math.max(event.getPassId(), this.currentPassId));
notifyListenersAboutReceive(event);
} else {
ExLog.w(TAG, String.format("%s (%s) was not added to log. Ignoring", event, event.getClass().getName()));
ExLog.w(TAG, String.format("%s (%s) was not added to log. Ignoring", event, event.getClass().getName()));
}
return isAdded;
}
}
}
@@ -1,4 +1,4 @@
package com.sap.sailing.racecommittee.app.domain.state.impl;
package com.sap.sailing.racecommittee.app.domain.racelog.impl;
import com.sap.sailing.domain.racelog.RaceLogCourseAreaChangedEvent;
import com.sap.sailing.domain.racelog.RaceLogEventVisitor;
@@ -6,7 +6,7 @@ import com.sap.sailing.domain.racelog.RaceLogFlagEvent;
import com.sap.sailing.domain.racelog.RaceLogPassChangeEvent;
import com.sap.sailing.domain.racelog.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent;
import com.sap.sailing.racecommittee.app.domain.state.RaceLogChangedListener;
import com.sap.sailing.racecommittee.app.domain.racelog.RaceLogChangedListener;
public class RaceLogChangedVisitor implements RaceLogEventVisitor {
@@ -1,7 +1,5 @@
package com.sap.sailing.racecommittee.app.domain.state;
public interface RaceStateChangedListener {
void onRaceStateChanged(RaceState state);
}
@@ -14,7 +14,8 @@ import com.sap.sailing.domain.racelog.impl.RaceLogPassChangeEventImpl;
import com.sap.sailing.domain.racelog.impl.RaceLogRaceStatusEventImpl;
import com.sap.sailing.domain.racelog.impl.RaceLogStartTimeEventImpl;
import com.sap.sailing.racecommittee.app.domain.racelog.PassAwareRaceLog;
import com.sap.sailing.racecommittee.app.domain.state.RaceLogChangedListener;
import com.sap.sailing.racecommittee.app.domain.racelog.RaceLogChangedListener;
import com.sap.sailing.racecommittee.app.domain.racelog.impl.RaceLogChangedVisitor;
import com.sap.sailing.racecommittee.app.domain.state.RaceState;
import com.sap.sailing.racecommittee.app.domain.state.RaceStateChangedListener;
import com.sap.sailing.racecommittee.app.domain.state.impl.analyzers.RaceStatusAnalyzer;
@@ -5,17 +5,17 @@ import com.sap.sailing.domain.racelog.RaceLogEvent;
public abstract class RaceLogAnalyzer {
private RaceLog raceLog;
public RaceLogAnalyzer(RaceLog raceLog) {
this.raceLog = raceLog;
}
protected Iterable<RaceLogEvent> getPassEvents() {
return raceLog.getFixes();
}
protected Iterable<RaceLogEvent> getAllEvents() {
return raceLog.getRawFixes();
}
private RaceLog raceLog;
public RaceLogAnalyzer(RaceLog raceLog) {
this.raceLog = raceLog;
}
protected Iterable<RaceLogEvent> getPassEvents() {
return raceLog.getFixes();
}
protected Iterable<RaceLogEvent> getAllEvents() {
return raceLog.getRawFixes();
}
}
@@ -7,29 +7,27 @@ import com.sap.sailing.domain.racelog.RaceLogRaceStatusEvent;
import com.sap.sailing.racecommittee.app.logging.ExLog;
public class RaceStatusAnalyzer extends RaceLogAnalyzer {
private static final String TAG = RaceStatusAnalyzer.class.getName();
private static final String TAG = RaceStatusAnalyzer.class.getName();
public RaceStatusAnalyzer(RaceLog raceLog) {
super(raceLog);
}
public RaceLogRaceStatus getStatus() {
ExLog.i(TAG, String.format("Updating status..."));
RaceLogRaceStatus newStatus = RaceLogRaceStatus.UNSCHEDULED;
for (RaceLogEvent event : getPassEvents()) {
ExLog.i(TAG, String.format("Deciding on event of type %s.", event.getClass().getSimpleName()));
if (event instanceof RaceLogRaceStatusEvent) {
RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
ExLog.i(TAG, String.format("Decision to %s.", statusEvent.getNextStatus()));
newStatus = statusEvent.getNextStatus();
}
}
ExLog.i(TAG, String.format("Status will be set to %s.", newStatus));
return newStatus;
}
public RaceStatusAnalyzer(RaceLog raceLog) {
super(raceLog);
}
public RaceLogRaceStatus getStatus() {
ExLog.i(TAG, String.format("Updating status..."));
RaceLogRaceStatus newStatus = RaceLogRaceStatus.UNSCHEDULED;
for (RaceLogEvent event : getPassEvents()) {
ExLog.i(TAG, String.format("Deciding on event of type %s.", event.getClass().getSimpleName()));
if (event instanceof RaceLogRaceStatusEvent) {
RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
ExLog.i(TAG, String.format("Decision to %s.", statusEvent.getNextStatus()));
newStatus = statusEvent.getNextStatus();
}
}
ExLog.i(TAG, String.format("Status will be set to %s.", newStatus));
return newStatus;
}
}
@@ -7,18 +7,18 @@ import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent;
public class StartTimeFinder extends RaceLogAnalyzer {
public StartTimeFinder(RaceLog raceLog) {
super(raceLog);
}
public TimePoint getStartTime() {
TimePoint startTime = null;
for (RaceLogEvent event : getPassEvents()) {
if (event instanceof RaceLogStartTimeEvent) {
startTime = ((RaceLogStartTimeEvent) event).getStartTime();
}
}
return startTime;
}
public StartTimeFinder(RaceLog raceLog) {
super(raceLog);
}
public TimePoint getStartTime() {
TimePoint startTime = null;
for (RaceLogEvent event : getPassEvents()) {
if (event instanceof RaceLogStartTimeEvent) {
startTime = ((RaceLogStartTimeEvent) event).getStartTime();
}
}
return startTime;
}
}
@@ -0,0 +1,21 @@
package com.sap.sailing.racecommittee.app.services;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.state.RaceState;
import com.sap.sailing.racecommittee.app.domain.state.RaceStateChangedListener;
import com.sap.sailing.racecommittee.app.logging.ExLog;
public class RaceStateListener implements RaceStateChangedListener {
private final static String TAG = RaceStateListener.class.getName();
private final ManagedRace race;
public RaceStateListener(ManagedRace race) {
this.race = race;
}
public void onRaceStateChanged(RaceState state) {
ExLog.i(TAG, "Race state changed but service is ignoring it...");
}
}
@@ -0,0 +1,172 @@
package com.sap.sailing.racecommittee.app.services;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
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.IBinder;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogEventVisitor;
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.ReadonlyDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.racelog.impl.RaceLogChangedVisitor;
import com.sap.sailing.racecommittee.app.domain.state.RaceState;
import com.sap.sailing.racecommittee.app.domain.state.RaceStateChangedListener;
import com.sap.sailing.racecommittee.app.logging.ExLog;
import com.sap.sailing.racecommittee.app.services.sending.RaceEventSender;
import com.sap.sailing.racecommittee.app.ui.activities.LoginActivity;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.CompetitorIdJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.racelog.RaceLogEventSerializer;
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 UUID serviceId;
private AlarmManager alarmManager;
private ReadonlyDataManager dataManager;
private Map<ManagedRace, RaceLogEventVisitor> registeredLogListeners;
private Map<ManagedRace, RaceStateChangedListener> registeredStateListeners;
@Override
public void onCreate() {
this.serviceId = UUID.randomUUID();
this.alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
this.dataManager = DataManager.create(this);
this.registeredLogListeners = new HashMap<ManagedRace, RaceLogEventVisitor>();
this.registeredStateListeners = new HashMap<ManagedRace, RaceStateChangedListener>();
startForeground();
ExLog.i(TAG, "Started.");
super.onCreate();
}
@Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
ExLog.i(TAG, "Restarted.");
} else {
handleStartCommand(intent);
}
// We want this service to continue running until it is explicitly
// stopped, therefore return sticky.
return START_STICKY;
}
@Override
public void onDestroy() {
// Unregister all race listener...
for (Entry<ManagedRace, RaceLogEventVisitor> entry : registeredLogListeners.entrySet()) {
entry.getKey().getState().getRaceLog().removeListener(entry.getValue());
}
for (Entry<ManagedRace, RaceStateChangedListener> entry : registeredStateListeners.entrySet()) {
entry.getKey().getState().unregisterListener(entry.getValue());
}
// ... and remove from status bar!
stopForeground(true);
super.onDestroy();
}
private void startForeground() {
CharSequence text = getText(R.string.service_title);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.sap_sailing_app_icon, text, System.currentTimeMillis());
Intent intent = new Intent(this, LoginActivity.class);
// clear all activities above started and reuse if already existing!
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// The PendingIntent to launch our activity if the user selects this
// notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.service_info), text, contentIntent);
notification.flags |= Notification.FLAG_NO_CLEAR;
// Send the notification.
this.startForeground(42, notification);
}
private void handleStartCommand(Intent intent) {
String action = intent.getAction();
ExLog.i(TAG, String.format("Command action '%s' received.", action));
if (AppConstants.REGISTER_RACE_ACTION.equals(action)) {
handleRegisterRace(intent);
return;
}
}
private void handleRegisterRace(Intent intent) {
ExLog.i(TAG, "Registering race.");
ManagedRace race = getRaceFromIntent(intent);
if (race == null) {
ExLog.i(TAG, "Intent did not carry valid race information.");
return;
}
registerRace(race);
}
private ManagedRace getRaceFromIntent(Intent intent) {
if (intent.getExtras() == null || !intent.getExtras().containsKey(AppConstants.RACE_ID_KEY)) {
return null;
}
Serializable raceId = intent.getExtras().getSerializable(AppConstants.RACE_ID_KEY);
return dataManager.getDataStore().getRace(raceId);
}
private void registerRace(ManagedRace race) {
RaceState state = race.getState();
// Register on event additions...
JsonSerializer<RaceLogEvent> eventSerializer = RaceLogEventSerializer.create(new CompetitorIdJsonSerializer());
RaceEventSender sender = new RaceEventSender(this, eventSerializer, race);
RaceLogChangedVisitor logListener = new RaceLogChangedVisitor(sender);
state.getRaceLog().addListener(logListener);
// ... register on state changes!
RaceStateChangedListener stateListener = new RaceStateListener(race);
state.registerListener(stateListener);
this.registeredLogListeners.put(race, logListener);
this.registeredStateListeners.put(race, stateListener);
}
}
@@ -0,0 +1,167 @@
package com.sap.sailing.racecommittee.app.services.sending;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.logging.ExLog;
import com.sap.sailing.racecommittee.app.utils.FileHandlerUtils;
public class EventPersistenceManager {
private final static String delayedEventFileName = "delayedEvents.txt";
private final static String TAG = "EventPersistanceManager";
private Context context;
private String fileContent;
public EventPersistenceManager(Context context) {
this.context = context;
fileContent = "";
initializeFile();
}
public boolean areIntentsDelayed() {
return !fileContent.isEmpty();
}
public void persistIntent(Intent intent) {
Bundle extras = intent.getExtras();
String serializedEvent = extras.getString(AppConstants.EXTRAS_JSON_KEY);
String raceUuid = extras.getSerializable(AppConstants.RACE_ID_KEY).toString();
persistEvent(raceUuid, serializedEvent);
}
private void persistEvent(String raceUuid, String serializedEvent) {
String eventLine = String.format("%s;%s\n", raceUuid, serializedEvent);
ExLog.i(TAG, "Called to persist following event " + eventLine);
if (!fileContent.isEmpty()) {
if (fileContent.contains(eventLine)) {
ExLog.i(TAG, "The event to persist exists already in file");
return;
}
}
writeEventToFile(eventLine);
}
public void removeIntent(Intent intent) {
Bundle extras = intent.getExtras();
String serializedEvent = extras.getString(AppConstants.EXTRAS_JSON_KEY);
String raceUuid = extras.getString(AppConstants.RACE_ID_KEY);
removeEvent(raceUuid, serializedEvent);
}
private void removeEvent(String raceUuid, String serializedEvent) {
if (fileContent.isEmpty())
return;
ExLog.i(TAG, "removeEvent is called for " + raceUuid + ", serializedEvent " + serializedEvent);
String eventLine = String.format("%s;%s\n", raceUuid, serializedEvent);
removeEntryInFileContent(eventLine);
}
/**
* @param eventLine
*/
private void removeEntryInFileContent(String eventLine) {
if (fileContent.contains(eventLine)) {
fileContent = fileContent.replace(eventLine, "");
writeFileContentToFile();
ExLog.i(TAG, "Entry " + eventLine + " is removed from fileContent");
}
}
public int getEventCount() {
if (fileContent.isEmpty()) {
return 0;
}
return fileContent.split("\n").length;
}
public List<Intent> restoreEvents() {
List<Intent> delayedIntents = new ArrayList<Intent>();
if (!fileContent.isEmpty()) {
String[] eventLines = fileContent.split("\n");
for (String line : eventLines) {
String[] lineParts = line.split(";");
Intent eventIntent = EventSendingService.createEventIntent(context, lineParts[0], lineParts[1]);
delayedIntents.add(eventIntent);
}
}
ExLog.i(TAG, "Restored " + delayedIntents.size() + " events");
return delayedIntents;
}
/**
* @return
* @throws FileNotFoundException
*/
private String getFileContent() throws FileNotFoundException {
String fileContent = "";
FileInputStream inputStream;
try {
inputStream = context.openFileInput(delayedEventFileName);
fileContent = FileHandlerUtils.convertStreamToString(inputStream);
inputStream.close();
} catch (IOException e) {
ExLog.e(TAG, "In Method getFileContent(): " + e.getMessage() + " fileContent is empty");
}
return fileContent;
}
private void writeEventToFile(String eventLine) {
fileContent += eventLine;
writeFileContentToFile();
ExLog.i(TAG, "Wrote event to file: " + eventLine);
}
private void writeFileContentToFile() {
writeToFile(fileContent, Context.MODE_PRIVATE);
ExLog.i(TAG, "Wrote file content to file: " + fileContent);
}
private void initializeFile() {
try {
fileContent = getFileContent();
} catch (FileNotFoundException e) {
ExLog.w(TAG, "persistence file not found in internal storage. The file will be created.");
clearFileContent();
}
ExLog.i(TAG, "Initialized file");
}
private void clearFileContent() {
fileContent = "";
writeFileContentToFile();
}
private void writeToFile(String content, int mode) {
try {
FileOutputStream outputStream = context.openFileOutput(delayedEventFileName, mode);
outputStream.write(content.getBytes());
outputStream.close();
} catch (IOException e) {
ExLog.e(TAG, "In Method writeToFile: " + e.getMessage() + " with content " + content + " and mode " + mode);
}
}
}
@@ -0,0 +1,61 @@
package com.sap.sailing.racecommittee.app.services.sending;
import java.io.Serializable;
import java.net.URI;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Pair;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.data.http.HttpJsonPostRequest;
import com.sap.sailing.racecommittee.app.data.http.HttpRequest;
import com.sap.sailing.racecommittee.app.logging.ExLog;
public class EventSenderTask extends AsyncTask<Intent, Void, Pair<Intent, Integer>> {
public interface EventSendingListener {
public void onResult(Intent intent, boolean success);
}
private EventSendingListener listener;
public EventSenderTask(EventSendingListener listener) {
this.listener = listener;
}
@Override
protected Pair<Intent, Integer> doInBackground(Intent... params) {
Intent intent = params[0];
Bundle extras = intent.getExtras();
Serializable serializedEvent = extras.getSerializable(AppConstants.EXTRAS_JSON_KEY);
String url = extras.getString(AppConstants.EXTRAS_URL);
if (serializedEvent == null || url == null) {
return Pair.create(intent, -1);
}
try {
HttpRequest post = new HttpJsonPostRequest(URI.create(url), serializedEvent.toString());
try {
post.execute().close();
} finally {
post.disconnect();
}
ExLog.i(getClass().getName(), "Post request issued for the following Event: " + serializedEvent);
} catch (Exception e) {
ExLog.e(getClass().getName(), e.toString());
return Pair.create(intent, -1);
}
return Pair.create(intent, 0);
}
@Override
protected void onPostExecute(Pair<Intent, Integer> resultPair) {
super.onPostExecute(resultPair);
listener.onResult(resultPair.first, resultPair.second == 0);
}
}
@@ -0,0 +1,205 @@
package com.sap.sailing.racecommittee.app.services.sending;
import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.data.DataManager;
import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.logging.ExLog;
import com.sap.sailing.racecommittee.app.services.sending.EventSenderTask.EventSendingListener;
/*
* Sending an event to the webservice
*
* Usage:
*
* Intent i = new Intent(AppConstants.SEND_EVENT_ACTION);
* i.putExtra(AppConstants.EXTRAS_JSON_KEY, JsonUtils.getObjectAsString('someEventObject'));
* i.putExtra(AppConstants.RACE_UUID_KEY, 'raceuuid');
* context.startService(i);
*/
public class EventSendingService extends Service implements EventSendingListener {
protected final static String TAG = "EventSendingService";
private ConnectivityManager connectivityManager;
private Handler handler;
private final IBinder mBinder = new EventSendingBinder();
private EventPersistenceManager persistenceManager;
private boolean isHandlerSet;
public class EventSendingBinder extends Binder {
public EventSendingService getService() {
return EventSendingService.this;
}
}
@Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
private Date lastSuccessfulSend;
public int getDelayedIntentsCount() {
return persistenceManager.getEventCount();
}
public Date getLastSuccessfulSend() {
return lastSuccessfulSend;
}
/**
* creates an intent that contains the event to be sent and the race id which shall be sent to the backend
*
* @param context
* the context of the app
* @param race
* the race for which the event was created
* @param serializedEvent
* the event serizalized to JSON
* @return the intent that shall be sent to the EventSendingService
*/
public static Intent createEventIntent(Context context, ManagedRace race, Serializable serializedEvent) {
String url = String.format(
"%s/rc/racelog?leaderboard=%s&raceColumn=%s&fleet=%s",
AppConstants.getServerBaseURL(context),
race.getRaceGroup().getName(),
race.getName(),
race.getFleet().getName());
Intent eventIntent = new Intent(AppConstants.SEND_EVENT_ACTION);
eventIntent.putExtra(AppConstants.RACE_ID_KEY, race.getId());
eventIntent.putExtra(AppConstants.EXTRAS_JSON_KEY, serializedEvent);
eventIntent.putExtra(AppConstants.EXTRAS_URL, url);
ExLog.i(TAG, "Created event " + eventIntent + " for sending to backend");
return eventIntent;
}
public static Intent createEventIntent(Context context, Serializable raceId, Serializable serializedEvent) {
ReadonlyDataManager data = DataManager.create(context);
ManagedRace race = data.getDataStore().getRace(raceId);
if (race == null) {
ExLog.e(TAG, "There is no race with id " + raceId);
return null;
}
return createEventIntent(context, race, serializedEvent);
}
/*
* (non-Javadoc)
*
* @see android.app.Service#onCreate()
*/
@Override
public void onCreate() {
connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
persistenceManager = new EventPersistenceManager(this);
handler = new Handler();
isHandlerSet = false;
if (persistenceManager.areIntentsDelayed()) {
handleDelayedEvents();
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
ExLog.i(TAG, "Service is restarted.");
return START_STICKY;
}
ExLog.i(TAG, "Service is called by following intent: " + intent.getAction());
handleCommand(intent, startId);
// 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) {
String action = intent.getAction();
if (action.equals(AppConstants.SEND_SAVED_INTENTS_ACTION)) {
handleDelayedEvents();
} else if (action.equals(AppConstants.SEND_EVENT_ACTION)) {
handleSendEvents(intent);
}
}
private void handleSendEvents(Intent intent) {
if (!isConnected()) {
persistenceManager.persistIntent(intent);
//ConnectivityChangedReceiver.enable(this);
} else {
sendEvent(intent);
}
}
private void handleDelayedEvents() {
isHandlerSet = false;
if (!isConnected())
return;
sendDelayedEvents();
}
private void sendDelayedEvents() {
List<Intent> delayedIntents = persistenceManager.restoreEvents();
ExLog.i(TAG, String.format("Trying to resend %d waiting events", delayedIntents.size()));
for (Intent intent : delayedIntents)
sendEvent(intent);
}
private void sendEvent(Intent intent) {
if (!AppConstants.isSendingActive(this)) {
ExLog.i(TAG, "Sending deactivated. Event will not be sent to server.");
} else {
EventSenderTask task = new EventSenderTask(this);
task.execute(intent);
}
}
public void onResult(Intent intent, boolean success) {
if (!success) {
ExLog.w(TAG, "Could not POST intent to server.");
persistenceManager.persistIntent(intent);
if (!isHandlerSet) {
SendDelayedEventsCaller delayedCaller = new SendDelayedEventsCaller(this);
handler.postDelayed(delayedCaller, 1000 * 60 * 1);
isHandlerSet = true;
}
} else {
ExLog.i(TAG, "Event successfully send. " + intent.getStringExtra(AppConstants.EXTRAS_JSON_KEY));
persistenceManager.removeIntent(intent);
lastSuccessfulSend = Calendar.getInstance().getTime();
}
}
/**
* checks if there is network connectivity
*
* @return connectivity check value
*/
private boolean isConnected() {
NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
if (activeNetwork == null)
return false;
boolean isConnected = activeNetwork.isConnected();
return isConnected;
}
}
@@ -0,0 +1,30 @@
package com.sap.sailing.racecommittee.app.services.sending;
import java.io.Serializable;
import android.app.Service;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sailing.racecommittee.app.domain.racelog.RaceLogChangedListener;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public class RaceEventSender implements RaceLogChangedListener {
private final Service service;
private final JsonSerializer<RaceLogEvent> serializer;
private final ManagedRace race;
public RaceEventSender(Service context, JsonSerializer<RaceLogEvent> serializer, ManagedRace race) {
this.service = context;
this.serializer = serializer;
this.race = race;
}
public void eventAdded(RaceLogEvent event) {
Serializable serializedEvent = serializer.serialize(event);
service.startService(EventSendingService.createEventIntent(service, race, serializedEvent));
}
}
@@ -0,0 +1,23 @@
package com.sap.sailing.racecommittee.app.services.sending;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.logging.ExLog;
import android.content.Context;
import android.content.Intent;
public class SendDelayedEventsCaller implements Runnable {
private final static String TAG = "SendDelayedEventsCaller";
private Context context;
public SendDelayedEventsCaller(Context context) {
this.context = context;
}
public void run() {
ExLog.i(TAG, "The Event Sending Service is called to send possibly delayed intents");
Intent sendSavedIntent = new Intent(AppConstants.SEND_SAVED_INTENTS_ACTION);
context.startService(sendSavedIntent);
}
}
@@ -128,7 +128,6 @@ public class RacingActivity extends TwoPaneActivity implements RaceInfoListener
dataManager.loadRaces(courseArea.getId(), new LoadClient<Collection<ManagedRace>>() {
public void onLoadSucceded(Collection<ManagedRace> data) {
onLoadRacesSucceded(courseArea, data);
}
public void onLoadFailed(Exception reason) {
@@ -0,0 +1,39 @@
package com.sap.sailing.racecommittee.app.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import com.sap.sailing.racecommittee.app.logging.ExLog;
public class FileHandlerUtils {
private final static String TAG = FileHandlerUtils.class.getName();
public static String convertStreamToString(InputStream inputStream) {
/*
* To convert the InputStream to String we use the BufferedReader.readLine() method. We iterate until the
* BufferedReader return null which means there's no more data to read. Each line will appended to a
* StringBuilder and returned as String.
*/
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
stringBuilder.append(line + "\n");
}
} catch (IOException e) {
ExLog.e(TAG, "In Method convertStreamToString while converting stream to string: " + e.getMessage());
} finally {
try {
inputStream.close();
} catch (IOException e) {
ExLog.e(TAG, "In Method convertStreamToString while closing the input stream: " + e.getMessage());
}
}
return stringBuilder.toString();
}
}