Merge branch 'android_apps_tracking' into android_apps

This commit is contained in:
Peter Siegmund
2015-01-15 15:32:10 +01:00
76 changed files with 1447 additions and 2096 deletions
@@ -44,4 +44,9 @@ public class HttpJsonPostRequest extends HttpRequest {
outputStream.write(requestBody.getBytes(Charset.forName("UTF-8")));
}
}
public String getRequestBody()
{
return requestBody;
}
}
@@ -53,6 +53,15 @@ public abstract class HttpRequest {
this.isCancelled = false;
this.context = context;
}
public String getUrlAsString()
{
if (url != null)
{
return url.toString();
}
return null;
}
public boolean isCancelled() {
return isCancelled;
@@ -1,7 +1,7 @@
/**
*
*/
package com.sap.sailing.android.tracking.app.services.sending;
package com.sap.sailing.android.shared.services.sending;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -12,8 +12,6 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.utils.ServiceHelper;
/**
* Informs {@link MessageSendingService} whenever connectivity is restored, so that it can start sending
@@ -21,7 +19,7 @@ import com.sap.sailing.android.tracking.app.utils.ServiceHelper;
*
* Register in manifest:
* <pre>{@code
* <receiver android:name="com.sap.sailing.android.tracking.app.services.sending.ConnectivityChangedReceiver" >
* <receiver android:name="com.sap.sailing.android.shared.services.sending.ConnectivityChangedReceiver" >
* <intent-filter>
* <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
* </intent-filter>
@@ -31,29 +29,17 @@ import com.sap.sailing.android.tracking.app.utils.ServiceHelper;
public class ConnectivityChangedReceiver extends BroadcastReceiver {
private final static String TAG = ConnectivityChangedReceiver.class.getName();
/* (non-Javadoc)
* @see android.content.BroadcastReceiver#onReceive(android.content.Context, android.content.Intent)
*/
@Override
public void onReceive(Context context, Intent intent) {
final ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo networkInfo = connManager.getActiveNetworkInfo();
NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (!networkInfo.isConnected())
return;
context.startService(MessageSendingService.createSendDelayedIntent(context));
if (networkInfo == null)
{
return; // was null when restarted router..
}
if (!networkInfo.isConnected()) {
return;
}
if (BuildConfig.DEBUG) {
ExLog.i(context, TAG, "Starting TransmittingService");
}
ServiceHelper.getInstance().startTransmittingService(context);
disable(context);
}
@@ -78,4 +64,5 @@ public class ConnectivityChangedReceiver extends BroadcastReceiver {
packageManager.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
ExLog.w(context, TAG, "Connectivity lost. ConnectivityChangedReceiver enabled");
}
}
@@ -1,4 +1,4 @@
package com.sap.sailing.android.tracking.app.services.sending;
package com.sap.sailing.android.shared.services.sending;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -14,7 +14,6 @@ import android.content.Intent;
import android.os.Bundle;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract;
import com.sap.sailing.android.shared.util.FileHandlerUtils;
public class MessagePersistenceManager {
@@ -1,4 +1,4 @@
package com.sap.sailing.android.tracking.app.services.sending;
package com.sap.sailing.android.shared.services.sending;
import java.io.IOException;
import java.io.InputStream;
@@ -68,5 +68,4 @@ public class MessageSenderTask extends AsyncTask<Intent, Void, Util.Triple<Inten
super.onPostExecute(resultTriple);
listener.onMessageSent(resultTriple.getA(), resultTriple.getB(), resultTriple.getC());
}
}
@@ -1,7 +1,8 @@
package com.sap.sailing.android.tracking.app.services.sending;
package com.sap.sailing.android.shared.services.sending;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Calendar;
import java.util.Date;
@@ -24,9 +25,9 @@ import android.os.IBinder;
import com.sap.sailing.android.shared.R;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessagePersistenceManager.MessageRestorer;
import com.sap.sailing.android.shared.services.sending.MessageSenderTask.MessageSendingListener;
import com.sap.sailing.android.shared.util.PrefUtils;
import com.sap.sailing.android.tracking.app.services.sending.MessagePersistenceManager.MessageRestorer;
import com.sap.sailing.android.tracking.app.services.sending.MessageSenderTask.MessageSendingListener;
import com.sap.sailing.domain.common.racelog.RaceLogServletConstants;
/**
@@ -34,17 +35,16 @@ import com.sap.sailing.domain.common.racelog.RaceLogServletConstants;
* by buffering the messages in a file, so that they can be sent when the connection is
* re-established.<br>
*
* <b>Use in the following way:</b> Add the service declaration to your {@code AndroidManifest.xml}, and also specify
* your class implementing the {@link MessagePersistenceManager.MessageRestorer} as a meta-data tag with the key
* {@code com.sap.sailing.android.tracking.app..services.sending.messageRestorer}. Also refer to
* {@link ConnectivityChangedReceiver}, which has to be registered as well. For example:
*
* <pre>
* {@code
* <b>Use in the following way:</b> Add the service declaration to your {@code AndroidManifest.xml},
* and also specify your class implementing the {@link MessagePersistenceManager.MessageRestorer}
* as a meta-data tag with the key {@code com.sap.sailing.android.shared.services.sending.messageRestorer}.
* Also refer to {@link ConnectivityChangedReceiver}, which has to be registered as well.
* For example:
* <pre>{@code
* <service
* android:name="com.sap.sailing.android.tracking.app.services.sending.MessageSendingService"
* android:name="com.sap.sailing.android.shared.services.sending.MessageSendingService"
* android:exported="false" >
* <meta-data android:name="com.sap.sailing.android.tracking.app.services.sending.messageRestorer"
* <meta-data android:name="com.sap.sailing.android.shared.services.sending.messageRestorer"
* android:value="com.sap.sailing.racecommittee.app.services.sending.EventRestorer" />
* </service>
* }</pre>
@@ -55,8 +55,6 @@ import com.sap.sailing.domain.common.racelog.RaceLogServletConstants;
* context.startService(MessageSendingService.createMessageIntent(
* context, url, race.getId(), eventId, serializedEventAsJson, callbackClass));
* }</pre>
*
* NOTE: Started using TransmittingService.java, perhaps this class can be dismissed? -LZ
*/
public class MessageSendingService extends Service implements MessageSendingListener {
public final static String URL = "url";
@@ -75,6 +73,8 @@ public class MessageSendingService extends Service implements MessageSendingList
private Set<Serializable> suppressedMessageIds = new HashSet<Serializable>();
private APIConnectivityListener apiConnectivityListener;
public void registerMessageForSuppression(Serializable messageId) {
suppressedMessageIds.add(messageId);
}
@@ -130,7 +130,8 @@ public class MessageSendingService extends Service implements MessageSendingList
serviceLogger.onMessageSentSuccessful();
}
public static Intent createMessageIntent(Context context, String url, Serializable callbackPayload, Serializable messageId, String payload,
public static Intent createMessageIntent(Context context, String url,
Serializable callbackPayload, Serializable messageId, String payload,
Class<? extends ServerReplyCallback> callbackClass) {
Intent messageIntent = new Intent(context, MessageSendingService.class);
messageIntent.setAction(context.getString(R.string.intent_send_message));
@@ -231,6 +232,7 @@ public class MessageSendingService extends Service implements MessageSendingList
persistenceManager.persistIntent(intent);
ConnectivityChangedReceiver.enable(this);
serviceLogger.onMessageSentFailed();
reportApiConnectivity(APIConnectivity.notReachable);
} else {
sendMessage(intent);
}
@@ -259,7 +261,7 @@ public class MessageSendingService extends Service implements MessageSendingList
private void sendMessage(Intent intent) {
boolean sendingActive = PrefUtils.getBoolean(this, R.string.preference_isSendingActive_key,
R.bool.preference_isSendingActive_default);
if (! sendingActive) {
if (!sendingActive) {
ExLog.i(this, TAG, "Sending deactivated. Message will not be sent to server.");
} else {
Serializable messageId = getMessageId(intent);
@@ -275,9 +277,12 @@ public class MessageSendingService extends Service implements MessageSendingList
@Override
public void onMessageSent(Intent intent, boolean success, InputStream inputStream) {
ExLog.i(this, "MS", "on message sent");
int resendMillis = PrefUtils.getInt(this, R.string.preference_messageResendIntervalMillis_key,
R.integer.preference_messageResendIntervalMillis_default);
if (!success) {
ExLog.i(this, "MS", "success");
reportApiConnectivity(APIConnectivity.transmissionError);
ExLog.w(this, TAG, "Error while posting intent to server. Will persist intent...");
persistenceManager.persistIntent(intent);
if (!isHandlerSet) {
@@ -285,8 +290,11 @@ public class MessageSendingService extends Service implements MessageSendingList
handler.postDelayed(delayedCaller, resendMillis); // after 30 sec, try the sending again
isHandlerSet = true;
}
reportUnsentGPSFixesCount();
serviceLogger.onMessageSentFailed();
} else {
ExLog.i(this, "MS", "!success");
reportApiConnectivity(APIConnectivity.transmissionSuccess);
ExLog.i(this, TAG, "Message successfully sent.");
if (persistenceManager.areIntentsDelayed()) {
persistenceManager.removeIntent(intent);
@@ -308,6 +316,8 @@ public class MessageSendingService extends Service implements MessageSendingList
String raceId = intent.getStringExtra(CALLBACK_PAYLOAD);
callback.processResponse(this, inputStream, raceId);
}
ExLog.i(this, "MS", "report");
reportUnsentGPSFixesCount() ;
}
}
@@ -331,14 +341,87 @@ public class MessageSendingService extends Service implements MessageSendingList
public static String getRaceLogEventSendAndReceiveUrl(Context context, final String raceGroupName,
final String raceName, final String fleetName) {
String url = String.format("%s/sailingserver/rc/racelog?"+
RaceLogServletConstants.PARAMS_LEADERBOARD_NAME+"=%s&"+
RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME+"=%s&"+
RaceLogServletConstants.PARAMS_RACE_FLEET_NAME+"=%s&"+
RaceLogServletConstants.PARAMS_CLIENT_UUID+"=%s",
PrefUtils.getString(context, R.string.preference_server_url_key, R.string.preference_server_url_default),
URLEncoder.encode(raceGroupName),
URLEncoder.encode(raceName), URLEncoder.encode(fleetName), uuid);
String url = null;
try {
url = String.format("%s/sailingserver/rc/racelog?"+
RaceLogServletConstants.PARAMS_LEADERBOARD_NAME+"=%s&"+
RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME+"=%s&"+
RaceLogServletConstants.PARAMS_RACE_FLEET_NAME+"=%s&"+
RaceLogServletConstants.PARAMS_CLIENT_UUID+"=%s",
PrefUtils.getString(context, R.string.preference_server_url_key, R.string.preference_server_url_default),
URLEncoder.encode(raceGroupName, "UTF-8"),
URLEncoder.encode(raceName,"UTF-8"),
URLEncoder.encode(fleetName, "UTF-8"), uuid);
} catch (UnsupportedEncodingException e) {
ExLog.e(context, TAG, "UnsupportedEncodingException: " + e.getLocalizedMessage());
}
return url;
}
/**
* Register listener for API-connectivity
*
* @param listener class that wants to be notified of api-connectivity changes
*/
public void registerAPIConnectivityListener(APIConnectivityListener listener) {
apiConnectivityListener = listener;
}
/**
* Unregister listener for API-connectivity
*/
public void unregisterAPIConnectivityListener() {
apiConnectivityListener = null;
}
/**
* Enum for reporting of network connectivity.
*/
public enum APIConnectivity {
notReachable(0),
transmissionSuccess(1),
transmissionError(2),
noAttempt(4);
private final int apiConnectivity;
APIConnectivity(int connectivity) {
this.apiConnectivity = connectivity;
}
public int toInt() {
return this.apiConnectivity;
}
}
/**
* Listener interface for reporting of connectivity and number of
* unsent GPS-fixes.
*/
public interface APIConnectivityListener {
public void apiConnectivityUpdated(APIConnectivity apiConnectivity);
public void setUnsentGPSFixesCount(int count);
}
/**
* Report API connectivity to listening activity
*
* @param apiConnectivity
*/
private void reportApiConnectivity(APIConnectivity apiConnectivity) {
if (apiConnectivityListener != null) {
apiConnectivityListener.apiConnectivityUpdated(apiConnectivity);
}
}
/**
* Report the number of currently unsent GPS-fixes
*
* @param unsentGPSFixesCount
*/
private void reportUnsentGPSFixesCount() {
if (apiConnectivityListener != null) {
apiConnectivityListener.setUnsentGPSFixesCount(getDelayedIntentsCount());
}
}
}
@@ -1,4 +1,4 @@
package com.sap.sailing.android.tracking.app.services.sending;
package com.sap.sailing.android.shared.services.sending;
import android.content.Context;
@@ -1,4 +1,4 @@
package com.sap.sailing.android.tracking.app.services.sending;
package com.sap.sailing.android.shared.services.sending;
import java.io.InputStream;
@@ -0,0 +1,140 @@
package com.sap.sailing.android.shared.ui.activities;
import java.util.Date;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;
import com.sap.sailing.android.shared.R;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessageSendingService;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.MessageSendingBinder;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.MessageSendingServiceLogger;
import com.sap.sailing.android.shared.util.PrefUtils;
public abstract class SendingServiceAwareActivity extends ResilientActivity {
private class MessageSendingServiceConnection implements ServiceConnection, MessageSendingServiceLogger {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
MessageSendingBinder binder = (MessageSendingBinder) service;
sendingService = binder.getService();
boundSendingService = true;
sendingService.setMessageSendingServiceLogger(this);
updateSendingServiceInformation();
}
@Override
public void onServiceDisconnected(ComponentName arg) {
boundSendingService = false;
}
@Override
public void onMessageSentSuccessful() {
updateSendingServiceInformation();
}
@Override
public void onMessageSentFailed() {
updateSendingServiceInformation();
}
}
private static final String TAG = SendingServiceAwareActivity.class.getName();
protected MenuItem menuItemLive;
protected int menuItemLiveId = -1;
protected boolean boundSendingService = false;
protected MessageSendingService sendingService;
private MessageSendingServiceConnection sendingServiceConnection;
private String sendingServiceStatus = "";
public SendingServiceAwareActivity() {
this.sendingServiceConnection = new MessageSendingServiceConnection();
}
@Override
public void onStart() {
super.onStart();
Intent intent = new Intent(this, MessageSendingService.class);
bindService(intent, sendingServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
public void onStop() {
super.onStop();
if (boundSendingService) {
unbindService(sendingServiceConnection);
boundSendingService = false;
}
}
protected void updateSendingServiceInformation() {
if (menuItemLive == null)
return;
if (!boundSendingService)
return;
int errorCount = this.sendingService.getDelayedIntentsCount();
if (errorCount > 0) {
menuItemLive.setIcon(R.drawable.ic_menu_share_red);
Date lastSuccessfulSend = this.sendingService.getLastSuccessfulSend();
sendingServiceStatus = String.format("Currently %d events waiting to be sent.\nLast successful sent was at %s",
errorCount, lastSuccessfulSend == null ? "never" : lastSuccessfulSend);
} else {
menuItemLive.setIcon(R.drawable.ic_menu_share);
sendingServiceStatus = String.format("Currently no event waiting to be sent.", errorCount);
}
}
/**
* @return the resource ID for the options menu, {@code 0} if none.
* The menu item displaying the connection status is added automatically.
*/
protected abstract int getOptionsMenuResId();
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu_live_status, menu);
menuItemLive = menu.findItem(R.id.options_menu_live);
if (getOptionsMenuResId() != 0) {
inflater.inflate(getOptionsMenuResId(), menu);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (R.id.options_menu_live == item.getItemId()) {
ExLog.i(this, TAG, "Clicked LIVE.");
Toast.makeText(this, getLiveIconText(), Toast.LENGTH_LONG).show();
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
updateSendingServiceInformation();
return super.onPrepareOptionsMenu(menu);
}
private String getLiveIconText() {
return String.format("Connected to: %s\n%s", PrefUtils.getString(this, R.string.preference_server_url_key,
R.string.preference_server_url_default), sendingServiceStatus);
}
}
@@ -0,0 +1,44 @@
package com.sap.sailing.android.tracking.app.test;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.utils.DatabaseHelper;
import android.app.Activity;
import android.content.Intent;
import android.support.v4.app.FragmentManager;
import android.view.ContextThemeWrapper;
public class DatabaseCallsTest extends ActivityUnitTestCase<Activity> {
private Intent mStartIntent;
public DatabaseCallsTest() {
super(Activity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
mStartIntent = new Intent(Intent.ACTION_MAIN);
startActivity(mStartIntent, null, null);
DatabaseTestHelper.deleteAllEventsFromDB(getActivity());
}
public void testDeleteRegatta()
{
DatabaseTestHelper.createNewRegattaInDBAndReturnEventRowId(getActivity(), "TEST-EVENT",
"123-456-789", "TEST-LEADERBOARD", "TEST-COMPETITOR", "TEST-DIGEST1");
DatabaseTestHelper.createNewRegattaInDBAndReturnEventRowId(getActivity(), "TEST-EVENT 2",
"234-567-890", "TEST-LEADERBOARD 2", "TEST-COMPETITOR 2", "TEST-DIGEST2");
DatabaseHelper.getInstance().deleteRegattaFromDatabase(getActivity(), "TEST-DIGEST1");
assertEquals(1, DatabaseTestHelper.getNumberOfEventsFromDB(getActivity()));
//TODO: Continue, ensure context is not null
}
}
@@ -10,8 +10,10 @@ import android.database.Cursor;
import android.net.Uri;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Competitor;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.EventGpsFixesJoined;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Leaderboard;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.SensorGps;
import com.sap.sailing.android.tracking.app.valueobjects.GpsFix;
@@ -98,14 +100,57 @@ public class DatabaseTestHelper {
cr.insert(SensorGps.CONTENT_URI, cv);
}
public static long createNewEventInDBAndReturnItsId(Context context, String eventName, String eventId)
public static long createNewEventInDBAndReturnItsId(Context context, String eventName, String eventId, String checkinDigest)
{
ContentResolver cr = context.getContentResolver();
ContentValues cv = new ContentValues();
cv.put(Event.EVENT_NAME, eventName);
cv.put(Event.EVENT_ID, eventId);
cv.put(Event.EVENT_SERVER, "127.0.0.1");
cv.put(Event.EVENT_CHECKIN_DIGEST, checkinDigest);
Uri uri = cr.insert(Event.CONTENT_URI, cv);
return ContentUris.parseId(uri);
}
public static long createNewRegattaInDBAndReturnEventRowId(Context context, String eventName,
String eventId, String leaderboardName, String competitorName, String checkinDigest) {
long leaderboardRowId = createNewLeaderboardInDBAndReturnItsId(context, leaderboardName, checkinDigest);
long eventRowId = createNewEventInDBAndReturnItsId(context, eventName, eventId, leaderboardRowId, checkinDigest);
createNewCompetitorInDBAndReturnItsId(context, competitorName, leaderboardRowId, checkinDigest);
return eventRowId;
}
private static long createNewEventInDBAndReturnItsId(Context context, String eventName, String eventId, long leaderboardRowId, String checkinDigest)
{
ContentResolver cr = context.getContentResolver();
ContentValues cv = new ContentValues();
cv.put(Event.EVENT_NAME, eventName);
cv.put(Event.EVENT_ID, eventId);
cv.put(Event.EVENT_SERVER, "127.0.0.1");
cv.put(Event.EVENT_CHECKIN_DIGEST, checkinDigest);
Uri uri = cr.insert(Event.CONTENT_URI, cv);
return ContentUris.parseId(uri);
}
private static long createNewLeaderboardInDBAndReturnItsId(Context context, String leaderboardName, String checkinDigest)
{
ContentResolver cr = context.getContentResolver();
ContentValues cv = new ContentValues();
cv.put(Leaderboard.LEADERBOARD_NAME, leaderboardName);
cv.put(Leaderboard.LEADERBOARD_CHECKIN_DIGEST, checkinDigest);
Uri uri = cr.insert(Leaderboard.CONTENT_URI, cv);
return ContentUris.parseId(uri);
}
private static long createNewCompetitorInDBAndReturnItsId(Context context,
String competitorDisplayName, long leaderboardRowId, String checkinDigest) {
ContentResolver cr = context.getContentResolver();
ContentValues cv = new ContentValues();
cv.put(Competitor.COMPETITOR_DISPLAY_NAME, competitorDisplayName);
cv.put(Competitor.COMPETITOR_CHECKIN_DIGEST, checkinDigest);
Uri uri = cr.insert(Competitor.CONTENT_URI, cv);
return ContentUris.parseId(uri);
}
}
@@ -22,7 +22,7 @@ import com.sap.sailing.android.tracking.app.valueobjects.GpsFix;
public class TrackingServiceTest extends ServiceTestCase<TrackingService> {
static final String TAG = TrackingServiceTest.class.getName();
final String eventId = "test123";
final String checkinDigest = "testDigest123";
long eventRowId;
private ServiceHelperTestable serviceHelperSpy;
@@ -37,8 +37,7 @@ public class TrackingServiceTest extends ServiceTestCase<TrackingService> {
System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
DatabaseTestHelper.deleteAllGpsFixesFromDB(getContext());
DatabaseTestHelper.deleteAllEventsFromDB(getContext());
eventRowId = DatabaseTestHelper.createNewEventInDBAndReturnItsId(getContext(), "test123",
eventId);
eventRowId = DatabaseTestHelper.createNewEventInDBAndReturnItsId(getContext(), "test123", "123-456", checkinDigest);
serviceHelperSpy = Mockito.mock(ServiceHelperTestable.class);
ServiceHelperTestable.injectInstance(serviceHelperSpy);
@@ -52,8 +51,7 @@ public class TrackingServiceTest extends ServiceTestCase<TrackingService> {
private void startService() {
Intent startIntent = new Intent();
startIntent.setClass(getContext(), TrackingService.class);
startIntent.putExtra(getContext().getString(R.string.tracking_service_event_id_parameter),
eventId);
startIntent.putExtra(getContext().getString(R.string.tracking_service_checkin_digest_parameter), checkinDigest);
startIntent.setAction(getContext().getString(R.string.tracking_service_start));
startService(startIntent);
}
@@ -12,13 +12,15 @@ import android.content.Context;
import android.content.Intent;
import android.test.ServiceTestCase;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.sap.sailing.android.shared.data.http.HttpJsonPostRequest;
import com.sap.sailing.android.shared.data.http.HttpRequest;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.services.TrackingService;
import com.sap.sailing.android.tracking.app.services.TransmittingService;
import com.sap.sailing.android.tracking.app.test.extensions.DatabaseHelperTestable;
import com.sap.sailing.android.tracking.app.test.extensions.VolleyHelperTestable;
import com.sap.sailing.android.tracking.app.test.extensions.NetworkHelperTestable;
import com.sap.sailing.android.tracking.app.utils.NetworkHelper.NetworkHelperFailureListener;
import com.sap.sailing.android.tracking.app.utils.NetworkHelper.NetworkHelperSuccessListener;
import com.sap.sailing.android.tracking.app.valueobjects.GpsFix;
public class TransmittingServiceTest extends ServiceTestCase<TransmittingService> {
@@ -26,7 +28,7 @@ public class TransmittingServiceTest extends ServiceTestCase<TransmittingService
static final String TAG = TransmittingServiceTest.class.getName();
final String eventId = "test123";
private VolleyHelperTestable volleyHelperSpy;
private NetworkHelperTestable networkHelperSpy;
private DatabaseHelperTestable databaseHelperMock;
public TransmittingServiceTest() {
@@ -40,11 +42,10 @@ public class TransmittingServiceTest extends ServiceTestCase<TransmittingService
DatabaseTestHelper.deleteAllEventsFromDB(getContext());
DatabaseTestHelper.deleteAllGpsFixesFromDB(getContext());
if (volleyHelperSpy == null)
if (networkHelperSpy == null)
{
VolleyHelperTestable.injectInstance(null, null);
volleyHelperSpy = Mockito.spy(new VolleyHelperTestable(getContext()));
VolleyHelperTestable.injectInstance(getContext(), volleyHelperSpy);
networkHelperSpy = Mockito.spy(new NetworkHelperTestable());
NetworkHelperTestable.injectInstance(getContext(), networkHelperSpy);
}
if (databaseHelperMock == null)
@@ -71,8 +72,8 @@ public class TransmittingServiceTest extends ServiceTestCase<TransmittingService
public void testTransmittingServiceTransmitsGpsFix() throws InterruptedException, JSONException {
long timestamp = (new Date()).getTime();
ArgumentCaptor<JSONObject> jsonObjectCaptor = ArgumentCaptor.forClass(JSONObject.class);
ArgumentCaptor<String> urlCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<HttpJsonPostRequest> requestCaptor = ArgumentCaptor.forClass(HttpJsonPostRequest.class);
//ArgumentCaptor<String> urlCaptor = ArgumentCaptor.forClass(String.class);
ArrayList<GpsFix> list = new ArrayList<GpsFix>();
GpsFix fix = new GpsFix();
@@ -91,12 +92,12 @@ public class TransmittingServiceTest extends ServiceTestCase<TransmittingService
startService();
Thread.sleep(3500);
Mockito.verify(networkHelperSpy, Mockito.times(1)).executeHttpJsonRequestAsnchronously(requestCaptor.capture(),
(NetworkHelperSuccessListener)Mockito.any(), (NetworkHelperFailureListener)Mockito.any());
Mockito.verify(volleyHelperSpy, Mockito.times(1)).enqueueRequest(
urlCaptor.capture(), jsonObjectCaptor.capture(),
(Listener<JSONObject>)Mockito.any(), Mockito.any(ErrorListener.class));
JSONObject json = jsonObjectCaptor.getValue();
JSONObject json = new JSONObject(requestCaptor.getValue().getRequestBody());
assertEquals(1, json.getJSONArray("fixes").length());
JSONObject jsonFix = (JSONObject)json.getJSONArray("fixes").get(0);
@@ -106,7 +107,7 @@ public class TransmittingServiceTest extends ServiceTestCase<TransmittingService
assertEquals(14, jsonFix.getLong("speed"));
assertEquals(101.5, jsonFix.getDouble("course"));
assertEquals("http://127.0.0.1/sailingserver/api/v1/gps_fixes", urlCaptor.getValue());
assertEquals("http://127.0.0.1/sailingserver/api/v1/gps_fixes", requestCaptor.getValue().getUrlAsString());
shutdownService();
}
@@ -0,0 +1,36 @@
package com.sap.sailing.android.tracking.app.test.extensions;
import java.lang.reflect.Field;
import java.net.URL;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import com.sap.sailing.android.shared.data.http.HttpJsonPostRequest;
public class HttpJsonPostRequestTestable extends HttpJsonPostRequest {
public HttpJsonPostRequestTestable(URL requestUrl, String body, Context context) {
super(requestUrl, body, context);
}
public JSONObject getPayloadJSON() throws NoSuchFieldException, IllegalAccessException, IllegalArgumentException, JSONException{
Class<?> clazz = getClass().getSuperclass();
Field requestBodyField = clazz.getDeclaredField("requestBody");
requestBodyField.setAccessible(true);
return new JSONObject((String)requestBodyField.get(this));
}
public URL getUrl() throws NoSuchFieldException, IllegalAccessException, IllegalArgumentException
{
Class<?> clazz = getClass().getSuperclass();
Field requestBodyField = clazz.getDeclaredField("url");
requestBodyField.setAccessible(true);
return (URL)requestBodyField.get(this);
}
}
@@ -0,0 +1,13 @@
package com.sap.sailing.android.tracking.app.test.extensions;
import android.content.Context;
import com.sap.sailing.android.tracking.app.utils.NetworkHelper;
public class NetworkHelperTestable extends NetworkHelper {
public static void injectInstance(Context context, NetworkHelper instance)
{
mContext = context;
mInstance = instance;
}
}
@@ -1,25 +0,0 @@
package com.sap.sailing.android.tracking.app.test.extensions;
import android.content.Context;
import com.sap.sailing.android.tracking.app.utils.VolleyHelper;
public class VolleyHelperTestable extends VolleyHelper {
public VolleyHelperTestable(Context context) {
super(context);
}
private static Context mContext;
@Override
public Context getApplicationContext() {
return mContext;
}
public static void injectInstance(Context context, VolleyHelper instance)
{
mContext = context;
mInstance = instance;
}
}
@@ -5,5 +5,7 @@
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry combineaccessrules="false" kind="src" path="/com.sap.sailing.domain.common"/>
<classpathentry combineaccessrules="false" kind="src" path="/com.sap.sailing.domain"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
@@ -79,29 +79,29 @@
<activity
android:name=".ui.activities.SettingsActivity"
android:screenOrientation="unspecified" />
<activity android:name="net.hockeyapp.android.UpdateActivity" />
<service
android:name=".services.TrackingService"
android:exported="false"
android:icon="@drawable/icon"
android:label="@string/tracking_service" />
<service
<!-- <service
android:name=".services.TransmittingService"
android:exported="false"
android:icon="@drawable/icon"
android:label="@string/transmitting_service" />
<!--
android:label="@string/transmitting_service" /> -->
<service
android:name="com.sap.sailing.android.tracking.app.services.sending.MessageSendingService"
android:name="com.sap.sailing.android.shared.services.sending.MessageSendingService"
android:exported="false" >
<intent-filter>
<action android:name="com.sap.sailing.android.tracking.app.action.sendSavedIntents" />
<action android:name="com.sap.sailing.android.tracking.app.action.sendMessage" />
</intent-filter>
</service>
-->
<!--<service android:name=".service.GcmIntentService" />-->
<receiver android:name="com.sap.sailing.android.tracking.app.services.sending.ConnectivityChangedReceiver" >
<receiver android:name="com.sap.sailing.android.shared.services.sending.ConnectivityChangedReceiver" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
@@ -54,6 +54,8 @@ android {
}
dependencies {
compile "net.hockeyapp.android:HockeySDK:3.5.0"
/* support libraries */
compile "com.android.support:appcompat-v7:21.0.3"
@@ -62,10 +64,9 @@ dependencies {
/* local dependencies */
compile project(":mobile:com.sap.sailing.android.shared")
compile project(":mobile:google-volley_lib")
/* all local jar-libs */
compile fileTree(dir: "libs", include: ["*.jar"])
// compile fileTree(dir: "libs", include: ["*.jar"])
}
// Custom tasks
@@ -7,6 +7,6 @@
android:width="1dip"
android:color="#ffffff" />
<corners android:radius="0dip" />
<corners android:radius="5dip" />
</shape>
@@ -4,9 +4,9 @@
<solid android:color="#00FFFFFF" />
<stroke
android:width="1dip"
android:width="0.5dip"
android:color="#ffffff" />
<corners android:radius="0dip" />
<corners android:radius="5dip" />
</shape>
@@ -4,8 +4,8 @@
<solid android:color="@color/sap_green" />
<corners
android:bottomLeftRadius="3dip"
android:bottomRightRadius="3dip"
android:topLeftRadius="3dip"
android:topRightRadius="3dip" />
android:bottomLeftRadius="5dip"
android:bottomRightRadius="5dip"
android:topLeftRadius="5dip"
android:topRightRadius="5dip" />
</shape>
@@ -4,8 +4,8 @@
<solid android:color="@color/sap_red" />
<corners
android:bottomLeftRadius="3dip"
android:bottomRightRadius="3dip"
android:topLeftRadius="3dip"
android:topRightRadius="3dip" />
android:bottomLeftRadius="5dip"
android:bottomRightRadius="5dip"
android:topLeftRadius="5dip"
android:topRightRadius="5dip" />
</shape>
@@ -8,9 +8,9 @@
android:color="@color/white" />
<corners
android:bottomLeftRadius="3dip"
android:bottomRightRadius="3dip"
android:topLeftRadius="3dip"
android:topRightRadius="3dip" />
android:bottomLeftRadius="5dip"
android:bottomRightRadius="5dip"
android:topLeftRadius="5dip"
android:topRightRadius="5dip" />
</shape>
@@ -4,8 +4,8 @@
<solid android:color="@color/sap_yellow" />
<corners
android:bottomLeftRadius="3dip"
android:bottomRightRadius="3dip"
android:topLeftRadius="3dip"
android:topRightRadius="3dip" />
android:bottomLeftRadius="5dip"
android:bottomRightRadius="5dip"
android:topLeftRadius="5dip"
android:topRightRadius="5dip" />
</shape>
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.sap.sailing.android.tracking.app.views"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res/com.sap.sailing.android.tracking.app"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/darker_gray" >
<TextView
android:id="@+id/compass_bearing_header_text_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:layout_alignParentTop="true"
android:text="@string/heading"
android:textColor="@android:color/darker_gray"
android:textSize="15sp"
android:paddingTop="15dp"
/>
<com.sap.sailing.android.tracking.app.customviews.AutoResizeLightTextView
android:id="@+id/compass_bearing_text_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text=""
android:layout_centerInParent="true"
android:textSize="124sp" />
</RelativeLayout>
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.sap.sailing.android.tracking.app.views"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res/com.sap.sailing.android.tracking.app"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/darker_gray" >
<TextView
android:id="@+id/speed_header_text_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/sog"
android:textColor="@android:color/darker_gray"
android:textSize="14sp"
android:paddingLeft="10dp"
android:paddingTop="15dp" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true"
android:orientation="vertical" >
<com.sap.sailing.android.tracking.app.customviews.AutoResizeLightTextView
android:id="@+id/speed_text_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:textColor="@color/white"
android:text="-"
android:textSize="124sp"
android:textStyle="bold" />
<com.sap.sailing.android.tracking.app.customviews.AutoResizeLightTextView
android:id="@+id/speed_unit_text_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/speed_text_view"
android:gravity="top|center"
android:text="@string/knots"
android:textColor="@color/white"
android:layout_marginTop="-20dp"
android:textSize="20sp"
android:textStyle="bold" />
</RelativeLayout>
</RelativeLayout>
@@ -5,28 +5,27 @@
xmlns:app="http://schemas.android.com/apk/res/com.sap.sailing.android.tracking.app"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/sap_yellow" >
android:background="@color/darker_gray" >
<com.sap.sailing.android.tracking.app.customviews.AutoResizeTextView
<TextView
android:id="@+id/compass_bearing_header_text_view"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:layout_marginTop="8dp"
android:gravity="center"
android:text="@string/heading"
android:textColor="@color/black"
android:textSize="48sp"
android:textStyle="bold" />
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:layout_alignParentTop="true"
android:text="@string/course_over_ground"
android:textColor="@android:color/darker_gray"
android:textSize="15sp"
android:paddingTop="15dp"
/>
<com.sap.sailing.android.tracking.app.customviews.AutoResizeTextView
<com.sap.sailing.android.tracking.app.customviews.AutoResizeLightTextView
android:id="@+id/compass_bearing_text_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/compass_bearing_header_text_view"
android:gravity="center"
android:text=""
android:textColor="@color/black"
android:textSize="118sp"
android:textStyle="bold" />
android:layout_centerInParent="true"
android:textSize="124sp" />
</RelativeLayout>
@@ -18,17 +18,19 @@
android:id="@+id/toolbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:lines="1"
android:singleLine="true"
android:textSize="18sp"/>
android:textSize="16sp"/>
<com.sap.sailing.android.tracking.app.customviews.OpenSansTextView
android:id="@+id/toolbar_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:lines="1"
android:singleLine="true"
android:textSize="14sp" />
android:textSize="12sp" />
</LinearLayout>
@@ -19,17 +19,19 @@
android:id="@+id/toolbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:lines="1"
android:singleLine="true"
android:textSize="18sp" />
android:textSize="16sp" />
<com.sap.sailing.android.tracking.app.customviews.OpenSansTextView
android:id="@+id/toolbar_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:lines="1"
android:singleLine="true"
android:textSize="14sp" />
android:textSize="12sp" />
</LinearLayout>
</com.sap.sailing.android.tracking.app.customviews.OpenSansToolbar>
@@ -62,8 +64,8 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#00000000"
android:paddingBottom="10dp"
android:background="@color/black"
android:paddingTop="10dp" />
@@ -5,28 +5,46 @@
xmlns:app="http://schemas.android.com/apk/res/com.sap.sailing.android.tracking.app"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/sap_yellow" >
android:background="@color/darker_gray" >
<com.sap.sailing.android.tracking.app.customviews.AutoResizeTextView
<TextView
android:id="@+id/speed_header_text_view"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:layout_marginTop="8dp"
android:gravity="center"
android:layout_height="wrap_content"
android:text="@string/sog"
android:textColor="@color/black"
android:textSize="48sp"
android:textStyle="bold" />
<com.sap.sailing.android.tracking.app.customviews.AutoResizeTextView
android:id="@+id/speed_text_view"
android:textColor="@android:color/darker_gray"
android:textSize="14sp"
android:paddingLeft="10dp"
android:paddingTop="15dp" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/speed_header_text_view"
android:gravity="center"
android:text=""
android:textColor="@color/black"
android:textSize="108sp"
android:textStyle="bold" />
android:layout_centerInParent="true"
android:orientation="vertical" >
<com.sap.sailing.android.tracking.app.customviews.AutoResizeLightTextView
android:id="@+id/speed_text_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:textColor="@color/white"
android:text="0"
android:textSize="124sp"
android:textStyle="bold" />
<com.sap.sailing.android.tracking.app.customviews.AutoResizeLightTextView
android:id="@+id/speed_unit_text_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/speed_text_view"
android:gravity="top|center"
android:text="@string/knots"
android:textColor="@color/white"
android:layout_marginTop="-20dp"
android:textSize="20sp"
android:textStyle="bold" />
</RelativeLayout>
</RelativeLayout>
@@ -5,15 +5,15 @@
xmlns:app="http://schemas.android.com/apk/res/com.sap.sailing.android.tracking.app"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black" >
android:background="@color/darker_gray" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:layout_above="@+id/stop_tracking_box"
android:baselineAligned="false"
android:paddingTop="20sp" >
android:paddingTop="15dp" >
<com.sap.sailing.android.tracking.app.customviews.OpenSansTextView
android:id="@+id/tracking_time"
@@ -23,44 +23,38 @@
android:layout_alignParentTop="true"
android:text="@string/tracking_time" />
<com.sap.sailing.android.tracking.app.customviews.AutoResizeTextView
<com.sap.sailing.android.tracking.app.customviews.AutoResizeLightTextView
android:id="@+id/tracking_time_label"
android:layout_width="match_parent"
android:layout_height="110dp"
android:gravity="center"
android:layout_height="fill_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingBottom="0dp"
android:layout_marginBottom="0dp"
android:text="00:00:00"
android:layout_below="@+id/tracking_time"
android:textSize="128sp" />
android:gravity="bottom"
android:textSize="100sp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/stop_tracking_box"
android:layout_width="fill_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:background="@color/gray" >
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="@color/white_transparent" />
android:layout_alignParentBottom="true" >
<com.sap.sailing.android.tracking.app.customviews.OpenSansButton
android:id="@+id/stop_tracking"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="10dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="0dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@drawable/rounded_btn_red"
android:padding="10dp"
android:padding="0dp"
android:text="@string/stop_tracking"
android:textAllCaps="true"
android:textStyle="bold" />
</RelativeLayout>
</RelativeLayout>
@@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_height="402dp"
android:background="@color/black" >
<ScrollView
@@ -142,6 +142,12 @@
android:layout_height="1dp"
android:layout_below="@id/gps_quality"
android:background="@android:color/darker_gray" />
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_below="@id/tracking_status_label"
android:background="@android:color/darker_gray" />
</RelativeLayout>
</ScrollView>
</RelativeLayout>
@@ -7,9 +7,11 @@
<string name="preference_energy_saving_is_disabled">Aus</string>
<string name="prefrence_energy_saving_summary_is_enabled">GPS Updates werden alle 30 Sekunden verschickt.</string>
<string name="prefrence_energy_saving_summary_is_disabled">GPS Updates werden alle 3 Sekunden verschickt.</string>
<string name="preference_heading_from_magnetic_key_is_enabled">magn.</string>
<string name="preference_heading_from_magnetic_key_is_disabled">GPS</string>
<string name="preference_heading_from_magnetic_key_summary_is_enabled">Richtung stammt vom Magnetkompass</string>
<string name="preference_heading_from_magnetic_key_summary_is_disabled">Richtung stammt vom GPS</string>
<string name="preference_heading_from_magnetic_enabled">Richtung</string>
</resources>
<string name="preference_heading_with_declination_subtracted_key_is_enabled">Ein</string>
<string name="preference_heading_with_declination_subtracted_key_is_disabled">Aus</string>
<string name="preference_heading_with_declination_subtracted_key_summary_is_enabled">Anzeige: Magnetkompasskurs ohne Ablenkung</string>
<string name="preference_heading_with_declination_subtracted_key_summary_is_disabled">Anzeige: Magnetkompasskurs mit Ablenkung</string>
<string name="preference_heading_with_declination_subtracted_enabled">Richtung</string>
</resources>
@@ -80,9 +80,19 @@
<string name="hud_heading_prefix">HDG: </string>
<string name="hud_speed_over_ground_prefix">SPD: </string>
<string name="heading">Kurs</string>
<string name="course_over_ground">Course over Ground</string>
<string name="sog">SOG</string>
<string name="none">Keine</string>
<string name="show_leaderboard">Leaderboard</string>
<string name="desc_regatta_user_image">Benutzerdefiniertes Bild für Regatta</string>
<string name="desc_add_photo_button">Foto hinzufügen Button</string>
<string name="error_play_store_and_scanning_not_available">"PlayStore und BarcodeScanner-App nicht verfügbar."</string>
<string name="scanning_cancelled">Scan abgebrochen</string>
<string name="error_scanning_qrcode">Fehler beim Scannen des QR-Codes {result-code}</string>
<string name="error_invalid_qr_code">Ungültiger QR-Code</string>
<string name="error_while_receiving_server_data">Fehler beim Empfang vom Server</string>
<string name="info_already_checked_in_this_qr_code">Bereits für diese Regatta eingecheckt</string>
<string name="warning">Warnung</string>
<string name="gps_turned_off">GPS ist nicht aktiviert. Bitte aktivieren Sie den Standortzugriff (GPS) in Ihren Android-Einstellungen!</string>
<string name="knots">Knoten</string>
</resources>
@@ -4,7 +4,7 @@
<string name="notify_user_api_call_failed">Bei der Kommunikation mit dem Server ist ein Fehler aufgetreten. Bitte versuchen Sie es noch einmal.</string>
<string name="confirm_data_hello_name">Hallo {full_name}</string>
<string name="confirm_data_you_are_signed_in_as_sail_id">Sie sind als {sail_id} gemeldet</string>
<string name="confirm_data_is_correct">Stimmt</string>
<string name="confirm_data_is_correct">OK</string>
<string name="decline_data_is_incorrect">Das bin nicht ich!</string>
<string name="notify_user_db_operation_failed">Fehler: Datenbank-Operation ist fehlgeschlagen.</string>
@@ -10,7 +10,7 @@
<string name="preference_server_url_default">http://95.85.58.11:8000</string>
<string name="preference_energy_saving_enabled_key">preferenceEnergySavingEnabled</string>
<string name="preference_heading_from_magnetic_key">preferenceHeadingFromMagneticKey</string>
<string name="preference_heading_with_declination_subtracted">preferenceHeadingWithDeclinationSubtracted</string>
<string name="settings_category_energy_saving">Battery Saving</string>
<string name="energy_saving_enabled">Save Energy</string>
@@ -26,11 +26,12 @@
<string name="preference_battery_is_charging">batteryIsCharging</string>
<string name="preference_tracking_timer_started">trackingTimerStarted</string>
<string name="preference_tracker_is_tracking">trackerIsTracking</string>
<string name="preference_tracker_is_tracking_event_id">trackerIsTrackingEventId</string>
<string name="preference_heading_from_magnetic_key_is_enabled">magn.</string>
<string name="preference_heading_from_magnetic_key_is_disabled">GPS</string>
<string name="preference_heading_from_magnetic_key_summary_is_enabled">Heading from magnetic compass</string>
<string name="preference_heading_from_magnetic_key_summary_is_disabled">Heading from GPS</string>
<string name="preference_heading_from_magnetic_enabled">Heading</string>
<string name="preference_tracker_is_tracking_checkin_digest">trackerIsTrackingCheckinDigest</string>
<string name="preference_last_scanned_qr_code">lastScannedQRCode</string>
<string name="preference_heading_with_declination_subtracted_key_is_enabled">On</string>
<string name="preference_heading_with_declination_subtracted_key_is_disabled">Off</string>
<string name="preference_heading_with_declination_subtracted_key_summary_is_enabled">Display: Course without declination</string>
<string name="preference_heading_with_declination_subtracted_key_summary_is_disabled">Display: Course with declination</string>
<string name="preference_heading_with_declination_subtracted_enabled">Heading</string>
</resources>
@@ -35,6 +35,7 @@
<string name="competitor_id">CompetitorId</string>
<string name="event_id">EventId</string>
<string name="leaderboard_name">LeaderboardName</string>
<string name="checkin_digest">CheckinDigest</string>
<string name="add_team_photo">Add Team Photo</string>
<string name="do_you_want_to_choose_existing_img_or_take_a_new_one">Do you want to use an existing image or take a new one?</string>
<string name="existing_image">Existing</string>
@@ -65,8 +66,17 @@
<string name="tracking_colon">Tracking:</string>
<string name="regatta_in_progress">The regatta is in progress.</string>
<string name="heading">Heading</string>
<string name="sog">SOG</string>
<string name="course_over_ground">Course over Ground</string>
<string name="sog">Speed over Ground</string>
<string name="none">None</string>
<string name="show_leaderboard">Leader board</string>
<string name="error_play_store_and_scanning_not_available">"PlayStore and Scanning not available."</string>
<string name="scanning_cancelled">Scanning cancelled</string>
<string name="error_scanning_qrcode">Error scanning QR-Code {result-code}</string>
<string name="error_invalid_qr_code">Invalid QR-Code</string>
<string name="error_while_receiving_server_data">Error while receiving server data</string>
<string name="info_already_checked_in_this_qr_code">Already checked in to this regatta</string>
<string name="warning">Warning</string>
<string name="gps_turned_off">GPS is turned off. Please turn on location access (GPS) in your Android Settings!</string>
<string name="knots">knots</string>
</resources>
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="tracking_activity_event_id_parameter">trackingActivityEventId</string>
<string name="tracking_activity_checkin_digest_parameter">trackingActivityCheckinDigestParameter</string>
</resources>
@@ -13,7 +13,7 @@
<string name="notify_user_api_call_failed">An error occurred while contacting the server. Please try again.</string>
<string name="confirm_data_hello_name">Hello {full_name}</string>
<string name="confirm_data_you_are_signed_in_as_sail_id">You are signing in as {sail_id}</string>
<string name="confirm_data_is_correct">Correct</string>
<string name="confirm_data_is_correct">OK</string>
<string name="decline_data_is_incorrect">That\'s not me!</string>
<string name="notify_user_db_operation_failed">Error: Database operation has failed.</string>
@@ -6,7 +6,7 @@
<string name="tracking_service_start">tracking_start</string>
<string name="tracking_service_stop">tracking_stop</string>
<string name="tracking_service_change">tracking_change</string>
<string name="tracking_service_event_id_parameter">trackingServiceEventId</string>
<string name="tracking_service_checkin_digest_parameter">trackingServiceCheckinDigest</string>
<string name="transmitting_service">Transmitting Service</string>
<string name="transmitting_service_start">transmitting_start</string>
<string name="transmitting_service_stop">transmitting_stop</string>
@@ -23,12 +23,12 @@
<SwitchPreference
android:defaultValue="true"
android:key="@string/preference_heading_from_magnetic_key"
android:switchTextOn="@string/preference_heading_from_magnetic_key_is_enabled"
android:switchTextOff="@string/preference_heading_from_magnetic_key_is_disabled"
android:summaryOn="@string/preference_heading_from_magnetic_key_summary_is_enabled"
android:summaryOff="@string/preference_heading_from_magnetic_key_summary_is_disabled"
android:title="@string/preference_heading_from_magnetic_enabled" />
android:key="@string/preference_heading_with_declination_subtracted"
android:switchTextOn="@string/preference_heading_with_declination_subtracted_key_is_enabled"
android:switchTextOff="@string/preference_heading_with_declination_subtracted_key_is_disabled"
android:summaryOn="@string/preference_heading_with_declination_subtracted_key_summary_is_enabled"
android:summaryOff="@string/preference_heading_with_declination_subtracted_key_summary_is_disabled"
android:title="@string/preference_heading_with_declination_subtracted_enabled" />
</PreferenceCategory>
@@ -0,0 +1,23 @@
package com.sap.sailing.android.tracking.app.customviews;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
public class AutoResizeLightTextView extends AutoResizeTextView {
public AutoResizeLightTextView(Context context) {
this(context, null);
}
public AutoResizeLightTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoResizeLightTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/OpenSans-Light.ttf"));
mTextSize = getTextSize();
}
}
@@ -56,7 +56,7 @@ public class AutoResizeTextView extends TextView {
// Text size that is set from code. This acts as a starting point for
// resizing
private float mTextSize;
protected float mTextSize;
// Temporary upper bounds on the starting text size
private float mMaxTextSize = 0;
@@ -0,0 +1,29 @@
package com.sap.sailing.android.tracking.app.customviews;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;
public class OpenSansLightTextView extends TextView {
public OpenSansLightTextView(Context context) {
super(context);
init();
}
public OpenSansLightTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public OpenSansLightTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
protected void init() {
setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/OpenSans-Light.ttf"));
}
}
@@ -12,11 +12,12 @@ public class AnalyticsContract {
String COMPETITOR_COUNTRY_CODE = "competitor_country_code";
String COMPETITOR_NATIONALITY = "competitor_nationality";
String COMPETITOR_SAIL_ID = "competitor_sail_id";
String COMPETITOR_LEADERBOARD_FK = "leaderboard_id";
String COMPETITOR_CHECKIN_DIGEST = "competitor_checkin_digest";
}
interface LeaderboardColumns {
String LEADERBOARD_NAME = "leaderboard_name";
String LEADERBOARD_CHECKIN_DIGEST = "leaderboard_checkin_digest";
}
interface EventColumns {
@@ -26,44 +27,22 @@ public class AnalyticsContract {
String EVENT_SERVER = "event_server";
String EVENT_IMAGE_URL = "image_url";
String EVENT_NAME = "event_name";
String EVENT_LEADERBOARD_FK = "leaderboard_id";
}
interface SensorGpsColumns {
String GPS_ACCURACY = "gps_accuracy";
String GPS_ALTITUDE = "gps_altitude";
String GPS_BEARING = "gps_bearing";
String GPS_DEVICE = "gps_device";
String GPS_LATITUDE = "gps_latitude";
String GPS_LONGITUDE = "gps_longitude";
String GPS_PROVIDER = "gps_provider";
String GPS_SYNCED = "gps_synced";
String GPS_SPEED = "gps_speed";
String GPS_TIME = "gps_time";
String GPS_EVENT_FK = "event_id";
String EVENT_CHECKIN_DIGEST = "event_checkin_digest";
}
public static final String CONTENT_AUTHORITY = "com.sap.sailing.android.tracking.app.provider.db";
public static final Uri BASE_CONTENT_URI = Uri.parse("content://"
+ CONTENT_AUTHORITY);
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
private static final String PATH_COMPETITOR = "competitors";
private static final String PATH_EVENT = "events";
private static final String PATH_LEADERBOARD = "leaderboards";
private static final String PATH_SENSOR_GPS = "sensor_gps";
public static class LeaderboardsEventsJoined {
public final static Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
.appendPath("leaderboards_events_joined").build();
}
public static class EventGpsFixesJoined {
public final static Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
.appendPath("event_gps_fix_joined").build();
}
public static class EventLeaderboardCompetitorJoined {
public final static Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
.appendPath("event_leaderboard_competitor_joined")
@@ -120,25 +99,6 @@ public class AnalyticsContract {
}
}
public static class SensorGps implements SensorGpsColumns, BaseColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
.appendPath(PATH_SENSOR_GPS).build();
public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE
+ "/vnd.sap_sailing_analytics.sensor.gps";
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE
+ "/vnd.sap_sailing_analytics.sensor.gps";
public static final String DEFAULT_SORT = BaseColumns._ID + " ASC ";
public static Uri buildSensorGpsUri(String gpsId) {
return CONTENT_URI.buildUpon().appendPath(gpsId).build();
}
public static String getGpsId(Uri uri) {
return uri.getPathSegments().get(1);
}
}
public static class Leaderboard implements LeaderboardColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
.appendPath(PATH_LEADERBOARD).build();
@@ -6,10 +6,12 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Competitor;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.CompetitorColumns;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.EventColumns;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Leaderboard;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.LeaderboardColumns;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.SensorGpsColumns;
public class AnalyticsDatabase extends SQLiteOpenHelper {
@@ -26,13 +28,15 @@ public class AnalyticsDatabase extends SQLiteOpenHelper {
String COMPETITORS = "competitors";
String EVENTS = "events";
String EVENTS_COMPETITORS = "events_competitors";
String SENSOR_GPS = "sensor_gps";
String LEADERBOARDS = "leaderboards";
String EVENTS_JOIN_LEADERBOARDS_JOIN_COMPETITORS = Tables.LEADERBOARDS +
" INNER JOIN " + Tables.EVENTS + " ON (" + Tables.LEADERBOARDS + "." + BaseColumns._ID + " = " + Tables.EVENTS + ".leaderboard_id ) " +
" INNER JOIN " + Tables.COMPETITORS + " ON (" + Tables.LEADERBOARDS + "."+ BaseColumns._ID + " = " + Tables.COMPETITORS + ".leaderboard_id ) ";
String GPS_FIXES_JOIN_EVENTS = "sensor_gps LEFT JOIN events ON sensor_gps.event_id = events._id ";
String LEADERBOARDS_JOIN_EVENTS = "events LEFT JOIN leaderboards ON events.leaderboard_id = leaderboards._id ";
" INNER JOIN " + Tables.EVENTS + " ON (" + Tables.LEADERBOARDS + "." + Leaderboard.LEADERBOARD_CHECKIN_DIGEST
+ " = " + Tables.EVENTS + "." + Event.EVENT_CHECKIN_DIGEST + ") " +
" INNER JOIN " + Tables.COMPETITORS + " ON (" + Tables.LEADERBOARDS + "."+ Leaderboard.LEADERBOARD_CHECKIN_DIGEST
+ " = " + Tables.COMPETITORS + "." + Competitor.COMPETITOR_CHECKIN_DIGEST + ") ";
String LEADERBOARDS_JOIN_EVENTS = "events LEFT JOIN leaderboards ON " + Tables.EVENTS + "." + Event.EVENT_CHECKIN_DIGEST
+ " = " + Tables.LEADERBOARDS + "." + Leaderboard.LEADERBOARD_CHECKIN_DIGEST;
}
public AnalyticsDatabase(Context context) {
@@ -44,6 +48,7 @@ public class AnalyticsDatabase extends SQLiteOpenHelper {
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + Tables.LEADERBOARDS + " ("
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ LeaderboardColumns.LEADERBOARD_CHECKIN_DIGEST + " TEXT, "
+ LeaderboardColumns.LEADERBOARD_NAME + " TEXT );");
db.execSQL("CREATE TABLE " + Tables.COMPETITORS + " ("
@@ -53,9 +58,7 @@ public class AnalyticsDatabase extends SQLiteOpenHelper {
+ CompetitorColumns.COMPETITOR_COUNTRY_CODE + " TEXT, "
+ CompetitorColumns.COMPETITOR_NATIONALITY + " TEXT, "
+ CompetitorColumns.COMPETITOR_SAIL_ID + " TEXT, "
+ CompetitorColumns.COMPETITOR_LEADERBOARD_FK + " INTEGER, "
+ "FOREIGN KEY (" + CompetitorColumns.COMPETITOR_LEADERBOARD_FK + ") "
+ "REFERENCES " + Tables.LEADERBOARDS + "( " + BaseColumns._ID + " ))");
+ CompetitorColumns.COMPETITOR_CHECKIN_DIGEST + " TEXT )");
db.execSQL("CREATE TABLE " + Tables.EVENTS + " ( "
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
@@ -65,25 +68,7 @@ public class AnalyticsDatabase extends SQLiteOpenHelper {
+ EventColumns.EVENT_DATE_END + " INTEGER, "
+ EventColumns.EVENT_SERVER + " TEXT, "
+ EventColumns.EVENT_IMAGE_URL + " TEXT, "
+ EventColumns.EVENT_LEADERBOARD_FK + " INTEGER, "
+ "FOREIGN KEY (" + EventColumns.EVENT_LEADERBOARD_FK + ") "
+ "REFERENCES " + Tables.LEADERBOARDS + "( " + BaseColumns._ID + " ))");
db.execSQL("CREATE TABLE " + Tables.SENSOR_GPS + " ( "
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ SensorGpsColumns.GPS_ACCURACY + " DOUBLE, "
+ SensorGpsColumns.GPS_ALTITUDE + " DOUBLE, "
+ SensorGpsColumns.GPS_BEARING + " DOUBLE, "
+ SensorGpsColumns.GPS_DEVICE + " TEXT, "
+ SensorGpsColumns.GPS_LATITUDE + " DOUBLE, "
+ SensorGpsColumns.GPS_LONGITUDE + " DOUBLE, "
+ SensorGpsColumns.GPS_PROVIDER + " TEXT, "
+ SensorGpsColumns.GPS_SPEED + " DOUBLE, "
+ SensorGpsColumns.GPS_SYNCED + " INTEGER DEFAULT 0, "
+ SensorGpsColumns.GPS_TIME + " INTEGER, "
+ SensorGpsColumns.GPS_EVENT_FK + " INTEGER, "
+ "FOREIGN KEY (" + SensorGpsColumns.GPS_EVENT_FK + ") "
+ "REFERENCES " + Tables.EVENTS + "( " + EventColumns.EVENT_ID + " ))");
+ EventColumns.EVENT_CHECKIN_DIGEST + " TEXT )");
}
@Override
@@ -97,7 +82,6 @@ public class AnalyticsDatabase extends SQLiteOpenHelper {
db.execSQL("DROP TABLE IF EXISTS " + Tables.COMPETITORS);
db.execSQL("DROP TABLE IF EXISTS " + Tables.EVENTS);
db.execSQL("DROP TABLE IF EXISTS " + Tables.SENSOR_GPS);
db.execSQL("DROP TABLE IF EXISTS " + Tables.LEADERBOARDS);
onCreate(db);
@@ -18,7 +18,6 @@ import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Competitor;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Leaderboard;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.SensorGps;
import com.sap.sailing.android.tracking.app.provider.AnalyticsDatabase.Tables;
import com.sap.sailing.android.tracking.app.utils.AppPreferences;
@@ -42,9 +41,6 @@ public class AnalyticsProvider extends ContentProvider {
private static final int MESSAGE = 400;
private static final int MESSAGE_ID = 401;
private static final int SENSOR_GPS = 500;
private static final int SENSOR_GPS_ID = 501;
private static final int EVENT_LEADERBOARD_COMPETITOR_JOINED = 600;
private static final int EVENT_GPS_FIXES_JOINED = 700;
@@ -67,9 +63,6 @@ public class AnalyticsProvider extends ContentProvider {
matcher.addURI(authority, "messages", MESSAGE);
matcher.addURI(authority, "messages/#", MESSAGE_ID);
matcher.addURI(authority, "sensor_gps", SENSOR_GPS);
matcher.addURI(authority, "sensor_gps/#", SENSOR_GPS_ID);
matcher.addURI(authority, "event_leaderboard_competitor_joined", EVENT_LEADERBOARD_COMPETITOR_JOINED);
matcher.addURI(authority, "event_gps_fix_joined", EVENT_GPS_FIXES_JOINED);
@@ -112,12 +105,6 @@ public class AnalyticsProvider extends ContentProvider {
cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
return cursor;
case EVENT_GPS_FIXES_JOINED:
SQLiteQueryBuilder eb = new SQLiteQueryBuilder();
eb.setTables(Tables.GPS_FIXES_JOIN_EVENTS);
cursor = eb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
return cursor;
case LEADERBOARDS_EVENTS_JOINED:
SQLiteQueryBuilder el = new SQLiteQueryBuilder();
el.setTables(Tables.LEADERBOARDS_JOIN_EVENTS);
@@ -161,12 +148,6 @@ public class AnalyticsProvider extends ContentProvider {
case LEADERBOARD_ID:
return Leaderboard.CONTENT_ITEM_TYPE;
case SENSOR_GPS:
return SensorGps.CONTENT_TYPE;
case SENSOR_GPS_ID:
return SensorGps.CONTENT_ITEM_TYPE;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
@@ -183,9 +164,9 @@ public class AnalyticsProvider extends ContentProvider {
switch (sUriMatcher.match(uri)) {
case COMPETITOR:
db.insertOrThrow(Tables.COMPETITORS, null, values);
long competitorId = db.insertOrThrow(Tables.COMPETITORS, null, values);
notifyChange(uri);
return Competitor.buildCompetitorUri(values.getAsString(Competitor.COMPETITOR_ID));
return Competitor.buildCompetitorUri(String.valueOf(competitorId));
case EVENT:
long eventId = db.insertOrThrow(Tables.EVENTS, null, values);
@@ -193,14 +174,9 @@ public class AnalyticsProvider extends ContentProvider {
return Event.buildEventUri(String.valueOf(eventId));
case LEADERBOARD:
db.insertOrThrow(Tables.LEADERBOARDS, null, values);
long leaderboardId = db.insertOrThrow(Tables.LEADERBOARDS, null, values);
notifyChange(uri);
return Leaderboard.buildLeaderboardUri(values.getAsString(BaseColumns._ID));
case SENSOR_GPS:
db.insertOrThrow(Tables.SENSOR_GPS, null, values);
notifyChange(uri);
return SensorGps.buildSensorGpsUri("XX");
return Leaderboard.buildLeaderboardUri(String.valueOf(leaderboardId));
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
@@ -218,18 +194,6 @@ public class AnalyticsProvider extends ContentProvider {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
switch (sUriMatcher.match(uri)) {
case SENSOR_GPS:
int numGpsFixesDeleted = db.delete(Tables.SENSOR_GPS, selection, selectionArgs);
notifyChange(uri);
return numGpsFixesDeleted;
case SENSOR_GPS_ID:
String idStr = uri.getLastPathSegment();
String where = SensorGps._ID + " = " + idStr;
int numGpsFixesWithIdDeleted = db.delete(Tables.SENSOR_GPS, where, selectionArgs);
notifyChange(uri);
return numGpsFixesWithIdDeleted;
case COMPETITOR:
int numCompetitorRowsDeleted = db.delete(Tables.COMPETITORS, selection, selectionArgs);
@@ -260,17 +224,9 @@ public class AnalyticsProvider extends ContentProvider {
ExLog.i(getContext(), TAG, message);
}
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
//final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
switch (sUriMatcher.match(uri)) {
case SENSOR_GPS_ID:
String idStr = uri.getLastPathSegment();
String where = SensorGps._ID + " = " + idStr;
int numRowsAffected = db.update(Tables.SENSOR_GPS, values, where, selectionArgs);
notifyChange(uri);
return numRowsAffected;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
@@ -308,9 +264,6 @@ public class AnalyticsProvider extends ContentProvider {
return builder.table(Tables.EVENTS)
.where(Event.EVENT_ID + " = ?", event_id);
case SENSOR_GPS:
return builder.table(Tables.SENSOR_GPS);
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
@@ -1,102 +0,0 @@
package com.sap.sailing.android.tracking.app.sensors;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
public class CompassManager implements SensorEventListener {
//private final static String TAG = CompassManager.class.getName();
private static CompassManager mInstance;
private Context mContext;
private SensorManager sensorManager;
private Sensor gsensor;
private Sensor msensor;
private float[] mGravity = new float[3];
private float[] mGeomagnetic = new float[3];
private float azimuth = 0f;
private MagneticHeadingListener listener;
public static synchronized CompassManager getInstance(Context context) {
if (mInstance == null) {
mInstance = new CompassManager(context);
}
return mInstance;
}
private CompassManager(Context context) {
mContext = context;
sensorManager = (SensorManager) mContext
.getSystemService(Context.SENSOR_SERVICE);
gsensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
msensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
}
public void registerListener(MagneticHeadingListener listener)
{
this.listener = listener;
start();
}
public void unregisterListener()
{
stop();
this.listener = null;
}
public void start() {
sensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_GAME);
}
public void stop() {
sensorManager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent event) {
final float alpha = 0.97f;
if (listener == null) {
return;
}
synchronized (this) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mGravity[0] = alpha * mGravity[0] + (1 - alpha) * event.values[0];
mGravity[1] = alpha * mGravity[1] + (1 - alpha) * event.values[1];
mGravity[2] = alpha * mGravity[2] + (1 - alpha) * event.values[2];
}
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
mGeomagnetic[0] = alpha * mGeomagnetic[0] + (1 - alpha) * event.values[0];
mGeomagnetic[1] = alpha * mGeomagnetic[1] + (1 - alpha) * event.values[1];
mGeomagnetic[2] = alpha * mGeomagnetic[2] + (1 - alpha) * event.values[2];
}
float R[] = new float[9];
float I[] = new float[9];
boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
if (success) {
float orientation[] = new float[3];
SensorManager.getOrientation(R, orientation);
azimuth = (float) Math.toDegrees(orientation[0]); // orientation
azimuth = (azimuth + 360) % 360;
listener.magneticHeadingUpdated(azimuth);
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
public interface MagneticHeadingListener {
public void magneticHeadingUpdated(float heading);
}
}
@@ -1,14 +1,22 @@
package com.sap.sailing.android.tracking.app.services;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.GeomagneticField;
import android.location.Location;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -16,242 +24,321 @@ import android.os.IBinder;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessageSendingService;
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.LaunchActivity;
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.utils.ServiceHelper;
import com.sap.sailing.android.tracking.app.valueobjects.EventInfo;
public class TrackingService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
private GoogleApiClient googleApiClient;
private LocationRequest locationRequest;
private NotificationManager notificationManager;
private boolean locationUpdateRequested = false;
private AppPreferences prefs;
private ScheduledExecutorService scheduler;
private GoogleApiClient googleApiClient;
private LocationRequest locationRequest;
private NotificationManager notificationManager;
private boolean locationUpdateRequested = false;
private AppPreferences prefs;
private ScheduledExecutorService scheduler;
private GPSQualityListener gpsQualityListener;
private final IBinder trackingBinder = new TrackingBinder();
private GPSQualityListener gpsQualityListener;
private final IBinder trackingBinder = new TrackingBinder();
private static final String TAG = TrackingService.class.getName();
private static final String TAG = TrackingService.class.getName();
public static final String WEB_SERVICE_PATH = "/sailingserver/api/v1/gps_fixes";
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private int NOTIFICATION_ID = R.string.tracker_started;
public static final String WEB_SERVICE_PATH = "/sailingserver/api/v1/gps_fixes";
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private int NOTIFICATION_ID = R.string.tracker_started;
private final int UPDATE_INTERVAL_DEFAULT = 3000;
private final int UPDATE_INTERVAL_POWERSAVE_MODE = 30000;
private final float BATTERY_POWER_SAVE_TRESHOLD = 0.2f;
private String eventId;
private long eventRowId;
private String checkinDigest;
private EventInfo event;
@Override
public void onCreate() {
super.onCreate();
prefs = new AppPreferences(this);
@Override
public void onCreate() {
super.onCreate();
prefs = new AppPreferences(this);
// http://developer.android.com/training/location/receive-location-updates.html
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(prefs.getGPSFixInterval());
locationRequest.setFastestInterval(prefs.getGPSFixFastestInterval());
// http://developer.android.com/training/location/receive-location-updates.html
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(prefs.getGPSFixInterval());
locationRequest.setFastestInterval(prefs.getGPSFixFastestInterval());
googleApiClient = new GoogleApiClient.Builder(this)
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "Starting Tracking Service with eventId: " + eventId);
}
if (intent != null) {
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "Starting Tracking Service with checkinDigest: " + checkinDigest);
}
if (intent.getAction() != null) {
if (intent.getAction().equals(getString(R.string.tracking_service_stop))) {
stopTracking();
} else {
if (intent.getExtras() != null) {
eventId = intent.getExtras().getString(getString(R.string.tracking_service_event_id_parameter));
if (intent.getAction() != null) {
if (intent.getAction().equals(
getString(R.string.tracking_service_stop))) {
stopTracking();
} else {
if (intent.getExtras() != null) {
checkinDigest = intent
.getExtras()
.getString(getString(R.string.tracking_service_checkin_digest_parameter));
event = DatabaseHelper.getInstance().getEventInfo(this, checkinDigest);
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "Starting Tracking Service with checkinDigest: "+ checkinDigest);
}
startTracking();
}
}
} else {
stopTracking();
}
}
return Service.START_STICKY;
}
eventRowId = DatabaseHelper.getInstance().getRowIdForEventId(this, eventId);
public void startTracking() {
googleApiClient.connect();
locationUpdateRequested = true;
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "Starting Tracking Service with eventId: " + eventId);
ExLog.i(this, TAG, "And with event._id: " + eventRowId);
}
ExLog.i(this, TAG, "Started Tracking");
// showNotification();
startTracking();
}
}
} else {
stopTracking();
}
}
return Service.START_STICKY;
}
prefs.setTrackerIsTracking(true);
prefs.setTrackerIsTrackingCheckinDigest(checkinDigest);
}
public void startTracking() {
googleApiClient.connect();
locationUpdateRequested = true;
public void stopTracking() {
if (googleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this);
}
googleApiClient.disconnect();
locationUpdateRequested = false;
ExLog.i(this, TAG, "Started Tracking");
// showNotification();
if (scheduler != null) {
scheduler.shutdown();
}
prefs.setTrackerIsTracking(true);
prefs.setTrackerIsTrackingEventId(eventId);
}
prefs.setTrackerIsTracking(false);
prefs.setTrackerIsTrackingCheckinDigest(null);
public void stopTracking() {
if (googleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this);
}
googleApiClient.disconnect();
locationUpdateRequested = false;
stopSelf();
ExLog.i(this, TAG, "Stopped Tracking");
}
if (scheduler != null) {
scheduler.shutdown();
}
@Override
public void onConnectionFailed(ConnectionResult arg0) {
ExLog.e(this, TAG,
"Failed to connect to Google Play Services for location updates");
}
prefs.setTrackerIsTracking(false);
prefs.setTrackerIsTrackingEventId(null);
stopSelf();
ExLog.i(this, TAG, "Stopped Tracking");
}
@Override
public void onConnectionFailed(ConnectionResult arg0) {
ExLog.e(this, TAG, "Failed to connect to Google Play Services for location updates");
}
@Override
public void onConnected(Bundle arg0) {
if (locationUpdateRequested) {
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
}
}
@Override
public void onConnected(Bundle arg0) {
if (locationUpdateRequested) {
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
}
}
@Override
public void onConnectionSuspended(int i) {
}
public void reportGPSQualityBearingAndSpeed(float gpsAccurracy, float bearing, float speed) {
GPSQuality quality = GPSQuality.noSignal;
if (gpsQualityListener != null) {
if (gpsAccurracy > 48) {
quality = GPSQuality.poor;
} else if (gpsAccurracy > 10) {
quality = GPSQuality.good;
} else if (gpsAccurracy <= 10) {
quality = GPSQuality.great;
}
public void reportGPSQualityBearingAndSpeed(float gpsAccurracy, float bearing, float speed, double latitude, double longitude, double altitude) {
if (prefs.getDisplayHeadingWithSubtractedDeclination())
{
GeomagneticField geomagneticField = new GeomagneticField((float)latitude, (float)longitude, (float)altitude, System.currentTimeMillis());
bearing = bearing - geomagneticField.getDeclination();
}
GPSQuality quality = GPSQuality.noSignal;
if (gpsQualityListener != null) {
if (gpsAccurracy > 48) {
quality = GPSQuality.poor;
} else if (gpsAccurracy > 10) {
quality = GPSQuality.good;
} else if (gpsAccurracy <= 10) {
quality = GPSQuality.great;
}
gpsQualityListener.gpsQualityAndAccurracyUpdated(quality, gpsAccurracy, bearing, speed);
}
}
gpsQualityListener.gpsQualityAndAccurracyUpdated(quality, gpsAccurracy, bearing, speed);
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
public void onLocationChanged(Location location) {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
public void onLocationChanged(Location location) {
updateResendIntervalSetting();
reportGPSQualityBearingAndSpeed(location.getAccuracy(), location.getBearing(),
location.getSpeed(), location.getLatitude(), location.getLongitude(),
location.getAltitude());
JSONObject json = new JSONObject();
try {
JSONArray jsonArray = new JSONArray();
JSONObject fixJson = new JSONObject();
reportGPSQualityBearingAndSpeed(location.getAccuracy(), location.getBearing(), location.getSpeed());
fixJson.put("course", location.getBearing());
fixJson.put("timestamp", location.getTime());
fixJson.put("speed", location.getSpeed());
fixJson.put("longitude", location.getLongitude());
fixJson.put("latitude", location.getLatitude());
DatabaseHelper.getInstance().insertGPSFix(this, location.getLatitude(), location.getLongitude(),
location.getSpeed(), location.getBearing(), location.getProvider(), location.getTime(), eventRowId);
jsonArray.put(fixJson);
ensureTransmittingServiceIsRunning();
}
json.put("fixes", jsonArray);
json.put("deviceUuid", prefs.getDeviceIdentifier());
/**
* start transmitting service when a new fix arrives, because it ends itself, if there's no data to send.
*/
private void ensureTransmittingServiceIsRunning() {
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "ensureTransmittingServiceIsRunning, starting TransmittingService");
}
String postUrlStr = event.server + prefs.getServerGpsFixesPostPath();
ServiceHelper.getInstance().startTransmittingService(this);
}
@Override
public IBinder onBind(Intent intent) {
return trackingBinder;
}
startService(MessageSendingService.createMessageIntent(this, postUrlStr,
null, UUID.randomUUID(), json.toString(), null));
@Override
public void onDestroy() {
stopTracking();
notificationManager.cancel(NOTIFICATION_ID);
Toast.makeText(this, R.string.tracker_stopped, Toast.LENGTH_SHORT).show();
}
} catch (JSONException ex) {
ExLog.i(this, TAG, "Error while building geolocation json " + ex.getMessage());
}
// DatabaseHelper.getInstance().insertGPSFix(this, location.getLatitude(),
// location.getLongitude(), location.getSpeed(),
// location.getBearing(), location.getProvider(),
// location.getTime(), eventRowId);
// ensureTransmittingServiceIsRunning();
}
/**
* Update whether message sending service should retry every 3 seconds or
* every 30.
*/
private void updateResendIntervalSetting() {
float batteryPct = getBatteryPercentage();
boolean batteryIsCharging = prefs.getBatteryIsCharging();
int updateInterval = UPDATE_INTERVAL_DEFAULT;
if (prefs.getEnergySavingEnabledByUser() ||
(batteryPct < BATTERY_POWER_SAVE_TRESHOLD && !batteryIsCharging)) {
if (BuildConfig.DEBUG) {
ExLog.i(this, "POWER-LEVELS", "in power saving mode");
}
// private void showNotification() {
// Intent intent = new Intent(this, RegattaActivity.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
// | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
// CharSequence text = getText(R.string.tracker_started);
// Notification notification = new NotificationCompat.Builder(this)
// .setContentTitle(getText(R.string.app_name))
// .setContentText(text).setContentIntent(pi)
// .setSmallIcon(R.drawable.icon).build();
// notification.flags |= Notification.FLAG_NO_CLEAR;
// startForeground(NOTIFICATION_ID, notification);
// }
updateInterval = UPDATE_INTERVAL_POWERSAVE_MODE;
} else {
if (BuildConfig.DEBUG) {
ExLog.i(this, "POWER-LEVELS", "in default power mode");
}
}
prefs.setMessageResendInterval(updateInterval);
}
/**
* Get battery charging level
* @return battery charging level in interval [0,1]
*/
private float getBatteryPercentage() {
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = this.registerReceiver(null, ifilter);
public void registerGPSQualityListener(GPSQualityListener listener) {
gpsQualityListener = listener;
}
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
public void unregisterGPSQualityListener() {
gpsQualityListener = null;
}
float batteryPct = level / (float) scale;
public class TrackingBinder extends Binder {
public TrackingService getService() {
return TrackingService.this;
}
}
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "Battery: " + (batteryPct * 100) + "%");
}
public enum GPSQuality {
noSignal(0), poor(2), good(3), great(4);
return batteryPct;
}
private final int gpsQuality;
/**
* start transmitting service when a new fix arrives, because it ends
* itself, if there's no data to send.
*/
// private void ensureTransmittingServiceIsRunning() {
// if (BuildConfig.DEBUG) {
// ExLog.i(this, TAG,
// "ensureTransmittingServiceIsRunning, starting TransmittingService");
// }
//
// ServiceHelper.getInstance().startTransmittingService(this);
// }
GPSQuality(int quality) {
this.gpsQuality = quality;
}
@Override
public IBinder onBind(Intent intent) {
return trackingBinder;
}
public int toInt() {
return this.gpsQuality;
}
}
@Override
public void onDestroy() {
stopTracking();
notificationManager.cancel(NOTIFICATION_ID);
Toast.makeText(this, R.string.tracker_stopped, Toast.LENGTH_SHORT)
.show();
}
public interface GPSQualityListener {
public void gpsQualityAndAccurracyUpdated(GPSQuality quality, float gpsAccurracy, float gpsBearing,
float gpsSpeed);
}
// private void showNotification() {
// Intent intent = new Intent(this, RegattaActivity.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
// | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
// CharSequence text = getText(R.string.tracker_started);
// Notification notification = new NotificationCompat.Builder(this)
// .setContentTitle(getText(R.string.app_name))
// .setContentText(text).setContentIntent(pi)
// .setSmallIcon(R.drawable.icon).build();
// notification.flags |= Notification.FLAG_NO_CLEAR;
// startForeground(NOTIFICATION_ID, notification);
// }
public void registerGPSQualityListener(GPSQualityListener listener) {
gpsQualityListener = listener;
}
public void unregisterGPSQualityListener() {
gpsQualityListener = null;
}
public class TrackingBinder extends Binder {
public TrackingService getService() {
return TrackingService.this;
}
}
public enum GPSQuality {
noSignal(0), poor(2), good(3), great(4);
private final int gpsQuality;
GPSQuality(int quality) {
this.gpsQuality = quality;
}
public int toInt() {
return this.gpsQuality;
}
}
public interface GPSQualityListener {
public void gpsQualityAndAccurracyUpdated(GPSQuality quality, float gpsAccurracy, float gpsBearing, float gpsSpeed);
}
private void showNotification() {
CharSequence text = getText(R.string.tracker_started);
Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
Intent i = new Intent(this, LaunchActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0);
notification.setLatestEventInfo(this, getText(R.string.app_name), text, pi);
notification.flags |= Notification.FLAG_NO_CLEAR;
startForeground(NOTIFICATION_ID, notification);
}
}
@@ -1,559 +0,0 @@
package com.sap.sailing.android.tracking.app.services;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
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.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.IBinder;
import com.sap.sailing.android.shared.data.http.HttpJsonPostRequest;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.services.sending.ConnectivityChangedReceiver;
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.utils.NetworkHelper;
import com.sap.sailing.android.tracking.app.utils.NetworkHelper.NetworkHelperError;
import com.sap.sailing.android.tracking.app.utils.NetworkHelper.NetworkHelperFailureListener;
import com.sap.sailing.android.tracking.app.utils.NetworkHelper.NetworkHelperSuccessListener;
import com.sap.sailing.android.tracking.app.utils.UniqueDeviceUuid;
import com.sap.sailing.android.tracking.app.valueobjects.GpsFix;
/**
* Service that handles sending of GPS-fixes (fixes) to the Sailing-API.
*
* It checks, after a certain time-interval has passed, if any fixes are
* available in the database and attempts to send them batch-wise.
*
* (There are two strategies for sending, depending on whether tracking is
* running. If it's running, only fixes for the current event are being sent. If
* not, it tries to send fixes for previous events, as well. Assuming there are
* older unsent fixes that for some reason have not been sent.)
*
* It keeps track of when the last successful sending occurred and, if no new
* data is available (i.e., if tracking is disabled), ends itself after a
* certain period. It relies on being restarted by the {@link TrackingService}.
*
* It also ends itself when connectivity is lost. In that case it relies on
* being restarted by the {@link ConnectivityChangedReceiver}.
*
* It switches into a power saving mode, if a certain battery discharge is
* reached. It does, however, not do so, if the device is plugged in (= when it
* is charging). The power saving mode causes the time-interval between check-
* and transmit-cycles to be increased to conserve power.
*
* @author Lukas Zielinski
*
*/
public class TransmittingService extends Service {
private static final String TAG = TransmittingService.class.getName();
private final int UPDATE_BATCH_SIZE = 1000;
private final int UPDATE_INTERVAL_DEFAULT = 3000;
private final int UPDATE_INTERVAL_POWERSAVE_MODE = 30000;
private final long AUTO_END_SERVICE_AFTER_NANO_PASSED = 10000000000L; // 10
// sec
private final float BATTERY_POWER_SAVE_TRESHOLD = 0.2f;
private int currentUpdateInterval = UPDATE_INTERVAL_DEFAULT;
private boolean sendingAttempted = false;
private boolean lastTransmissionFailed = false;
private long lastTransmissionTimestamp = 0;
private AppPreferences prefs;
private Timer timer;
private boolean timerRunning;
private APIConnectivityListener apiConnectivityListener;
private final IBinder transmittingBinder = new TransmittingBinder();
/**
* True if sending, because if sending takes longer a new attempt should not
* be made.
*/
private static boolean currentlySending = false;
@Override
public IBinder onBind(Intent intent) {
prefs = new AppPreferences(this);
if (!timerRunning) {
startTimer();
}
return transmittingBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
prefs = new AppPreferences(this);
if (intent != null) {
if (intent.getAction() != null) {
if (intent.getAction().equals(getString(R.string.transmitting_service_start))) {
if (!timerRunning) {
startTimer();
}
} else {
stopTimer();
}
} else {
stopTimer();
}
}
return Service.START_STICKY;
}
private void startTimer() {
timer = new Timer();
timer.start();
timerRunning = true;
if (BuildConfig.DEBUG) {
ExLog.i(this, "TIMER", "Background update-timer start");
}
}
private void stopTimer() {
if (timer != null) {
timer.stop();
timerRunning = false;
if (BuildConfig.DEBUG) {
ExLog.i(this, "TIMER", "Background update-timer stop");
}
}
}
private void markSuccessfulTransmission() {
currentlySending = false;
if (BuildConfig.DEBUG) {
ExLog.i(this, "TRANSMISSION", "markSuccessfulTransmission");
}
lastTransmissionFailed = false;
lastTransmissionTimestamp = System.nanoTime();
reportApiConnectivity(APIConnectivity.reachableTransmissionSuccess);
}
private void markFailedTransmission() {
currentlySending = false;
if (BuildConfig.DEBUG) {
ExLog.i(this, "TRANSMISSION", "markFailedTransmission");
}
lastTransmissionFailed = true;
lastTransmissionTimestamp = 0;
if (isConnected()) {
reportApiConnectivity(APIConnectivity.reachableTransmissionError);
} else {
reportApiConnectivity(APIConnectivity.notReachable);
}
}
/**
* If time interval has passed without any transmission, the service can
* turn itself off. Same for
*
* @return
*/
private boolean serviceCanShutItselfDown() {
long currentNanoTime = System.nanoTime();
if (BuildConfig.DEBUG) {
ExLog.i(this, "TRANSMISSION", "serviceCanShutItselfDown?");
ExLog.i(this, "TRANSMISSION", "DELTA:" + (currentNanoTime - lastTransmissionTimestamp));
}
// if network is not connected, shutdown. ConnevtivityChangedReceiver
// should restart us.
if (!isConnected()) {
enabledConnectivityReceiver(); // make sure ConnectivityReceiver is
// running
return true;
}
// if we had a failure, never go to sleep until data is sent
// successfully.
if (lastTransmissionFailed) {
if (BuildConfig.DEBUG) {
ExLog.i(this, "TRANSMISSION", "returning false, have failed transmission");
}
return false;
}
if (!sendingAttempted) {
if (BuildConfig.DEBUG) {
ExLog.i(this, "TRANSMISSION", "returning true, no sending attempt");
}
return true;
}
if (currentNanoTime - lastTransmissionTimestamp > AUTO_END_SERVICE_AFTER_NANO_PASSED) {
if (BuildConfig.DEBUG) {
ExLog.i(this, "TRANSMISSION", "returning true, timeout");
}
return true;
}
if (BuildConfig.DEBUG) {
ExLog.i(this, "TRANSMISSION", "returning false, don't terminate");
}
return false;
}
private void timerFired() {
float batteryPct = getBatteryPercentage();
boolean batteryIsCharging = prefs.getBatteryIsCharging();
if (prefs.getEnergySavingEnabledByUser()
|| (batteryPct < BATTERY_POWER_SAVE_TRESHOLD && !batteryIsCharging)) {
if (BuildConfig.DEBUG) {
ExLog.i(this, "POWER-LEVELS", "in power saving mode");
}
currentUpdateInterval = UPDATE_INTERVAL_POWERSAVE_MODE;
} else {
if (BuildConfig.DEBUG) {
ExLog.i(this, "POWER-LEVELS", "in default power mode");
}
currentUpdateInterval = UPDATE_INTERVAL_DEFAULT;
}
sendFixesToAPI(null);
reportUnsentGPSFixesCount(DatabaseHelper.getInstance().getNumberOfUnsentGPSFixes(
getBaseContext()));
}
private boolean getTrackingServiceIsCurrentlyTracking() {
return prefs.getTrackerIsTracking();
}
private void sendFixesToAPI(List<String> failedHosts) {
// first, lets fetch all unsent fixes
List<GpsFix> fixes = DatabaseHelper.getInstance().getUnsentFixes(getBaseContext(),
failedHosts, UPDATE_BATCH_SIZE);
// store ids so we can delete the rows later
ArrayList<String> ids = new ArrayList<String>();
// create JSON
JSONArray jsonArray = new JSONArray();
String currentEventId = null;
String host = null;
for (GpsFix fix : fixes) {
if (currentEventId == null) {
currentEventId = fix.eventId;
host = fix.host;
}
if (currentEventId.equals(fix.eventId)) {
// still have more unsent Gps-Fixes for this event.
ids.add(String.valueOf(fix.id));
JSONObject json = new JSONObject();
try {
json.put("course", fix.course);
json.put("timestamp", fix.timestamp);
json.put("speed", fix.speed);
json.put("longitude", fix.longitude);
json.put("latitude", fix.latitude);
} catch (JSONException ex) {
ExLog.i(this, TAG, "Error while building geolocation json " + ex.getMessage());
}
jsonArray.put(json);
} else {
// we don't have any more Gps-fixes for this batch, end
// collection, proceed with sending.
break;
}
}
if (jsonArray.length() > 0) {
// send
String[] idsArr = new String[ids.size()];
JSONObject requestObject = new JSONObject();
try {
requestObject.put("deviceUuid", UniqueDeviceUuid.getUniqueId(this));
requestObject.put("fixes", jsonArray);
} catch (JSONException e) {
e.printStackTrace();
}
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "sending gps fix json: " + requestObject.toString());
ExLog.i(this, TAG,
"url: " + prefs.getServerURL() + prefs.getServerGpsFixesPostPath());
}
if (host != null) {
if (currentlySending == true)
{
ExLog.w(this, TAG, "Warning, can't send fixes, currentlySending flag is set!");
}
else
{
currentlySending = true;
sendingAttempted = true; // will be set to false in the listeners
HttpJsonPostRequest request;
try {
request = new HttpJsonPostRequest(new URL(host
+ prefs.getServerGpsFixesPostPath()), requestObject.toString(), this);
NetworkHelper.getInstance(this).executeHttpJsonRequestAsnchronously(
request,
new FixSubmitListener(ids.toArray(idsArr)),
new FixSubmitErrorListener(host,
getTrackingServiceIsCurrentlyTracking(), failedHosts));
} catch (MalformedURLException e) {
ExLog.w(this, TAG, "Warning, can't send fixes, MalformedURLException: " + e.getMessage());
}
}
} else {
ExLog.w(this, TAG, "Warning, can't send fixes, host is null!");
}
} else {
if (serviceCanShutItselfDown()) {
if (BuildConfig.DEBUG) {
ExLog.i(this,
TAG,
"Nothing to send or timeout occurred, Transmitting Service is stopping timer and terminating itself.");
}
stopTimer();
stopSelf();
}
}
}
/**
* Get a count of ALL unsent GPS-fixes.
*
* This method is only interested in the GPS-fixes for the currently tracked
* event, and needs to find out the id of that event first.
*
* @return
*/
private float getBatteryPercentage() {
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = this.registerReceiver(null, ifilter);
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level / (float) scale;
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "Battery: " + (batteryPct * 100) + "%");
}
return batteryPct;
}
private void deleteSynced(String[] fixIdStrings) {
DatabaseHelper.getInstance().deleteGpsFixes(getBaseContext(), fixIdStrings);
}
/**
* checks if there is network connectivity
*
* @return connectivity check value
*/
private boolean isConnected() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
if (activeNetwork == null) {
return false;
}
return activeNetwork.isConnected();
}
private void enabledConnectivityReceiver() {
ConnectivityChangedReceiver.enable(this);
}
/**
* Report API connectivity to listening activity
*
* @param apiConnectivity
*/
private void reportApiConnectivity(APIConnectivity apiConnectivity) {
if (apiConnectivityListener != null) {
apiConnectivityListener.apiConnectivityUpdated(apiConnectivity);
}
}
/**
* Report the number of currently unsent GPS-fixes
*
* @param unsentGPSFixesCount
*/
private void reportUnsentGPSFixesCount(int unsentGPSFixesCount) {
if (apiConnectivityListener != null) {
apiConnectivityListener.setUnsentGPSFixesCount(unsentGPSFixesCount);
}
}
// might need this class in the future:
//
// private void markAsSynced(String[] fixIdStrings)
// {
// for (String idStr: fixIdStrings)
// {
// ContentValues updateValues = new ContentValues();
// updateValues.put(SensorGps.GPS_SYNCED, 1);
// Uri uri = ContentUris.withAppendedId(SensorGps.CONTENT_URI,
// Long.parseLong(idStr));
// getContentResolver().update(uri, updateValues, null, null);
// }
// }
private class FixSubmitListener implements NetworkHelperSuccessListener {
private String[] ids;
public FixSubmitListener(String[] ids) {
this.ids = ids;
}
@Override
public void performAction(JSONObject response) {
deleteSynced(ids);
markSuccessfulTransmission();
}
}
private class FixSubmitErrorListener implements NetworkHelperFailureListener {
private String host;
private List<String> failedHosts;
private boolean currentlyTracking = false;
public FixSubmitErrorListener(String host, boolean currentlyTracking,
List<String> failedHosts) {
this.failedHosts = failedHosts;
this.host = host;
this.currentlyTracking = true;
}
@Override
public void performAction(NetworkHelperError error) {
markFailedTransmission();
ExLog.e(TransmittingService.this, TAG,
"Error while sending GPS fix " + error.getMessage());
if (!currentlyTracking) {
if (failedHosts == null) {
failedHosts = new ArrayList<String>();
failedHosts.add(host);
}
sendFixesToAPI(this.failedHosts);
ExLog.i(TransmittingService.this, TAG,
"Retrying resend step with this failed hosts list: " + failedHosts);
}
}
}
class Timer implements Runnable {
public Thread t;
public volatile boolean endExecution;
@Override
public void run() {
while (!endExecution) {
try {
Thread.sleep(currentUpdateInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
timerFired();
}
reportApiConnectivity(APIConnectivity.noAttempt);
}
public void start() {
endExecution = false;
if (t == null) {
t = new Thread(this);
t.start();
}
}
public void stop() {
endExecution = true;
}
}
public void registerAPIConnectivityListener(APIConnectivityListener listener) {
apiConnectivityListener = listener;
}
public void unregisterAPIConnectivityListener() {
apiConnectivityListener = null;
}
public class TransmittingBinder extends Binder {
public TransmittingService getService() {
return TransmittingService.this;
}
}
public enum APIConnectivity {
notReachable(0), reachableTransmissionSuccess(1), reachableTransmissionError(2), noAttempt(
4);
private final int apiConnectivity;
APIConnectivity(int connectivity) {
this.apiConnectivity = connectivity;
}
public int toInt() {
return this.apiConnectivity;
}
}
public interface APIConnectivityListener {
public void apiConnectivityUpdated(APIConnectivity apiConnectivity);
public void setUnsentGPSFixesCount(int count);
}
}
@@ -8,17 +8,19 @@ import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.ui.activities.SendingServiceAwareActivity;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.utils.AppPreferences;
import com.sap.sailing.android.tracking.app.utils.SqlDebugHelper;
public class BaseActivity extends ActionBarActivity {
import com.sap.sailing.android.shared.ui.activities.SendingServiceAwareActivity;
public class BaseActivity extends SendingServiceAwareActivity {
private static final String TAG = BaseActivity.class.getName();
protected AppPreferences prefs;
@@ -102,4 +104,9 @@ public class BaseActivity extends ActionBarActivity {
public void showErrorPopup(int string1Id, int string2Id) {
showErrorPopup(getString(string1Id), getString(string2Id));
}
@Override
protected int getOptionsMenuResId() {
return R.menu.options_menu;
}
}
@@ -1,204 +0,0 @@
package com.sap.sailing.android.tracking.app.ui.activities;
import java.net.URL;
import java.util.UUID;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.Dialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.services.TrackingService;
import com.sap.sailing.android.tracking.app.services.sending.MessageSendingService;
import com.sap.sailing.domain.common.racelog.tracking.DeviceMappingConstants;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class LaunchActivity extends BaseActivity {
private static int requestCodeQRCode = 42471;
private static final String TAG = LaunchActivity.class.getName();
private Button toggleTrackingBtn;
private boolean isTrackingActive = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.launch_activity);
toggleTrackingBtn = (Button) findViewById(R.id.btnToggleTracking);
isTrackingActive = isTrackingServiceRunning();
updateToggleTrackingBtn();
}
private void updateToggleTrackingBtn() {
toggleTrackingBtn.setText(getString(
isTrackingActive ? R.string.stop_tracking : R.string.start_tracking));
}
public void onScanQRCodeClicked(View view) {
try {
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
startActivityForResult(intent, requestCodeQRCode);
} catch (Exception e) {
Uri marketUri = Uri.parse("market://details?id=com.google.zxing.client.android");
Intent marketIntent = new Intent(Intent.ACTION_VIEW,marketUri);
startActivity(marketIntent);
}
}
public static class ErrorDialogFragment extends DialogFragment {
private Dialog dialog;
public ErrorDialogFragment() {
super();
dialog = null;
}
/**
* Set the dialog to display
*
* @param dialog
* An error dialog
*/
public void setDialog(Dialog dialog) {
this.dialog = dialog;
}
/*
* This method must return a Dialog to the DialogFragment.
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return dialog;
}
}
private boolean googlePLayServicesAvailable() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (ConnectionResult.SUCCESS == resultCode) {
ExLog.i(this, TAG, getString(R.string.play_services_available));
return true;
} else {
// Display an error dialog
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0);
if (dialog != null) {
ErrorDialogFragment errorFragment = new ErrorDialogFragment();
errorFragment.setDialog(dialog);
errorFragment.show(getSupportFragmentManager(), TAG);
}
return false;
}
}
public void onToggleTrackingClicked(View view) {
if (googlePLayServicesAvailable()) {
if (!isTrackingActive) {
Intent startTrackingIntent = new Intent(this, TrackingService.class);
getBaseContext().startService(startTrackingIntent);
} else {
Intent startTrackingIntent = new Intent(this, TrackingService.class);
getBaseContext().stopService(startTrackingIntent);
}
isTrackingActive = !isTrackingActive;
updateToggleTrackingBtn();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != requestCodeQRCode) {
super.onActivityResult(requestCode, resultCode, data);
return;
}
if (resultCode == Activity.RESULT_OK) {
String content = data.getStringExtra("SCAN_RESULT");
ExLog.i(this, TAG, "Parsing URI: "+content);
Uri uri = Uri.parse(content);
String server = uri.getScheme() + "://";
server += uri.getHost();
if (uri.getPort() != -1) {
server += ":" + uri.getPort();
}
prefs.setServerURL(server);
String leaderboard = uri.getQueryParameter(DeviceMappingConstants.LEADERBOARD_NAME);
String competitorIdAsString = uri.getQueryParameter(DeviceMappingConstants.COMPETITOR_ID_AS_STRING);
String markIdAsString = uri.getQueryParameter(DeviceMappingConstants.MARK_ID_AS_STRING);
DeviceIdentifier device = new SmartphoneUUIDIdentifierImpl(UUID.fromString(prefs.getDeviceIdentifier()));
TimePoint from = MillisecondsTimePoint.now();
String itemId = null;
String itemType = null;
JSONObject mappingStart = new JSONObject();
try {
mappingStart.put(DeviceMappingConstants.DEVICE_UUID, device.getStringRepresentation());
mappingStart.put(DeviceMappingConstants.FROM_MILLIS, from.asMillis());
mappingStart.put(DeviceMappingConstants.DEVICE_TYPE, "android");
if (competitorIdAsString != null) {
mappingStart.put(DeviceMappingConstants.PUSH_DEVICE_ID, "<push_device_id_not_known_on_android>");
mappingStart.put(DeviceMappingConstants.COMPETITOR_ID_AS_STRING, competitorIdAsString);
itemType = "competitor";
} else if (markIdAsString != null) {
mappingStart.put(DeviceMappingConstants.MARK_ID_AS_STRING, markIdAsString);
itemType = "mark";
}
} catch (IllegalArgumentException e) {
//deserializing the competitor/mark on the server would fail if the idAsString were used
ExLog.e(this, TAG, "Found non-UUID as mapped item id - can only deal with UUIDs: " + itemId);
Toast.makeText(this, "Did not get a UUID as item ID: " + itemId, Toast.LENGTH_LONG).show(); // FIXME i18n
} catch (JSONException e) {
ExLog.e(this, TAG, "Internal error trying to register device: " + e.getMessage());
Toast.makeText(this, "Internal error trying to register device : " + e.getMessage(), Toast.LENGTH_LONG).show(); // FIXME i18n
}
String postCheckinUrl = getPostCheckinUrl(server, leaderboard);
startService(MessageSendingService.createMessageIntent(this, postCheckinUrl, /* callbackPayload */ null,
UUID.randomUUID(), mappingStart.toString(), /* callbackClass */ null));
ExLog.i(this, TAG, "Created mapping event");
Toast.makeText(this, "Successfully created mapping between device and " + itemType, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Error scanning QRCode (" + resultCode + ")", Toast.LENGTH_LONG).show();
}
}
private String getPostCheckinUrl(String serverBaseUrl, String leaderboardName) {
return serverBaseUrl+"/sailingserver/api/v1/leaderboards/"+Uri.encode(leaderboardName)+"/device_mappings/start";
}
//FIXME: currently duplicate code with racecommitte app (GeneralPreferenceFragment), refactor once maven build is available and thus update mechanism can be created for tracking app see bugs 2398 2399
protected String getServerUrl(URL apkUrl) {
String protocol = apkUrl.getProtocol();
String host = apkUrl.getHost();
String port = apkUrl.getPort() == -1 ? "" : ":" + apkUrl.getPort();
return protocol + "://" + host + port;
}
// approach with static member on TrackingService class did not work
// this is not a really good approach in my opinion either, but simple
private boolean isTrackingServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (TrackingService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
}
@@ -64,13 +64,11 @@ public class RegattaActivity extends BaseActivity {
competitor = new CompetitorInfo();
leaderboard = new LeaderboardInfo();
String leaderboardName = intent.getStringExtra(getString(R.string.leaderboard_name));
String eventId = intent.getStringExtra(getString(R.string.event_id));
String competitorId = intent.getStringExtra(getString(R.string.competitor_id));
String checkinDigest = intent.getStringExtra(getString(R.string.checkin_digest));
competitor = DatabaseHelper.getInstance().getCompetitor(this, competitorId);
event = DatabaseHelper.getInstance().getEventInfo(this, eventId);
leaderboard = DatabaseHelper.getInstance().getLeaderboard(this, leaderboardName);
competitor = DatabaseHelper.getInstance().getCompetitor(this, checkinDigest);
event = DatabaseHelper.getInstance().getEventInfo(this, checkinDigest);
leaderboard = DatabaseHelper.getInstance().getLeaderboard(this, checkinDigest);
setContentView(R.layout.fragment_container);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
@@ -83,12 +81,22 @@ public class RegattaActivity extends BaseActivity {
getSupportActionBar().setHomeButtonEnabled(true);
toolbar.setNavigationIcon(R.drawable.sap_logo_64_sq);
toolbar.setPadding(20, 0, 0, 0);
getSupportActionBar().setTitle(leaderboardName);
getSupportActionBar().setTitle(leaderboard.name);
getSupportActionBar().setSubtitle(event.name);
}
replaceFragment(R.id.content_frame, new RegattaFragment());
}
@Override
public void onStart() {
super.onStart();
if (prefs.getTrackerIsTracking())
{
String checkinDigest = prefs.getTrackerIsTrackingCheckinDigest();
startTrackingActivity(checkinDigest);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
@@ -223,7 +231,6 @@ public class RegattaActivity extends BaseActivity {
return;
}
try {
System.out.println("*** STORE IMAGE: " + image +", FILENAME: " + fileName);
FileOutputStream fos = new FileOutputStream(pictureFile);
image.compress(Bitmap.CompressFormat.PNG, 90, fos);
fos.close();
@@ -386,7 +393,7 @@ public class RegattaActivity extends BaseActivity {
*/
public void checkout()
{
final String checkoutURLStr = prefs.getServerURL()
final String checkoutURLStr = event.server
+ prefs.getServerCheckoutPath().replace("{leaderboard-name}",
Uri.encode(leaderboard.name));
@@ -411,8 +418,7 @@ public class RegattaActivity extends BaseActivity {
@Override
public void performAction(JSONObject response) {
DatabaseHelper.getInstance().deleteRegattaFromDatabase(
RegattaActivity.this, event, competitor, leaderboard);
DatabaseHelper.getInstance().deleteRegattaFromDatabase(RegattaActivity.this, event.checkinDigest);
deleteImageFile(getLeaderboardImageFileName(leaderboard.name));
dismissProgressDialog();
finish();
@@ -444,4 +450,10 @@ public class RegattaActivity extends BaseActivity {
super.onBackPressed();
}
}
private void startTrackingActivity(String checkinDigest) {
Intent intent = new Intent(this, TrackingActivity.class);
intent.putExtra(getString(R.string.tracking_activity_checkin_digest_parameter), checkinDigest);
startActivity(intent);
}
}
@@ -1,15 +1,18 @@
package com.sap.sailing.android.tracking.app.ui.activities;
import net.hockeyapp.android.CrashManager;
import android.app.Dialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.ui.fragments.HomeFragment;
import com.sap.sailing.android.tracking.app.utils.ServiceHelper;
public class StartActivity extends BaseActivity {
@@ -35,9 +38,22 @@ public class StartActivity extends BaseActivity {
replaceFragment(R.id.content_frame, new HomeFragment());
}
@Override
protected void onResume() {
super.onResume();
checkForCrashes();
//checkForUpdates();
int googleServicesResultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (googleServicesResultCode != ConnectionResult.SUCCESS)
{
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(googleServicesResultCode, this, 0);
dialog.show();
}
}
@Override
protected void onStart() {
public void onStart() {
super.onStart();
// get url if launched via url intent-filter
@@ -47,8 +63,7 @@ public class StartActivity extends BaseActivity {
if (urlStr != null) {
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG,
"Matched URL, handling scanned or matched URL.");
ExLog.i(this, TAG, "Matched URL, handling scanned or matched URL.");
}
Uri uri = Uri.parse(urlStr);
@@ -58,23 +73,34 @@ public class StartActivity extends BaseActivity {
homeFragment.handleScannedOrUrlMatchedUri(uri);
}
// start transmitting service that will henceforth run in the background
ServiceHelper.getInstance().startTransmittingService(this);
intent.setData(null);
if (prefs.getTrackerIsTracking())
{
String eventId = prefs.getTrackerIsTrackingEventId();
startTrackingActivity(eventId);
String checkinDigest = prefs.getTrackerIsTrackingCheckinDigest();
startRegatta(checkinDigest);
}
}
private void startTrackingActivity(String eventId) {
Intent intent = new Intent(this, TrackingActivity.class);
intent.putExtra(getString(R.string.tracking_activity_event_id_parameter), eventId);
startActivity(intent);
/**
* Hockeyapp integration method.
*/
private void checkForCrashes() {
CrashManager.register(this, "060ff0c8a907638e3b31d3146091c87b");
}
// /**
// * Hockeyapp integration method.
// */
// private void checkForUpdates() {
// // TODO: Remove this for store builds!
// UpdateManager.register(this, "060ff0c8a907638e3b31d3146091c87b");
// }
private void startRegatta(String checkinDigest) {
Intent intent = new Intent(this, RegattaActivity.class);
intent.putExtra(getString(R.string.checkin_digest), checkinDigest);
startActivity(intent);
}
}
@@ -6,6 +6,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.Fragment;
@@ -17,18 +18,16 @@ import android.support.v7.widget.Toolbar;
import android.widget.TextView;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessageSendingService;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.APIConnectivity;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.APIConnectivityListener;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.MessageSendingBinder;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.sensors.CompassManager;
import com.sap.sailing.android.tracking.app.sensors.CompassManager.MagneticHeadingListener;
import com.sap.sailing.android.tracking.app.services.TrackingService;
import com.sap.sailing.android.tracking.app.services.TrackingService.GPSQuality;
import com.sap.sailing.android.tracking.app.services.TrackingService.GPSQualityListener;
import com.sap.sailing.android.tracking.app.services.TrackingService.TrackingBinder;
import com.sap.sailing.android.tracking.app.services.TransmittingService;
import com.sap.sailing.android.tracking.app.services.TransmittingService.APIConnectivity;
import com.sap.sailing.android.tracking.app.services.TransmittingService.APIConnectivityListener;
import com.sap.sailing.android.tracking.app.services.TransmittingService.TransmittingBinder;
import com.sap.sailing.android.tracking.app.ui.fragments.CompassFragment;
import com.sap.sailing.android.tracking.app.ui.fragments.SpeedFragment;
import com.sap.sailing.android.tracking.app.ui.fragments.StopTrackingButtonFragment;
@@ -40,13 +39,13 @@ import com.sap.sailing.android.tracking.app.valueobjects.EventInfo;
import com.viewpagerindicator.CirclePageIndicator;
public class TrackingActivity extends BaseActivity implements GPSQualityListener,
APIConnectivityListener, MagneticHeadingListener {
APIConnectivityListener {
TrackingService trackingService;
boolean trackingServiceBound;
TransmittingService transmittingService;
boolean transmittingServiceBound;
MessageSendingService messageSendingService;
boolean messageSendingServiceBound;
private final static String TAG = TrackingActivity.class.getName();
private final static String SIS_TRACKING_FRAGMENT = "savedInstanceTrackingFragment";
@@ -56,11 +55,11 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
private ViewPager mPager;
private ScreenSlidePagerAdapter mPagerAdapter;
private String eventId;
private AppPreferences prefs;
private TrackingFragment trackingFragment;
private String checkinDigest;
private TrackingFragment trackingFragment;
private TimerRunnable timer;
private int lastViewPagerItem;
@@ -72,18 +71,16 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
* Thus they are cached here and the fragments can pick
* them up.
*/
public String lastSpeedIndicatorText = "";
public String lastCompassIndicatorText = "";
public String lastSpeedIndicatorText = "-";
public String lastCompassIndicatorText = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
prefs = new AppPreferences(this);
Intent intent = getIntent();
eventId = intent.getExtras().getString(
getString(R.string.tracking_activity_event_id_parameter));
checkinDigest = getIntent().getExtras().getString(getString(R.string.tracking_activity_checkin_digest_parameter));
setContentView(R.layout.fragment_hud_container);
@@ -96,7 +93,7 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
}
if (getSupportActionBar() != null) {
EventInfo eventInfo = DatabaseHelper.getInstance().getEventInfoWithLeaderboard(this, eventId);
EventInfo eventInfo = DatabaseHelper.getInstance().getEventInfoWithLeaderboardAndCompetitor(this, checkinDigest);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
toolbar.setNavigationIcon(R.drawable.sap_logo_64_sq);
@@ -124,8 +121,8 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
}
lastViewPagerItem = savedInstanceState.getInt(SIS_LAST_VIEWPAGER_ITEM);
lastSpeedIndicatorText = savedInstanceState.getString(SIS_LAST_SPEED_TEXT);
lastCompassIndicatorText = savedInstanceState.getString(SIS_LAST_COMPASS_TEXT);
lastSpeedIndicatorText = savedInstanceState.getString(SIS_LAST_SPEED_TEXT, "-");
lastCompassIndicatorText = savedInstanceState.getString(SIS_LAST_COMPASS_TEXT, "");
} else {
trackingFragment = new TrackingFragment();
}
@@ -150,7 +147,7 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
});
replaceFragment(R.id.tracking_linear_layout, trackingFragment);
ServiceHelper.getInstance().startTrackingService(this, eventId);
ServiceHelper.getInstance().startTrackingService(this, checkinDigest);
}
@Override
@@ -167,17 +164,16 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
}
@Override
protected void onStart() {
public void onStart() {
super.onStart();
Intent transmittingServiceIntent = new Intent(this, TransmittingService.class);
bindService(transmittingServiceIntent, transmittingServiceConnection,
Context.BIND_AUTO_CREATE);
Intent messageSendingServiceIntent = new Intent(this, MessageSendingService.class);
bindService(messageSendingServiceIntent, messageSendingServiceConnection, Context.BIND_AUTO_CREATE);
Intent trackingServiceIntent = new Intent(this, TrackingService.class);
bindService(trackingServiceIntent, trackingServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
public void onStop() {
super.onStop();
if (trackingServiceBound) {
trackingService.unregisterGPSQualityListener();
@@ -189,11 +185,11 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
}
}
if (transmittingServiceBound) {
transmittingService.unregisterAPIConnectivityListener();
unbindService(transmittingServiceConnection);
if (messageSendingServiceBound) {
messageSendingService.unregisterAPIConnectivityListener();
unbindService(messageSendingServiceConnection);
transmittingServiceBound = false;
messageSendingServiceBound = false;
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "Unbound transmitting Service");
@@ -204,7 +200,6 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
@Override
protected void onPause() {
super.onPause();
CompassManager.getInstance(this).unregisterListener();
timer.stop();
mPager = (ViewPager) findViewById(R.id.pager);
@@ -215,10 +210,6 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
protected void onResume() {
super.onResume();
if (prefs.getHeadingFromMagneticSensorPreferred()) {
CompassManager.getInstance(this).registerListener(this);
}
timer = new TimerRunnable();
timer.start();
@@ -229,6 +220,13 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
mPager.setAdapter(mPagerAdapter);
mPager.setCurrentItem(lastViewPagerItem);
LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean gpsEnabled = service.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (gpsEnabled == false)
{
showErrorPopup(R.string.warning, R.string.gps_turned_off);
}
}
@Override
@@ -270,24 +268,24 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection transmittingServiceConnection = new ServiceConnection() {
private ServiceConnection messageSendingServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// We've bound to LocalService, cast the IBinder and get
// LocalService instance
TransmittingBinder binder = (TransmittingBinder) service;
transmittingService = binder.getService();
transmittingServiceBound = true;
transmittingService.registerAPIConnectivityListener(TrackingActivity.this);
MessageSendingBinder binder = (MessageSendingBinder) service;
messageSendingService = binder.getService();
messageSendingServiceBound = true;
messageSendingService.registerAPIConnectivityListener(TrackingActivity.this);
if (BuildConfig.DEBUG) {
ExLog.i(TrackingActivity.this, TAG, "connected to transmitting service");
ExLog.i(TrackingActivity.this, TAG, "connected to message sending service");
}
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
transmittingServiceBound = false;
messageSendingServiceBound = false;
}
};
@@ -312,30 +310,6 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
trackingServiceBound = false;
}
};
@Override
public void magneticHeadingUpdated(float heading) {
// HudFragment hudFragment = (HudFragment)
// getSupportFragmentManager().findFragmentById(R.id.hud_content_frame);
if (prefs.getHeadingFromMagneticSensorPreferred()) {
if (mPager.getCurrentItem() == ScreenSlidePagerAdapter.VIEW_PAGER_FRAGMENT_COMPASS) {
ScreenSlidePagerAdapter viewPagerAdapter = getViewPagerAdapter();
if (viewPagerAdapter != null) {
CompassFragment compassFragment = viewPagerAdapter.getCompassFragment();
if (compassFragment != null && compassFragment.isAdded()) {
compassFragment.setBearing(heading);
}
}
}
} else {
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG,
"Received magnet compass update, even though prefs say get from GPS. Unregistering listener.");
}
CompassManager.getInstance(this).unregisterListener();
}
}
private ScreenSlidePagerAdapter getViewPagerAdapter() {
return (ScreenSlidePagerAdapter) mPager.getAdapter();
@@ -4,6 +4,7 @@ import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@@ -35,29 +36,24 @@ import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.VolleyError;
import com.sap.sailing.android.shared.data.http.HttpGetRequest;
import com.sap.sailing.android.shared.data.http.HttpJsonPostRequest;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.adapter.RegattaAdapter;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event;
import com.sap.sailing.android.tracking.app.ui.activities.RegattaActivity;
import com.sap.sailing.android.tracking.app.ui.activities.StartActivity;
import com.sap.sailing.android.tracking.app.utils.AppPreferences;
import com.sap.sailing.android.tracking.app.utils.CheckinHelper;
import com.sap.sailing.android.tracking.app.utils.DatabaseHelper;
import com.sap.sailing.android.tracking.app.utils.DatabaseHelper.GeneralDatabaseHelperException;
import com.sap.sailing.android.tracking.app.utils.JsonObjectOrStatusOnlyRequest;
import com.sap.sailing.android.tracking.app.utils.NetworkHelper;
import com.sap.sailing.android.tracking.app.utils.NetworkHelper.NetworkHelperError;
import com.sap.sailing.android.tracking.app.utils.NetworkHelper.NetworkHelperFailureListener;
import com.sap.sailing.android.tracking.app.utils.NetworkHelper.NetworkHelperSuccessListener;
import com.sap.sailing.android.tracking.app.utils.UniqueDeviceUuid;
import com.sap.sailing.android.tracking.app.utils.VolleyHelper;
import com.sap.sailing.android.tracking.app.valueobjects.CheckinData;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl;
@@ -66,7 +62,6 @@ public class HomeFragment extends BaseFragment implements
LoaderCallbacks<Cursor> {
private final static String TAG = HomeFragment.class.getName();
private final static String REQUEST_TAG = "request_homefragment";
private final static int REGATTA_LOADER = 1;
private AppPreferences prefs;
@@ -93,7 +88,6 @@ public class HomeFragment extends BaseFragment implements
if (noQrCodeButton != null) {
noQrCodeButton.setOnClickListener(new ClickListener());
}
ListView listView = (ListView) view.findViewById(R.id.listRegatta);
if (listView != null) {
@@ -114,7 +108,13 @@ public class HomeFragment extends BaseFragment implements
public void onResume() {
super.onResume();
getLoaderManager().restartLoader(REGATTA_LOADER, null, this);
String lastQRCode = prefs.getLastScannedQRCode();
if (lastQRCode != null) {
handleQRCode(lastQRCode);
}
}
private void showNoQRCodeMessage()
{
@@ -139,7 +139,7 @@ public class HomeFragment extends BaseFragment implements
startActivity(marketIntent);
} else {
Toast.makeText(getActivity(),
"PlayStore and Scanning not available.",
getString(R.string.error_play_store_and_scanning_not_available),
Toast.LENGTH_LONG).show();
}
return false;
@@ -150,20 +150,24 @@ public class HomeFragment extends BaseFragment implements
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
String scanResult = data.getStringExtra("SCAN_RESULT");
ExLog.i(getActivity(), TAG, "Parsing URI: " + scanResult);
Uri uri = Uri.parse(scanResult);
handleScannedOrUrlMatchedUri(uri);
prefs.setLastScannedQRCode(scanResult);
// handleQRCode is called in onResume()
} else if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(getActivity(), "Scanning canceled",
Toast.makeText(getActivity(), getString(R.string.scanning_cancelled),
Toast.LENGTH_LONG).show();
} else {
String templateString = getString(R.string.error_scanning_qrcode);
Toast.makeText(getActivity(),
"Error scanning QRCode (" + resultCode + ")",
templateString.replace("{result-code}", String.valueOf(resultCode)),
Toast.LENGTH_LONG).show();
}
}
private void handleQRCode(String qrCode) {
ExLog.i(getActivity(), TAG, "Parsing URI: " + qrCode);
Uri uri = Uri.parse(qrCode);
handleScannedOrUrlMatchedUri(uri);
}
public void handleScannedOrUrlMatchedUri(Uri uri) {
// TODO: assuming scheme is http, is this valid?
@@ -172,12 +176,11 @@ public class HomeFragment extends BaseFragment implements
scheme = "http";
}
final String uriStr = uri.toString();
final String server = scheme + "://" + uri.getHost();
final int port = (uri.getPort() == -1) ? 80 : uri.getPort();
final String hostWithPort = server + ":" + port;
prefs.setServerURL(hostWithPort);
String leaderboardNameFromQR;
try {
leaderboardNameFromQR = URLEncoder.encode(uri.getQueryParameter(CheckinHelper.LEADERBOARD_NAME),"UTF-8").replace("+", "%20");
@@ -186,12 +189,12 @@ public class HomeFragment extends BaseFragment implements
leaderboardNameFromQR = "";
} catch (NullPointerException e) {
ExLog.e(getActivity(), TAG, "Invalid Barcode (no leaderboard-name set): " + e.getMessage());
Toast.makeText(this.getActivity(), "Invalid QR Code", Toast.LENGTH_LONG).show();
Toast.makeText(this.getActivity(), getString(R.string.error_invalid_qr_code), Toast.LENGTH_LONG).show();
return;
}
final String competitorId = uri.getQueryParameter(CheckinHelper.COMPETITOR_ID);
final String checkinURLStr = prefs.getServerURL()
final String checkinURLStr = hostWithPort
+ prefs.getServerCheckinPath().replace("{leaderboard-name}",
leaderboardNameFromQR);
final String eventId = uri.getQueryParameter(CheckinHelper.EVENT_ID);
@@ -209,9 +212,9 @@ public class HomeFragment extends BaseFragment implements
final StartActivity startActivity = (StartActivity)getActivity();
final String getEventUrl = prefs.getServerURL() + prefs.getServerEventPath(eventId);
final String getLeaderboardUrl = prefs.getServerURL() + prefs.getServerLeaderboardPath(leaderboardName);
final String getCompetitorUrl = prefs.getServerURL() + prefs.getServerCompetitorPath(competitorId);
final String getEventUrl = hostWithPort + prefs.getServerEventPath(eventId);
final String getLeaderboardUrl = hostWithPort + prefs.getServerLeaderboardPath(leaderboardName);
final String getCompetitorUrl = hostWithPort + prefs.getServerCompetitorPath(competitorId);
startActivity.showProgressDialog(R.string.please_wait, R.string.getting_leaderboard);
@@ -354,6 +357,27 @@ public class HomeFragment extends BaseFragment implements
data.leaderboardName = leaderboardName;
data.deviceUid = deviceUuid
.getStringRepresentation();
try {
data.setCheckinDigestFromString(uriStr);
} catch (UnsupportedEncodingException e) {
ExLog.e(getActivity(),
TAG,
"Failed to get generate digest of qr-code string ("
+ uriStr + "). "
+ e.getMessage());
startActivity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
return;
} catch (NoSuchAlgorithmException e) {
ExLog.e(getActivity(),
TAG,
"Failed to get generate digest of qr-code string ("
+ uriStr + "). "
+ e.getMessage());
startActivity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
return;
}
displayUserConfirmationScreen(data);
@@ -419,12 +443,6 @@ public class HomeFragment extends BaseFragment implements
}
@Override
public void onDetach() {
super.onDetach();
VolleyHelper.getInstance(getActivity()).cancelRequest(REQUEST_TAG);
}
/**
* Display a confirmation-dialog in which the user confirms his full name
* and sail-id.
@@ -446,6 +464,7 @@ public class HomeFragment extends BaseFragment implements
@Override
public void onClick(DialogInterface dialog, int which) {
clearScannedQRCodeInPrefs();
checkInWithAPIAndDisplayTrackingActivity(checkinData);
}
@@ -454,15 +473,18 @@ public class HomeFragment extends BaseFragment implements
@Override
public void onClick(DialogInterface dialog, int which) {
clearScannedQRCodeInPrefs();
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
private void clearScannedQRCodeInPrefs() {
prefs.setLastScannedQRCode(null);
}
/**
* Perform a checkin request and launch RegattaAcitivity afterwards
@@ -471,14 +493,16 @@ public class HomeFragment extends BaseFragment implements
*
* @param deviceMappingData
*/
private void checkInWithAPIAndDisplayTrackingActivity(
CheckinData checkinData) {
if (DatabaseHelper.getInstance().eventLeaderboardCompetitorCombnationAvailable(
getActivity(), checkinData.eventId, checkinData.leaderboardName, checkinData.competitorId)) {
private void checkInWithAPIAndDisplayTrackingActivity(CheckinData checkinData) {
if (DatabaseHelper.getInstance().eventLeaderboardCompetitorCombnationAvailable(getActivity(), checkinData.checkinDigest)) {
try {
DatabaseHelper.getInstance().storeCheckinRow(getActivity(), checkinData.getEvent(),
checkinData.getCompetitor(), checkinData.getLeaderboard());
DatabaseHelper.getInstance().storeCheckinRow(
getActivity(),
checkinData.getEvent(),
checkinData.getCompetitor(),
checkinData.getLeaderboard());
adapter.notifyDataSetChanged();
} catch (GeneralDatabaseHelperException e) {
ExLog.e(getActivity(), TAG, "Batch insert failed: " + e.getMessage());
@@ -492,6 +516,7 @@ public class HomeFragment extends BaseFragment implements
} else {
ExLog.w(getActivity(), TAG,
"Combination of eventId, leaderboardName and competitorId already exists!");
Toast.makeText(getActivity(), getString(R.string.info_already_checked_in_this_qr_code), Toast.LENGTH_LONG).show();
}
performAPICheckin(checkinData);
@@ -509,21 +534,23 @@ public class HomeFragment extends BaseFragment implements
startActivity.showProgressDialog(R.string.please_wait, R.string.checking_in);
try {
JSONObject requestObject = CheckinHelper.getCheckinJson(
checkinData.competitorId, checkinData.deviceUid, "TODO!!",
date.getTime());
JSONObject requestObject = CheckinHelper.getCheckinJson(checkinData.competitorId,
checkinData.deviceUid, "TODO!!", date.getTime());
JsonObjectOrStatusOnlyRequest checkinRequest = new JsonObjectOrStatusOnlyRequest(checkinData.checkinURL,
requestObject, new CheckinListener(checkinData.leaderboardName,
checkinData.eventId, checkinData.competitorId),
new CheckinErrorListener(checkinData.leaderboardName, checkinData.eventId,
checkinData.competitorId));
VolleyHelper.getInstance(getActivity()).addRequest(checkinRequest);
HttpJsonPostRequest request = new HttpJsonPostRequest(new URL(checkinData.checkinURL),
requestObject.toString(), getActivity());
NetworkHelper.getInstance(getActivity()).executeHttpJsonRequestAsnchronously(
request,
new CheckinListener(checkinData.checkinDigest),
new CheckinErrorListener(checkinData.checkinDigest));
} catch (JSONException e) {
ExLog.e(getActivity(), TAG, "Failed to generate checkin JSON: " + e.getMessage());
displayAPIErrorRecommendRetry();
} catch (MalformedURLException e) {
ExLog.e(getActivity(), TAG,
"Failed to generate checkin JSON: " + e.getMessage());
"Failed to perform checkin, MalformedURLException: " + e.getMessage());
displayAPIErrorRecommendRetry();
}
}
@@ -573,14 +600,11 @@ public class HomeFragment extends BaseFragment implements
/**
* Start regatta activity.
*
* @param regattaName
* @param eventName
* @param checkinDigest
*/
private void startRegatta(String leaderboardName, String eventId, String competitorId) {
private void startRegatta(String checkinDigest) {
Intent intent = new Intent(getActivity(), RegattaActivity.class);
intent.putExtra(getString(R.string.leaderboard_name), leaderboardName);
intent.putExtra(getString(R.string.event_id), eventId);
intent.putExtra(getString(R.string.competitor_id), competitorId);
intent.putExtra(getString(R.string.checkin_digest), checkinDigest);
getActivity().startActivity(intent);
}
@@ -589,6 +613,7 @@ public class HomeFragment extends BaseFragment implements
switch (loaderId) {
case REGATTA_LOADER:
String[] projection = new String[] {
"events.event_checkin_digest",
"events.event_id",
"events._id", "events.event_name", "events.event_server",
"competitors.competitor_display_name",
@@ -656,76 +681,55 @@ public class HomeFragment extends BaseFragment implements
return;
}
Cursor cursor = (Cursor) adapter.getItem(position - 1); // -1,
// because
// there's
// a
// header
// row
prefs.setServerURL(cursor.getString(cursor.getColumnIndex(Event.EVENT_SERVER)));
// -1, because there's a header row
Cursor cursor = (Cursor) adapter.getItem(position - 1);
String leaderboardName = cursor.getString(cursor.getColumnIndex("leaderboard_name"));
String competitorId = cursor.getString(cursor.getColumnIndex("competitor_id"));
String eventId = cursor.getString(cursor.getColumnIndex("event_id"));
startRegatta(leaderboardName, eventId, competitorId);
String checkinDigest = cursor.getString(cursor.getColumnIndex("event_checkin_digest"));
startRegatta(checkinDigest);
}
}
private class CheckinListener implements Listener<JSONObject> {
private class CheckinListener implements NetworkHelperSuccessListener {
public String leaderboardName;
public String eventId;
public String competitorId;
public String checkinDigest;
public CheckinListener(String leaderboardName, String eventId, String competitorId) {
this.leaderboardName = leaderboardName;
this.eventId = eventId;
this.competitorId = competitorId;
public CheckinListener(String checkinDigest) {
this.checkinDigest = checkinDigest;
}
@Override
public void onResponse(JSONObject response) {
public void performAction(JSONObject response) {
StartActivity startActivity = (StartActivity)getActivity();
startActivity.dismissProgressDialog();
startRegatta(leaderboardName, eventId, competitorId);
startRegatta(checkinDigest);
}
}
private class CheckinErrorListener implements ErrorListener {
private class CheckinErrorListener implements NetworkHelperFailureListener {
public String leaderboardName;
public String eventId;
public String competitorId;
public String checkinDigest;
public CheckinErrorListener(String leaderboardName, String eventId, String competitorId) {
this.leaderboardName = leaderboardName;
this.eventId = eventId;
this.competitorId = competitorId;
public CheckinErrorListener(String checkinDigest) {
this.checkinDigest = checkinDigest;
}
@Override
public void onErrorResponse(VolleyError error) {
if (error.getMessage() != null)
public void performAction(NetworkHelperError e) {
if (e.getMessage() != null)
{
ExLog.e(getActivity(), TAG, error.getMessage().toString());
ExLog.e(getActivity(), TAG, e.getMessage().toString());
}
else
{
ExLog.e(getActivity(), TAG, "Unknown Error");
}
StartActivity startActivity = (StartActivity)getActivity();
startActivity.dismissProgressDialog();
startActivity.showErrorPopup(R.string.error, R.string.error_could_not_complete_operation_on_server_try_again);
DatabaseHelper.getInstance().deleteRegattaFromDatabase(getActivity(), eventId, leaderboardName, competitorId);
Toast.makeText(getActivity(), "Error while receiving server data", Toast.LENGTH_LONG).show();
DatabaseHelper.getInstance().deleteRegattaFromDatabase(getActivity(), checkinDigest);
Toast.makeText(getActivity(), getString(R.string.error_while_receiving_server_data), Toast.LENGTH_LONG).show();
}
}
@@ -1,144 +0,0 @@
package com.sap.sailing.android.tracking.app.ui.fragments;
import android.os.Bundle;
import android.text.method.MovementMethod;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.animation.BounceInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.sap.sailing.android.tracking.app.R;
public class HudFragment extends BaseFragment {
private static final String TAG = HudFragment.class.getName();
private float maxTranslateY;
private float minTranslateY;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
// View view = inflater.inflate(R.layout.fragment_hud, container, false);
//
// FrameLayout layout = (FrameLayout)getActivity().findViewById(R.id.hud_content_frame);
// layout.setOnTouchListener(new OverlayOnTouchListener());
//
// return view;
}
public void setHeading(float heading)
{
TextView headingLabel = (TextView)getActivity().findViewById(R.id.hud_hdg_label);
headingLabel.setText(getString(R.string.hud_heading_prefix) + String.valueOf(Math.round(heading)) + "°");
}
public void setSpeedOverGround(float speedInMetersPerSeconds)
{
float speedInKnots = speedInMetersPerSeconds * 1.9438444924574f;
TextView speedLabel = (TextView)getActivity().findViewById(R.id.hud_speed_label);
speedLabel.setText(getString(R.string.hud_speed_over_ground_prefix) + String.valueOf(speedInKnots) + "kn");
}
/**
* called from activity
*/
public void layoutOverlay() {
// FrameLayout layout = (FrameLayout)getActivity().findViewById(R.id.hud_content_frame);
// maxTranslateY = layout.getHeight()- dpToPx(40);
//
// View modeLabel = (View)getActivity().findViewById(R.id.mode_label);
// float heightOfOneDataRow = modeLabel.getHeight();
// minTranslateY = layout.getTranslationY() + (heightOfOneDataRow * 2);
//
// layout.setTranslationY(maxTranslateY);
}
class OverlayOnTouchListener implements OnTouchListener {
private int yDelta;
private int lastTranslation;
boolean isUp = false;
@Override
public boolean onTouch(View view, MotionEvent event) {
// if (view.getId() != R.id.hud_content_frame) return false;
final int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
int newTranslation = y - yDelta;
if (newTranslation > lastTranslation)
{
animateDown(view);
}
else
{
animateUp(view);
}
lastTranslation = newTranslation;
// if (newTranslation >= minTranslateY && newTranslation < maxTranslateY)
// {
// view.setTranslationY(newTranslation);
// }
break;
case MotionEvent.ACTION_UP:
// int treshold = Math.round((maxTranslateY - minTranslateY) / 2);
// if (lastTranslation < treshold) {
// animateUp(view);
// } else {
// animateDown(view);
// }
//
// long eventDuration =
// android.os.SystemClock.elapsedRealtime()
// - event.getDownTime();
//
// if (eventDuration < 200) {
// view.performClick();
// if (!isUp)
// {
// animateUp(view);
// }
// }
//
break;
case MotionEvent.ACTION_DOWN:
yDelta = y - (int)Math.round(view.getTranslationY());
break;
default:
break;
}
return true;
}
private void animateUp(View view) {
view.animate().translationY(minTranslateY).setDuration(100)
.setInterpolator(new LinearInterpolator()).start();
lastTranslation = (int) minTranslateY;
isUp = true;
}
private void animateDown(View view) {
view.animate().translationY(maxTranslateY).setDuration(500)
.setInterpolator(new BounceInterpolator()).start();
lastTranslation = (int) maxTranslateY;
isUp = false;
}
}
}
@@ -29,7 +29,6 @@ import android.widget.TextView;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event;
import com.sap.sailing.android.tracking.app.ui.activities.LeaderboardWebViewActivity;
import com.sap.sailing.android.tracking.app.ui.activities.RegattaActivity;
import com.sap.sailing.android.tracking.app.ui.activities.TrackingActivity;
@@ -44,7 +43,6 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
private final String CAMERA_TEMP_FILE = "cameraTempFile";
private boolean showingThankYouNote;
private TimerRunnable timer;
@@ -323,9 +321,8 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
private void startTrackingActivity() {
RegattaActivity regattaActivity = (RegattaActivity) getActivity();
Intent intent = new Intent(getActivity(), TrackingActivity.class);
intent.putExtra(
getString(R.string.tracking_activity_event_id_parameter),
regattaActivity.event.id);
String checkinDigest = regattaActivity.event.checkinDigest;
intent.putExtra(getString(R.string.tracking_activity_checkin_digest_parameter), checkinDigest);
getActivity().startActivity(intent);
}
@@ -34,7 +34,7 @@ public class SpeedFragment extends BaseFragment {
String formattedSpeed = df.format(speedInKnots);
TextView speedText = (TextView) getActivity().findViewById(R.id.speed_text_view);
speedText.setText(formattedSpeed + "kn");
speedText.setText(formattedSpeed);
TrackingActivity activity = (TrackingActivity) getActivity();
if (activity != null) {
@@ -53,7 +53,7 @@ public class SpeedFragment extends BaseFragment {
if (activity != null) {
speedText.setText(activity.lastSpeedIndicatorText);
} else {
speedText.setText("");
speedText.setText("0");
}
}
@@ -15,11 +15,11 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.APIConnectivity;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.customviews.SignalQualityIndicatorView;
import com.sap.sailing.android.tracking.app.services.TrackingService.GPSQuality;
import com.sap.sailing.android.tracking.app.services.TransmittingService.APIConnectivity;
import com.sap.sailing.android.tracking.app.ui.activities.TrackingActivity;
import com.sap.sailing.android.tracking.app.utils.AppPreferences;
@@ -143,7 +143,7 @@ public class TrackingFragment extends BaseFragment {
TextView textView = (TextView) getActivity().findViewById(
R.id.mode);
if (apiConnectivity == APIConnectivity.reachableTransmissionSuccess) {
if (apiConnectivity == APIConnectivity.transmissionSuccess) {
if (prefs.getEnergySavingEnabledByUser()) {
textView.setText(getString(R.string.tracking_mode_battery_saving));
textView.setTextColor(Color
@@ -158,7 +158,7 @@ public class TrackingFragment extends BaseFragment {
textView.setText(getString(R.string.tracking_mode_offline));
textView.setTextColor(Color
.parseColor(getString(R.color.sap_green)));
} else if (apiConnectivity == APIConnectivity.reachableTransmissionError) {
} else if (apiConnectivity == APIConnectivity.transmissionError) {
textView.setText(getString(R.string.tracking_mode_api_error));
textView.setTextColor(Color
.parseColor(getString(R.color.sap_red)));
@@ -218,7 +218,6 @@ public class TrackingFragment extends BaseFragment {
if (isAdded())
{
Activity activity = getActivity();
System.out.println("ACTIVITY: " + activity);
SignalQualityIndicatorView indicatorView = (SignalQualityIndicatorView) activity.findViewById(R.id.gps_quality_indicator);
indicatorView.setSignalQuality( quality.toInt() );
@@ -232,22 +231,19 @@ public class TrackingFragment extends BaseFragment {
}
public void setUnsentGPSFixesCount(final int count) {
if (isAdded()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
TextView unsentGpsFixesTextView = (TextView) getActivity()
.findViewById(R.id.tracking_unsent_fixes);
if (count == 0)
{
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isAdded()) {
TextView unsentGpsFixesTextView = (TextView) getActivity().findViewById(
R.id.tracking_unsent_fixes);
if (count == 0) {
unsentGpsFixesTextView.setText(getString(R.string.none));
}
else
{
unsentGpsFixesTextView.setText(String.valueOf(count));
} else {
unsentGpsFixesTextView.setText(String.valueOf(count));
}
}
});
}
}
});
}
}
@@ -30,10 +30,6 @@ public class AppPreferences {
return DeviceIdentifier;
}
public String getServerURL() {
return PrefUtils.getString(context, R.string.preference_server_url_key, R.string.preference_server_url_default);
}
public String getServerGpsFixesPostPath() {
return PrefUtils.getString(context, R.string.preference_server_gps_fixes_post_path, R.string.preference_server_gps_fixes_post_path);
}
@@ -70,10 +66,6 @@ public class AppPreferences {
return value == null ? -1 : Integer.valueOf(value);
}
public void setServerURL(String serverUrl) {
preferences.edit().putString(context.getString(R.string.preference_server_url_key), serverUrl).apply();
}
public String getEventId() {
return PrefUtils.getString(context, R.string.preference_eventid_key, R.string.preference_eventid_default);
}
@@ -104,8 +96,8 @@ public class AppPreferences {
return preferences.getBoolean(context.getString(R.string.preference_energy_saving_enabled_key), false);
}
public boolean getHeadingFromMagneticSensorPreferred() {
return preferences.getBoolean(context.getString(R.string.preference_heading_from_magnetic_key), true);
public boolean getDisplayHeadingWithSubtractedDeclination() {
return preferences.getBoolean(context.getString(R.string.preference_heading_with_declination_subtracted_enabled), true);
}
public void setTrackingTimerStarted(long milliseconds)
@@ -128,18 +120,33 @@ public class AppPreferences {
return preferences.getBoolean(context.getString(R.string.preference_tracker_is_tracking), false);
}
public void setTrackerIsTrackingEventId(String eventId)
public void setTrackerIsTrackingCheckinDigest(String checkinDigest)
{
preferences.edit().putString(context.getString(R.string.preference_tracker_is_tracking_event_id), eventId).commit();
preferences.edit().putString(context.getString(R.string.preference_tracker_is_tracking_checkin_digest), checkinDigest).commit();
}
public String getTrackerIsTrackingEventId()
public String getTrackerIsTrackingCheckinDigest()
{
return preferences.getString(context.getString(R.string.preference_tracker_is_tracking_event_id), null);
return preferences.getString(context.getString(R.string.preference_tracker_is_tracking_checkin_digest), null);
}
public static boolean getPrintDatabaseOperationDebugMessages()
{
return false;
}
public String getLastScannedQRCode()
{
return preferences.getString(context.getString(R.string.preference_last_scanned_qr_code), null);
}
public void setLastScannedQRCode(String lastQRCode)
{
preferences.edit().putString(context.getString(R.string.preference_last_scanned_qr_code), lastQRCode).commit();
}
public void setMessageResendInterval(int interval)
{
preferences.edit().putInt(context.getString(R.string.preference_messageResendIntervalMillis_key), interval).commit();
}
}
@@ -4,8 +4,8 @@ import org.json.JSONException;
import org.json.JSONObject;
public class CheckinHelper {
public static final String LEADERBOARD_NAME = "leaderboard_name";
public static final String COMPETITOR_ID = "competitor_id";
public static final String LEADERBOARD_NAME = "leaderboardName";
public static final String COMPETITOR_ID = "competitorId";
public static final String EVENT_ID = "event_id";
public static JSONObject getCheckinJson(String competitorId, String deviceUuid, String pushDeviceId, long fromMillis) throws JSONException
@@ -1,16 +1,13 @@
package com.sap.sailing.android.tracking.app.utils;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.BaseColumns;
@@ -19,13 +16,10 @@ import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Competitor;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.EventGpsFixesJoined;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.EventLeaderboardCompetitorJoined;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Leaderboard;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.LeaderboardsEventsJoined;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.SensorGps;
import com.sap.sailing.android.tracking.app.valueobjects.CompetitorInfo;
import com.sap.sailing.android.tracking.app.valueobjects.EventInfo;
import com.sap.sailing.android.tracking.app.valueobjects.GpsFix;
import com.sap.sailing.android.tracking.app.valueobjects.LeaderboardInfo;
public class DatabaseHelper {
@@ -43,168 +37,83 @@ public class DatabaseHelper {
return mInstance;
}
public List<GpsFix> getUnsentFixes(Context context, List<String> failedHosts, int updateBatchSize) {
String selectionClause = SensorGps.GPS_SYNCED + " = 0";
String projectionClauseStr = "events._id as _eid,sensor_gps.gps_time,sensor_gps.gps_latitude,"
+ "sensor_gps.gps_longitude,sensor_gps.gps_speed,sensor_gps.gps_bearing,sensor_gps.gps_synced,"
+ "events.event_server,sensor_gps._id as _gid";
String[] projectionClause = projectionClauseStr.split(",");
String sortAndLimitClause = SensorGps.GPS_TIME + " DESC LIMIT "
+ updateBatchSize;
if (failedHosts != null) {
if (failedHosts.size() > 0) {
StringBuffer buf = new StringBuffer();
buf.append("( ");
for (String failedHost : failedHosts) {
buf.append("\"" + failedHost + "\",");
}
// remove the last comma
buf.setLength(buf.length() - 1);
buf.append(" )");
selectionClause += " AND " + Event.EVENT_SERVER + " NOT IN "
+ buf.toString();
}
}
ArrayList<GpsFix> list = new ArrayList<GpsFix>();
Cursor cur = context.getContentResolver().query(
EventGpsFixesJoined.CONTENT_URI, projectionClause,
selectionClause, null, sortAndLimitClause);
while (cur.moveToNext()) {
GpsFix gpsFix = new GpsFix();
gpsFix.id = cur.getInt(cur.getColumnIndex("_gid"));
gpsFix.timestamp = cur.getLong(cur.getColumnIndex(SensorGps.GPS_TIME));
gpsFix.latitude = cur.getDouble(cur.getColumnIndex(SensorGps.GPS_LATITUDE));
gpsFix.longitude = cur.getDouble(cur.getColumnIndex(SensorGps.GPS_LONGITUDE));
gpsFix.speed = cur.getDouble(cur.getColumnIndex(SensorGps.GPS_SPEED));
gpsFix.course = cur.getDouble(cur.getColumnIndex(SensorGps.GPS_BEARING));
gpsFix.synced = cur.getInt(cur.getColumnIndex(SensorGps.GPS_SYNCED));
gpsFix.host = cur.getString(cur.getColumnIndex(Event.EVENT_SERVER));
gpsFix.eventId = cur.getString(cur.getColumnIndex("_eid"));
list.add(gpsFix);
if (list.size() >= updateBatchSize) {
break;
}
}
cur.close();
return list;
}
public int getNumberOfUnsentGPSFixes(Context context)
{
String selectionClause = SensorGps.GPS_SYNCED + " = 0";
String sortAndLimitClause = SensorGps.GPS_TIME + " DESC LIMIT 1";
int eventId = -1;
Cursor cur = context.getContentResolver().query(
EventGpsFixesJoined.CONTENT_URI,
new String[] { "events._id as _eid" },
selectionClause,
null,
sortAndLimitClause);
while (cur.moveToNext()) {
eventId = cur.getInt(0);
}
cur.close();
if (eventId == -1)
{
if (BuildConfig.DEBUG)
{
ExLog.i(context, TAG, "no event id, reporting 0 gps-fixes.");
}
return 0;
}
String selectionClause2 = "events._id = " + eventId;
Cursor countCursor = context.getContentResolver().query(
EventGpsFixesJoined.CONTENT_URI,
new String[] { "count(*) AS count" }, selectionClause2, null, null);
countCursor.moveToFirst();
int count = countCursor.getInt(0);
countCursor.close();
return count;
}
public int deleteGpsFixes(Context context, String[] fixIdStrings)
{
int numDeleted = 0;
for (String idStr: fixIdStrings)
{
ContentValues updateValues = new ContentValues();
updateValues.put(SensorGps.GPS_SYNCED, 1);
Uri uri = ContentUris.withAppendedId(SensorGps.CONTENT_URI, Long.parseLong(idStr));
numDeleted = context.getContentResolver().delete(uri, null, null);
}
return numDeleted;
}
public long getRowIdForEventId(Context context, String eventId)
public long getEventRowIdForCheckinDigest(Context context, String checkinDigest)
{
int result = 0;
ContentResolver cr = context.getContentResolver();
Cursor cursor = cr.query(Event.CONTENT_URI, null, "event_id = \"" + eventId + "\"", null, null);
Cursor cursor = cr.query(Event.CONTENT_URI, null, Event.EVENT_CHECKIN_DIGEST + " = \"" + checkinDigest + "\"", null, null);
cursor.moveToFirst();
result = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID));
cursor.close();
return result;
}
public void insertGPSFix(Context context, double lat, double lon, double speed,
double bearing, String provider, long timestamp, long eventRowId) {
ContentResolver cr = context.getContentResolver();
ContentValues cv = new ContentValues();
cv.put(SensorGps.GPS_LATITUDE, lat);
cv.put(SensorGps.GPS_LONGITUDE, lon);
cv.put(SensorGps.GPS_PROVIDER, provider);
cv.put(SensorGps.GPS_SPEED, speed);
cv.put(SensorGps.GPS_TIME, timestamp);
cv.put(SensorGps.GPS_BEARING, bearing);
cv.put(SensorGps.GPS_EVENT_FK, eventRowId);
cr.insert(SensorGps.CONTENT_URI, cv);
}
// public EventInfo getEventInfoWithLeaderboard(Context context, String eventId) {
// EventInfo result = new EventInfo();
//
// ContentResolver cr = context.getContentResolver();
// String projectionStr = "events._id,leaderboards.leaderboard_name,events.event_name";
// String[] projection = projectionStr.split(",");
// Cursor cursor = cr.query(LeaderboardsEventsJoined.CONTENT_URI, projection, "events.event_id = \"" + eventId + "\"", null, null);
// if (cursor.moveToFirst())
// {
// result.name = cursor.getString(cursor.getColumnIndex("event_name"));
// result.leaderboardName = cursor.getString(cursor.getColumnIndex("leaderboard_name"));
// }
//
// cursor.close();
// return result;
// }
public EventInfo getEventInfoWithLeaderboard(Context context, String eventId) {
public EventInfo getEventInfoWithLeaderboardAndCompetitor(Context context, String checkinDigest) {
EventInfo result = new EventInfo();
ContentResolver cr = context.getContentResolver();
String projectionStr = "events._id,leaderboards.leaderboard_name,events.event_name";
String[] projection = projectionStr.split(",");
Cursor cursor = cr.query(LeaderboardsEventsJoined.CONTENT_URI, projection, "events.event_id = \"" + eventId + "\"", null, null);
if (cursor.moveToFirst())
String projectionStr = "events._id ,leaderboards.leaderboard_name, events.event_id,"
+ " events.event_name, competitors.competitor_id";
String[] projection = projectionStr.split(",");
Cursor cursor = cr
.query(EventLeaderboardCompetitorJoined.CONTENT_URI, projection, "events."
+ Event.EVENT_CHECKIN_DIGEST + " = \"" + checkinDigest + "\"", null, null);
if (cursor.moveToFirst())
{
result.name = cursor.getString(cursor.getColumnIndex("event_name"));
result.leaderboardName = cursor.getString(cursor.getColumnIndex("leaderboard_name"));
result.competitorId = cursor.getString(cursor.getColumnIndex("competitor_id"));
result.id = cursor.getString(cursor.getColumnIndex("event_id"));
}
cursor.close();
return result;
}
public EventInfo getEventInfo(Context context, String eventId) {
// public EventInfo getEventInfoWithLeaderboardAndCompetitor(Context context, String eventId) {
// EventInfo result = new EventInfo();
//
// ContentResolver cr = context.getContentResolver();
// String projectionStr = "events._id,leaderboards.leaderboard_name,events.event_name, competitors.competitor_id";
// String[] projection = projectionStr.split(",");
// Cursor cursor = cr.query(EventLeaderboardCompetitorJoined.CONTENT_URI, projection, "events.event_id = \"" + eventId + "\"", null, null);
// if (cursor.moveToFirst())
// {
// result.name = cursor.getString(cursor.getColumnIndex("event_name"));
// result.leaderboardName = cursor.getString(cursor.getColumnIndex("leaderboard_name"));
// result.competitorId = cursor.getString(cursor.getColumnIndex("competitor_id"));
// }
//
// cursor.close();
// return result;
// }
//
public EventInfo getEventInfo(Context context, String checkinDigest) {
EventInfo event = new EventInfo();
event.id = eventId;
event.checkinDigest = checkinDigest;
Cursor cursor = context.getContentResolver().query(Event.CONTENT_URI,
null, "event_id = \"" + eventId + "\"", null, null);
null, Event.EVENT_CHECKIN_DIGEST + " = \"" + checkinDigest + "\"", null, null);
if (cursor.moveToFirst()) {
event.name = cursor.getString(cursor.getColumnIndex(Event.EVENT_NAME));
@@ -213,37 +122,76 @@ public class DatabaseHelper {
event.endMillis = cursor.getLong(cursor.getColumnIndex(Event.EVENT_DATE_END));
event.server = cursor.getString(cursor.getColumnIndex(Event.EVENT_SERVER));
event.rowId = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID));
event.id = cursor.getString(cursor.getColumnIndex(Event.EVENT_ID));
}
cursor.close();
return event;
}
public CompetitorInfo getCompetitor(Context context, String competitorId)
// public CompetitorInfo getCompetitor(Context context, String competitorId)
// {
// CompetitorInfo competitor = new CompetitorInfo();
// competitor.id = competitorId;
//
// Cursor cursor = context.getContentResolver().query(Competitor.CONTENT_URI, null, "competitor_id = \"" + competitorId + "\"", null, null);
// if (cursor.moveToFirst()) {
// competitor.name = cursor.getString(cursor.getColumnIndex(Competitor.COMPETITOR_DISPLAY_NAME));
// competitor.countryCode = cursor.getString(cursor.getColumnIndex(Competitor.COMPETITOR_COUNTRY_CODE));
// competitor.sailId = cursor.getString(cursor.getColumnIndex(Competitor.COMPETITOR_SAIL_ID));
// competitor.rowId = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID));
// }
//
// cursor.close();
// return competitor;
// }
public CompetitorInfo getCompetitor(Context context, String checkinDigest)
{
CompetitorInfo competitor = new CompetitorInfo();
competitor.id = competitorId;
competitor.checkinDigest = checkinDigest;
Cursor cursor = context.getContentResolver().query(Competitor.CONTENT_URI, null, "competitor_id = \"" + competitorId + "\"", null, null);
Cursor cursor = context.getContentResolver().query(Competitor.CONTENT_URI, null, Competitor.COMPETITOR_CHECKIN_DIGEST + " = \"" + checkinDigest + "\"", null, null);
if (cursor.moveToFirst()) {
competitor.name = cursor.getString(cursor.getColumnIndex(Competitor.COMPETITOR_DISPLAY_NAME));
competitor.countryCode = cursor.getString(cursor.getColumnIndex(Competitor.COMPETITOR_COUNTRY_CODE));
competitor.sailId = cursor.getString(cursor.getColumnIndex(Competitor.COMPETITOR_SAIL_ID));
competitor.rowId = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID));
competitor.id = cursor.getString(cursor.getColumnIndex(Competitor.COMPETITOR_CHECKIN_DIGEST));
}
cursor.close();
return competitor;
}
public LeaderboardInfo getLeaderboard(Context context, String leaderboardName)
// public LeaderboardInfo getLeaderboard(Context context, String leaderboardName)
// {
// LeaderboardInfo leaderboard = new LeaderboardInfo();
// leaderboard.name = leaderboardName;
//
// Cursor lc = context.getContentResolver().query(Leaderboard.CONTENT_URI, null, "leaderboard_name = \"" + leaderboardName + "\"", null, null);
// if (lc.moveToFirst()) {
// leaderboard.rowId = lc.getInt(lc.getColumnIndex(BaseColumns._ID));
// leaderboard.checkinDigest = lc.getString(lc.getColumnIndex(Leaderboard.LEADERBOARD_CHECKIN_DIGEST));
// }
//
// lc.close();
//
// return leaderboard;
// }
public LeaderboardInfo getLeaderboard(Context context, String checkinDigest)
{
LeaderboardInfo leaderboard = new LeaderboardInfo();
leaderboard.name = leaderboardName;
leaderboard.checkinDigest = checkinDigest;
Cursor lc = context.getContentResolver().query(Leaderboard.CONTENT_URI, null, "leaderboard_name = \"" + leaderboardName + "\"", null, null);
Cursor lc = context.getContentResolver().query(
Leaderboard.CONTENT_URI,
null,
Leaderboard.LEADERBOARD_CHECKIN_DIGEST + " = \"" + checkinDigest + "\"", null, null);
if (lc.moveToFirst()) {
leaderboard.rowId = lc.getInt(lc.getColumnIndex(BaseColumns._ID));
leaderboard.name = lc.getString(lc.getColumnIndex(Leaderboard.LEADERBOARD_NAME));
}
lc.close();
@@ -251,29 +199,29 @@ public class DatabaseHelper {
return leaderboard;
}
public void deleteRegattaFromDatabase(Context context, String eventId, String competitorId, String leaderboardName)
{
ContentResolver cr = context.getContentResolver();
int d1 = cr.delete(Event.CONTENT_URI, Event.EVENT_ID + " = \"" + eventId + "\"", null);
int d2 = cr.delete(Competitor.CONTENT_URI, Competitor.COMPETITOR_ID + " = \"" + competitorId + "\"", null);
int d3 = cr.delete(Leaderboard.CONTENT_URI, Leaderboard.LEADERBOARD_NAME + " = \"" + leaderboardName + "\"", null);
if (BuildConfig.DEBUG)
{
ExLog.i(context, TAG, "Checkout, number of events deleted: " + d1);
ExLog.i(context, TAG, "Checkout, number of competitors deleted: " + d2);
ExLog.i(context, TAG, "Checkout, number of leaderbards deleted: " + d3);
}
}
// public void deleteRegattaFromDatabase(Context context, String eventId, String competitorId, String leaderboardName)
// {
// ContentResolver cr = context.getContentResolver();
//
// int d1 = cr.delete(Event.CONTENT_URI, Event.EVENT_ID + " = \"" + eventId + "\"", null);
// int d2 = cr.delete(Competitor.CONTENT_URI, Competitor.COMPETITOR_ID + " = \"" + competitorId + "\"", null);
// int d3 = cr.delete(Leaderboard.CONTENT_URI, Leaderboard.LEADERBOARD_NAME + " = \"" + leaderboardName + "\"", null);
//
// if (BuildConfig.DEBUG)
// {
// ExLog.i(context, TAG, "Checkout, number of events deleted: " + d1);
// ExLog.i(context, TAG, "Checkout, number of competitors deleted: " + d2);
// ExLog.i(context, TAG, "Checkout, number of leaderbards deleted: " + d3);
// }
// }
public void deleteRegattaFromDatabase(Context context, EventInfo event, CompetitorInfo competitor, LeaderboardInfo leaderboard)
public void deleteRegattaFromDatabase(Context context, String checkinDigest)
{
ContentResolver cr = context.getContentResolver();
int d1 = cr.delete(Event.CONTENT_URI, BaseColumns._ID + " = " + event.rowId, null);
int d2 = cr.delete(Competitor.CONTENT_URI, BaseColumns._ID + " = " + competitor.rowId, null);
int d3 = cr.delete(Leaderboard.CONTENT_URI, BaseColumns._ID + " = " + leaderboard.rowId, null);
int d1 = cr.delete(Event.CONTENT_URI, Event.EVENT_CHECKIN_DIGEST + " = \"" + checkinDigest + "\"", null);
int d2 = cr.delete(Competitor.CONTENT_URI, Competitor.COMPETITOR_CHECKIN_DIGEST + " = \"" + checkinDigest + "\"", null);
int d3 = cr.delete(Leaderboard.CONTENT_URI, Leaderboard.LEADERBOARD_CHECKIN_DIGEST + " = \"" + checkinDigest + "\"", null);
if (BuildConfig.DEBUG)
{
@@ -284,7 +232,8 @@ public class DatabaseHelper {
}
/**
* Attempt to check in to a race.
* When checking in, store info on the event, the competitor and the leaderboard
* in the database.
*
* @param context
* @param event
@@ -298,27 +247,16 @@ public class DatabaseHelper {
public void storeCheckinRow(Context context, EventInfo event, CompetitorInfo competitor,
LeaderboardInfo leaderboard) throws GeneralDatabaseHelperException {
// inserting leaderboard first in order to get the ID.
// This should be atomic, but couldn't get withValueBackReference to
// work yet.
// inserting leaderboard first
ContentResolver cr = context.getContentResolver();
ContentValues clv = new ContentValues();
clv.put(Leaderboard.LEADERBOARD_NAME, leaderboard.name);
clv.put(Leaderboard.LEADERBOARD_CHECKIN_DIGEST, leaderboard.checkinDigest);
cr.insert(Leaderboard.CONTENT_URI, clv);
Cursor cur = cr.query(Leaderboard.CONTENT_URI, null, null, null, null);
long lastLeaderboardId = 0;
if (cur.moveToLast()) {
lastLeaderboardId = cur.getLong(cur.getColumnIndex(BaseColumns._ID));
}
cur.close();
// now, with the leaderboard id, insert event and competitor
// todo: fix this so its atomic.
// now insert event
ArrayList<ContentProviderOperation> opList = new ArrayList<ContentProviderOperation>();
@@ -329,9 +267,11 @@ public class DatabaseHelper {
cev.put(Event.EVENT_DATE_END, event.endMillis);
cev.put(Event.EVENT_SERVER, event.server);
cev.put(Event.EVENT_IMAGE_URL, event.imageUrl);
cev.put(Event.EVENT_LEADERBOARD_FK, lastLeaderboardId);
cev.put(Event.EVENT_CHECKIN_DIGEST, event.checkinDigest);
opList.add(ContentProviderOperation.newInsert(Event.CONTENT_URI).withValues(cev).build());
// competitor
ContentValues ccv = new ContentValues();
@@ -340,7 +280,7 @@ public class DatabaseHelper {
ccv.put(Competitor.COMPETITOR_ID, competitor.id);
ccv.put(Competitor.COMPETITOR_NATIONALITY, competitor.nationality);
ccv.put(Competitor.COMPETITOR_SAIL_ID, competitor.sailId);
ccv.put(Competitor.COMPETITOR_LEADERBOARD_FK, lastLeaderboardId);
ccv.put(Competitor.COMPETITOR_CHECKIN_DIGEST, competitor.checkinDigest);
opList.add(ContentProviderOperation.newInsert(Competitor.CONTENT_URI).withValues(ccv).build());
@@ -354,26 +294,29 @@ public class DatabaseHelper {
}
/**
* Return true if the combination of eventId, leaderboardName and
* competitorId does not exist in the DB.
* Return true if the combination of event, leaderboard and
* competitor does not exist in the DB. (based on the digest of the checkin-
* url obtained from the QR-code.)
*
* @param eventId
* @param checkinDigest SHA-256 digest of QR-code string
* @param leaderboardName
* @param competitorId
* @return
*/
public boolean eventLeaderboardCompetitorCombnationAvailable(Context context,
String eventId, String leaderboardName, String competitorId) {
public boolean eventLeaderboardCompetitorCombnationAvailable(Context context, String checkinDigest) {
ContentResolver cr = context.getContentResolver();
String sel = "leaderboards.leaderboard_name = \"" + leaderboardName
+ "\" AND competitors.competitor_id = \"" + competitorId
+ "\" AND events.event_id = \"" + eventId + "\"";
String sel = "leaderboards.leaderboard_checkin_digest = \"" + checkinDigest
+ "\" AND competitors.competitor_checkin_digest = \"" + checkinDigest
+ "\" AND events.event_checkin_digest = \"" + checkinDigest + "\"";
int count = cr.query(
Cursor cursor = cr.query(
AnalyticsContract.EventLeaderboardCompetitorJoined.CONTENT_URI,
null, sel, null, null).getCount();
null, sel, null, null);
int count = cursor.getCount();
cursor.close();
return count == 0;
}
@@ -1,48 +0,0 @@
package com.sap.sailing.android.tracking.app.utils;
import java.io.UnsupportedEncodingException;
import org.json.JSONException;
import org.json.JSONObject;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.JsonObjectRequest;
public class JsonObjectOrStatusOnlyRequest extends JsonObjectRequest {
public JsonObjectOrStatusOnlyRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener,
ErrorListener errorListener) {
super(url, jsonRequest, listener, errorListener);
}
public JsonObjectOrStatusOnlyRequest(int method, String url, JSONObject jsonRequest,
Listener<JSONObject> listener, ErrorListener errorListener) {
super(method, url, jsonRequest, listener, errorListener);
}
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
// here's the new code, if jsonString.length() == 0 don't parse
if (jsonString.length() == 0) {
return Response.success(null, HttpHeaderParser.parseCacheHeaders(response));
}
// end of patch
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
}
@@ -18,15 +18,16 @@ public class NetworkHelper {
private final static String TAG = NetworkHelper.class.getName();
static NetworkHelper mInstance;
static Context mContext;
protected static NetworkHelper mInstance;
protected static Context mContext;
public static NetworkHelper getInstance(Context context) {
if (mInstance == null)
{
mInstance = new NetworkHelper();
mContext = context;
mContext = context.getApplicationContext();
}
return mInstance;
}
@@ -38,7 +39,7 @@ public class NetworkHelper {
NetworkHelperSuccessListener successListener, NetworkHelperFailureListener failureListener) {
NetworkRequestTask task = new NetworkRequestTask(successListener, failureListener);
task.execute(request);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, request);
}
private class NetworkRequestTask extends AsyncTask<HttpRequest, Void, Void>
@@ -65,7 +66,7 @@ public class NetworkHelper {
String responseStr = readStream(stream);
response = null;
if (responseStr.length() > 0) {
response = new JSONObject(responseStr);
}
@@ -5,7 +5,6 @@ import android.content.Intent;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.services.TrackingService;
import com.sap.sailing.android.tracking.app.services.TransmittingService;
/**
* Helper class that starts services.
@@ -14,7 +13,7 @@ import com.sap.sailing.android.tracking.app.services.TransmittingService;
*/
public class ServiceHelper {
private final static String TAG = ServiceHelper.class.getName();
//private final static String TAG = ServiceHelper.class.getName();
protected static ServiceHelper mInstance;
@@ -24,28 +23,17 @@ public class ServiceHelper {
}
return mInstance;
}
/**
* Start transmitting service. It should be run at least once when the app
* starts for the first time, and every time, when a tracking gps-fix is
* stored, to ensure it gets sent to the API-server.
*/
public void startTransmittingService(Context context) {
Intent intent = new Intent(context, TransmittingService.class);
intent.setAction(context.getString(R.string.transmitting_service_start));
context.startService(intent);
}
/**
* Start tracking service with a given eventId. The eventId is used to determine
* Start tracking service with a given checkinDigest. The checkinDigest is used to determine
* the event from the database in order to set the host-address correctly.
* @param eventId id of the event (not the row-id)
* @param checkinDigest of the event
*/
public void startTrackingService(Context context, String eventId)
public void startTrackingService(Context context, String checkinDigest)
{
Intent intent = new Intent(context, TrackingService.class);
intent.setAction(context.getString(R.string.tracking_service_start));
intent.putExtra(context.getString(R.string.tracking_service_event_id_parameter), eventId);
intent.putExtra(context.getString(R.string.tracking_service_checkin_digest_parameter), checkinDigest);
context.startService(intent);
}
@@ -9,7 +9,6 @@ import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Competitor;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Leaderboard;
import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.SensorGps;
public class SqlDebugHelper {
@@ -28,6 +27,7 @@ public class SqlDebugHelper {
StringBuffer sb = new StringBuffer();
sb.append("_ID: " + c1.getString(c1.getColumnIndex(BaseColumns._ID)) + ", ");
sb.append("NAME: " + c1.getString(c1.getColumnIndex(Leaderboard.LEADERBOARD_NAME)) + "\n");
sb.append("DIGEST: " + c1.getString(c1.getColumnIndex(Leaderboard.LEADERBOARD_CHECKIN_DIGEST)) + "\n");
ExLog.w(context, TAG, "Leaderboard: " + sb.toString());
}
@@ -47,6 +47,7 @@ public class SqlDebugHelper {
sb.append("DATE END: " + c2.getString(c2.getColumnIndex(Event.EVENT_DATE_END)) + ", ");
sb.append("IMAGE-URL: " + c2.getString(c2.getColumnIndex(Event.EVENT_IMAGE_URL)) + ", ");
sb.append("SERVER: " + c2.getString(c2.getColumnIndex(Event.EVENT_SERVER)) + "\n");
sb.append("DIGEST: " + c2.getString(c2.getColumnIndex(Event.EVENT_CHECKIN_DIGEST)) + "\n");
ExLog.w(context, TAG, "Event: " + sb.toString());
}
@@ -66,15 +67,13 @@ public class SqlDebugHelper {
sb.append("NATIONALITY: " + c3.getString(c3.getColumnIndex(Competitor.COMPETITOR_NATIONALITY)) + ", ");
sb.append("COUNTRY CODE: " + c3.getString(c3.getColumnIndex(Competitor.COMPETITOR_COUNTRY_CODE)) + ", ");
sb.append("SAIL-ID: " + c3.getString(c3.getColumnIndex(Competitor.COMPETITOR_SAIL_ID)) + "\n");
sb.append("DIGEST: " + c3.getString(c3.getColumnIndex(Competitor.COMPETITOR_CHECKIN_DIGEST)) + "\n");
ExLog.w(context, TAG, "Competitor: " + sb.toString());
}
c3.close();
Cursor c4 = cr.query(SensorGps.CONTENT_URI, null, null, null, null);
ExLog.w(context, TAG, "--- NUMBER OF GPS FIXES IN DB: " + c4.getCount());
ExLog.w(context, TAG, "--- END OF SQL PRINTOUT" );
}
}
@@ -1,65 +0,0 @@
package com.sap.sailing.android.tracking.app.utils;
import org.json.JSONObject;
import android.app.Application;
import android.content.Context;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
public class VolleyHelper extends Application {
private static final String TAG = VolleyHelper.class.getName();
private RequestQueue mRequestQueue;
private Context mContext;
protected static VolleyHelper mInstance;
protected VolleyHelper(Context context){
super();
mContext = context;
}
public static synchronized VolleyHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new VolleyHelper(context);
}
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(mContext);
}
return mRequestQueue;
}
public <T> void enqueueRequest(String urlStr, JSONObject requestJsonObject,
Listener<JSONObject> listener, ErrorListener errorListener) {
addRequest(new JsonObjectOrStatusOnlyRequest(urlStr, requestJsonObject, listener,
errorListener));
}
public <T> void addRequest(Request<T> request) {
addRequest(request, TAG);
}
public <T> void addRequest(Request<T> request, Object tag) {
request.setTag((tag == null) ? TAG : tag);
getRequestQueue().add(request);
}
public void cancelRequest(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll((tag == null) ? TAG : tag);
}
}
}
@@ -1,5 +1,9 @@
package com.sap.sailing.android.tracking.app.valueobjects;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class CheckinData {
// public String gcmId;
public String leaderboardName;
@@ -16,6 +20,17 @@ public class CheckinData {
public String competitorNationality;
public String competitorCountryCode;
public String deviceUid;
public String checkinDigest;
public void setCheckinDigestFromString(String checkinString) throws UnsupportedEncodingException, NoSuchAlgorithmException
{
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(checkinString.getBytes("UTF-8"));
byte[] digest = md.digest();
StringBuffer buf = new StringBuffer();
for (byte byt : digest) buf.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1));
checkinDigest = buf.toString();
}
public EventInfo getEvent()
{
@@ -26,6 +41,7 @@ public class CheckinData {
event.endMillis = Long.parseLong(eventEndDateStr);
event.imageUrl = eventFirstImageUrl;
event.server = eventServerUrl;
event.checkinDigest = checkinDigest;
return event;
}
@@ -33,6 +49,7 @@ public class CheckinData {
{
LeaderboardInfo leaderboard = new LeaderboardInfo();
leaderboard.name = leaderboardName;
leaderboard.checkinDigest = checkinDigest;
return leaderboard;
}
@@ -44,6 +61,7 @@ public class CheckinData {
competitor.sailId = competitorSailId;
competitor.nationality = competitorNationality;
competitor.countryCode = competitorCountryCode;
competitor.checkinDigest = checkinDigest;
return competitor;
}
}
@@ -7,6 +7,7 @@ public class CompetitorInfo {
public String countryCode;
public String nationality;
public String sailId;
public String checkinDigest;
public int rowId;
}
@@ -5,18 +5,22 @@ public class EventInfo {
public String id;
public String name;
public String leaderboardName; // when using join-query
public String competitorId; // when using join-query
public String imageUrl;
public long startMillis;
public long endMillis;
public int rowId;
public String checkinDigest;
public String server;
@Override
public String toString() {
return "eventName: " + name + ", leaderboardName: "
+ leaderboardName + ", eventImageUrl: " + imageUrl
+ leaderboardName + ", competitorId: " + competitorId
+ ", eventImageUrl: " + imageUrl
+ ", eventStartMillis: " + startMillis
+ ", eventEndMillis: " + endMillis + ", eventRowId: "
+ ", checkinDigest: " + checkinDigest
+ rowId + ", server: " + server;
}
}
@@ -3,5 +3,6 @@ package com.sap.sailing.android.tracking.app.valueobjects;
public class LeaderboardInfo {
public String name;
public String checkinDigest;
public int rowId;
}
@@ -199,7 +199,7 @@ public class CirclePageIndicator extends View implements PageIndicator {
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mViewPager == null) {
if (mViewPager == null || mViewPager.getAdapter() == null) {
return;
}
final int count = mViewPager.getAdapter().getCount();
-3
View File
@@ -9,6 +9,3 @@ include ":java:com.sap.sse.datamining.shared"
include ":mobile:com.sap.sailing.android.shared"
include ":mobile:com.sap.sailing.android.tracking.app"
include ":mobile:com.sap.sailing.racecommittee.app"
/* to be removed later */
include ":mobile:google-volley_lib"