Merge branch 'android_apps' of ssh://sapsailing.com/home/trac/git into android_apps

This commit is contained in:
Frank Urbainsky committed 2014-12-17 14:11:52 +01:00
commit 9d5fa2efe1
23 files changed
+1477 -604

No files matched your search

@@ -0,0 +1,67 @@
package com.sap.sailing.android.tracking.app.test;
import com.sap.sailing.android.tracking.app.valueobjects.CheckinData;
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.LeaderboardInfo;
import junit.framework.TestCase;
public class CheckinDataTest extends TestCase {
private CheckinData checkinData;
@Override
protected void setUp() throws Exception {
checkinData = new CheckinData();
checkinData.leaderboardName = "lt";
checkinData.eventId = "et";
checkinData.eventName = "ent";
checkinData.eventStartDateStr = "123";
checkinData.eventEndDateStr = "456";
checkinData.eventFirstImageUrl = "http://1.2.3.4/test.png";
checkinData.eventServerUrl = "http://5.6.7.8";
checkinData.checkinURL = "http://9.1.2.3/checkin";
checkinData.competitorCountryCode = "DE";
checkinData.competitorName = "cn";
checkinData.competitorNationality = "GER";
checkinData.competitorSailId = "SAIL 1";
checkinData.competitorId = "1-2-3-4";
checkinData.deviceUid = "0011223344";
super.setUp();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
public void testGetEvent()
{
EventInfo event = checkinData.getEvent();
assertEquals("et", event.id);
assertEquals("ent", event.name);
assertEquals(123, event.startMillis);
assertEquals(456, event.endMillis);
assertEquals("http://1.2.3.4/test.png", event.imageUrl);
assertEquals("http://5.6.7.8", event.server);
}
public void testGetCompetitor()
{
CompetitorInfo competitor = checkinData.getCompetitor();
assertEquals("DE", competitor.countryCode);
assertEquals("cn", competitor.name);
assertEquals("GER", competitor.nationality);
assertEquals("SAIL 1", competitor.sailId);
assertEquals("1-2-3-4", competitor.id);
}
public void testGetLeaderboard()
{
LeaderboardInfo leaderboard = checkinData.getLeaderboard();
assertEquals("lt", leaderboard.name);
}
}
@@ -52,6 +52,14 @@
<data android:scheme="comsapsailingtracker" />
</intent-filter>
</activity>
<activity
android:name=".ui.activities.LeaderboardWebViewActivity"
android:label="@string/title_activity_webview"
android:screenOrientation="unspecified" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.activities.LeaderboardWebViewActivity" />
</activity>
<activity
android:name=".ui.activities.RegattaActivity"
android:label="@string/title_activity_regatta"
@@ -149,7 +149,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:padding="0dp"
android:padding="5dp"
android:text="@string/thank_you_you_successfully_participated"
android:textColor="@color/white"
android:textStyle="bold" />
@@ -302,18 +302,40 @@
android:layout_alignParentTop="true"
android:background="@color/white_transparent" />
<com.sap.sailing.android.tracking.app.customviews.OpenSansButton
android:id="@+id/start_tracking"
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="10dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:background="@drawable/rounded_btn_green"
android:padding="10dp"
android:text="@string/start_tracking"
android:textAllCaps="true"
android:textStyle="bold" />
android:layout_below="@id/white_bar"
android:orientation="horizontal" >
<com.sap.sailing.android.tracking.app.customviews.OpenSansButton
android:id="@+id/show_leaderboards_button"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="10dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:background="@drawable/rounded_btn_yellow"
android:padding="5dp"
android:text="@string/show_leaderboard"
android:textAllCaps="true"
android:layout_weight="0.50"
android:textStyle="bold" />
<com.sap.sailing.android.tracking.app.customviews.OpenSansButton
android:id="@+id/start_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_weight="0.50"
android:background="@drawable/rounded_btn_green"
android:padding="5dp"
android:text="@string/start_tracking"
android:textAllCaps="true"
android:textStyle="bold" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<WebView
android:id="@+id/web_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
@@ -287,19 +287,41 @@
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="@color/white_transparent" />
<com.sap.sailing.android.tracking.app.customviews.OpenSansButton
android:id="@+id/start_tracking"
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="10dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:background="@drawable/rounded_btn_green"
android:padding="10dp"
android:text="@string/start_tracking"
android:textAllCaps="true"
android:textStyle="bold" />
android:layout_below="@id/white_bar"
android:orientation="horizontal" >
<com.sap.sailing.android.tracking.app.customviews.OpenSansButton
android:id="@+id/show_leaderboards_button"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="10dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:background="@drawable/rounded_btn_yellow"
android:padding="10dp"
android:text="@string/show_leaderboard"
android:textAllCaps="true"
android:layout_weight="0.50"
android:textStyle="bold" />
<com.sap.sailing.android.tracking.app.customviews.OpenSansButton
android:id="@+id/start_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_weight="0.50"
android:background="@drawable/rounded_btn_green"
android:padding="10dp"
android:text="@string/start_tracking"
android:textAllCaps="true"
android:textStyle="bold" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
@@ -21,6 +21,7 @@
<string name="scan_race_barcode">Scan Race Barcode</string>
<string name="gps_fix_interval">GPS Fix Interval</string>
<string name="title_activity_regatta">Regatta</string>
<string name="title_activity_webview">Leaderboard</string>
<string name="title_activity_start">GPS Tracker einstellen</string>
<string name="title_activity_tracking">Tracking</string>
<string name="hello_world">Hello wörld!</string>
@@ -81,4 +82,5 @@
<string name="heading">Kurs</string>
<string name="sog">SOG</string>
<string name="none">Keine</string>
<string name="show_leaderboard">Leaderboard</string>
</resources>
@@ -21,6 +21,7 @@
<string name="scan_race_barcode">Scan Race Barcode</string>
<string name="gps_fix_interval">GPS fix interval</string>
<string name="title_activity_regatta">Regatta</string>
<string name="title_activity_webview">Leader board</string>
<string name="title_activity_start">Setup GPS-Tracker</string>
<string name="title_activity_tracking">Tracking</string>
<string name="hello_world">Hello world!</string>
@@ -68,5 +69,6 @@
<string name="heading">Heading</string>
<string name="sog">SOG</string>
<string name="none">None</string>
<string name="show_leaderboard">Leader board</string>
</resources>
@@ -151,11 +151,11 @@ public class TrackingService extends Service implements ConnectionCallbacks,
GPSQuality quality = GPSQuality.noSignal;
if (gpsQualityListener != null) {
if (gpsAccurracy > 163) {
if (gpsAccurracy > 48) {
quality = GPSQuality.poor;
} else if (gpsAccurracy > 48) {
} else if (gpsAccurracy > 10) {
quality = GPSQuality.good;
} else if (gpsAccurracy < 48) {
} else if (gpsAccurracy <= 10) {
quality = GPSQuality.great;
}
@@ -34,87 +34,92 @@ 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.
* 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.)
* (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 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 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.
* 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 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();
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)
{
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();
if (!timerRunning) {
startTimer();
}
} else {
stopTimer();
}
}
else
{
} else {
stopTimer();
}
}
return Service.START_STICKY;
}
private void startTimer() {
timer = new Timer();
timer.start();
@@ -122,168 +127,151 @@ public class TransmittingService extends Service {
if (BuildConfig.DEBUG) {
ExLog.i(this, "TIMER", "Background update-timer start");
}
}
private void stopTimer() {
if (timer != null)
{
timer.stop();
if (timer != null) {
timer.stop();
timerRunning = false;
if (BuildConfig.DEBUG) {
ExLog.i(this, "TIMER", "Background update-timer stop");
}
}
}
private void markSuccessfulTransmission()
{
private void markSuccessfulTransmission() {
currentlySending = false;
if (BuildConfig.DEBUG) {
ExLog.i(this, "TRANSMISSION", "markSuccessfulTransmission");
}
lastTransmissionFailed = false;
lastTransmissionTimestamp = System.nanoTime();
reportApiConnectivity(APIConnectivity.reachableTransmissionSuccess);
}
private void markFailedTransmission()
{
private void markFailedTransmission() {
currentlySending = false;
if (BuildConfig.DEBUG) {
ExLog.i(this, "TRANSMISSION", "markFailedTransmission");
}
lastTransmissionFailed = true;
lastTransmissionTimestamp = 0;
if (isConnected())
{
if (isConnected()) {
reportApiConnectivity(APIConnectivity.reachableTransmissionError);
} else {
reportApiConnectivity(APIConnectivity.notReachable);
}
else
{
reportApiConnectivity(APIConnectivity.notReachable);
}
}
/**
* If time interval has passed without any transmission, the service can turn
* itself off. Same for
* If time interval has passed without any transmission, the service can
* turn itself off. Same for
*
* @return
*/
private boolean serviceCanShutItselfDown()
{
private boolean serviceCanShutItselfDown() {
long currentNanoTime = System.nanoTime();
if (BuildConfig.DEBUG)
{
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
// 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");
// 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");
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");
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");
if (BuildConfig.DEBUG) {
ExLog.i(this, "TRANSMISSION", "returning false, don't terminate");
}
return false;
}
private void timerFired()
{
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");
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");
} else {
if (BuildConfig.DEBUG) {
ExLog.i(this, "POWER-LEVELS", "in default power mode");
}
currentUpdateInterval = UPDATE_INTERVAL_DEFAULT;
}
sendFixesToAPI(null);
reportUnsentGPSFixesCount(DatabaseHelper.getInstance().getNumberOfUnsentGPSFixes(getBaseContext()));
reportUnsentGPSFixesCount(DatabaseHelper.getInstance().getNumberOfUnsentGPSFixes(
getBaseContext()));
}
private boolean getTrackingServiceIsCurrentlyTracking()
{
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);
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)
{
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));
@@ -296,8 +284,7 @@ public class TransmittingService extends Service {
json.put("longitude", fix.longitude);
json.put("latitude", fix.latitude);
} catch (JSONException ex) {
ExLog.i(this, TAG, "Error while building geolocation json "
+ ex.getMessage());
ExLog.i(this, TAG, "Error while building geolocation json " + ex.getMessage());
}
jsonArray.put(json);
@@ -306,197 +293,198 @@ public class TransmittingService extends Service {
// 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());
ExLog.i(this, TAG,
"url: " + prefs.getServerURL() + prefs.getServerGpsFixesPostPath());
}
if (host != null)
{
sendingAttempted = true;
JsonObjectOrStatusOnlyRequest request = new JsonObjectOrStatusOnlyRequest(host
+ prefs.getServerGpsFixesPostPath(), requestObject, new FixSubmitListener(
ids.toArray(idsArr)), new FixSubmitErrorListener(host,
getTrackingServiceIsCurrentlyTracking(), failedHosts));
VolleyHelper.getInstance(this).addRequest(request);
}
else
{
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
JsonObjectOrStatusOnlyRequest request = new JsonObjectOrStatusOnlyRequest(host
+ prefs.getServerGpsFixesPostPath(), requestObject,
new FixSubmitListener(ids.toArray(idsArr)), new FixSubmitErrorListener(
host, getTrackingServiceIsCurrentlyTracking(), failedHosts));
VolleyHelper.getInstance(this).addRequest(request);
}
} else {
ExLog.w(this, TAG, "Warning, can't send fixes, host is null!");
}
} else {
if (serviceCanShutItselfDown())
{
if (serviceCanShutItselfDown()) {
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "Nothing to send or timeout occurred, Transmitting Service is stopping timer and terminating itself.");
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.
* 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()
{
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;
float batteryPct = level / (float) scale;
if (BuildConfig.DEBUG) {
ExLog.i(this, TAG, "Battery: " + (batteryPct * 100) + "%");
}
return batteryPct;
}
private void deleteSynced(String[] fixIdStrings)
{
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);
// }
// }
* 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 Listener<JSONObject> {
private String[] ids;
private String[] ids;
public FixSubmitListener(String[] ids) {
this.ids = ids;
}
@Override
public void onResponse(JSONObject response) {
deleteSynced(ids);
markSuccessfulTransmission();
//markAsSynced(ids);
}
}
private class FixSubmitErrorListener implements ErrorListener {
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;
public FixSubmitListener(String[] ids) {
this.ids = ids;
}
@Override
public void onErrorResponse(VolleyError 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);
}
}
}
@Override
public void onResponse(JSONObject response) {
deleteSynced(ids);
markSuccessfulTransmission();
// markAsSynced(ids);
}
}
private class FixSubmitErrorListener implements ErrorListener {
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 onErrorResponse(VolleyError 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) {
@@ -505,7 +493,7 @@ public class TransmittingService extends Service {
} catch (InterruptedException e) {
e.printStackTrace();
}
timerFired();
}
reportApiConnectivity(APIConnectivity.noAttempt);
@@ -518,50 +506,45 @@ public class TransmittingService extends Service {
t.start();
}
}
public void stop() {
endExecution = true;
}
}
public void registerAPIConnectivityListener( APIConnectivityListener listener )
{
public void registerAPIConnectivityListener(APIConnectivityListener listener) {
apiConnectivityListener = listener;
}
public void unregisterAPIConnectivityListener()
{
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);
notReachable(0), reachableTransmissionSuccess(1), reachableTransmissionError(2), noAttempt(
4);
private final int apiConnectivity;
APIConnectivity(int connectivity)
{
APIConnectivity(int connectivity) {
this.apiConnectivity = connectivity;
}
public int toInt()
{
public int toInt() {
return this.apiConnectivity;
}
}
public interface APIConnectivityListener {
public void apiConnectivityUpdated( APIConnectivity apiConnectivity );
public void setUnsentGPSFixesCount( int count );
public void apiConnectivityUpdated(APIConnectivity apiConnectivity);
public void setUnsentGPSFixesCount(int count);
}
}
@@ -0,0 +1,45 @@
package com.sap.sailing.android.tracking.app.ui.activities;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.ui.fragments.LeaderboardFragment;
public class LeaderboardWebViewActivity extends BaseActivity {
public final static String LEADERBOARD_EXTRA_SERVER_URL = "leaderboardExtraServerUrl";
public final static String LEADERBOARD_EXTRA_EVENT_ID = "leaderboardExtraEventId";
public final static String LEADERBOARD_EXTRA_LEADERBOARD_NAME = "leaderboardExtraLeaderboardName";
public String serverUrl;
public String eventId;
public String leaderboardName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
serverUrl = intent.getStringExtra(LEADERBOARD_EXTRA_SERVER_URL);
eventId = intent.getStringExtra(LEADERBOARD_EXTRA_EVENT_ID);
leaderboardName = intent.getStringExtra(LEADERBOARD_EXTRA_LEADERBOARD_NAME);
setContentView(R.layout.fragment_container);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
if (toolbar != null) {
setSupportActionBar(toolbar);
toolbar.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
}
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
toolbar.setNavigationIcon(R.drawable.sap_logo_64_sq);
toolbar.setPadding(20, 0, 0, 0);
getSupportActionBar().setTitle(getString(R.string.title_activity_webview));
}
replaceFragment(R.id.content_frame, new LeaderboardFragment());
}
}
@@ -45,10 +45,11 @@ import com.sap.sailing.android.tracking.app.valueobjects.LeaderboardInfo;
public class RegattaActivity extends BaseActivity {
private final static String TAG = RegattaActivity.class.getName();
private final static String LEADERBOARD_IMAGE_FILENAME_PREFIX = "leaderboardImage_";
private EventInfo event;
private CompetitorInfo competitor;
private LeaderboardInfo leaderboard;
public EventInfo event;
public CompetitorInfo competitor;
public LeaderboardInfo leaderboard;
@Override
@@ -85,7 +86,6 @@ public class RegattaActivity extends BaseActivity {
}
replaceFragment(R.id.content_frame, new RegattaFragment());
}
@Override
@@ -102,10 +102,6 @@ public class RegattaActivity extends BaseActivity {
ExLog.i(this, TAG, "Clicked SETTINGS.");
startActivity(new Intent(this, SettingsActivity.class));
return true;
// case R.id.options_menu_info:
// ExLog.i(this, TAG, "Clicked INFO.");
// startActivity(new Intent(this, SystemInformationActivity.class));
// return true;
case R.id.options_menu_checkout:
ExLog.i(this, TAG, "Clicked CHECKOUT.");
checkout();
@@ -153,21 +149,22 @@ public class RegattaActivity extends BaseActivity {
}
getRegattaFragment().setChangePhotoButtonHidden(true);
ExLog.w(this, TAG, "Event Image URL: " + event.imageUrl);
if (event.imageUrl != null)
ImageView imageView = (ImageView) findViewById(R.id.userImage);
Bitmap storedImage = getStoredImage(getLeaderboardImageFileName(leaderboard.name));
if (storedImage == null)
{
ImageView imageView = (ImageView) findViewById(R.id.userImage);
Bitmap storedImage = getStoredImage();
if (storedImage != null)
{
imageView.setImageBitmap(storedImage);
userImageUpdated();
}
else
{
new DownloadImageTask(imageView).execute(event.imageUrl);
}
if (event.imageUrl != null)
{
new DownloadImageTask(imageView).execute(event.imageUrl);
}
}
else
{
imageView.setImageBitmap(storedImage);
userImageUpdated();
}
super.onResume();
@@ -177,9 +174,9 @@ public class RegattaActivity extends BaseActivity {
*
* @param bitmap
*/
public void updatePictureChosenByUser(final Bitmap bitmap)
public void updateLeaderboardPictureChosenByUser(final Bitmap bitmap)
{
storeImage(bitmap);
storeImage(bitmap, getLeaderboardImageFileName(leaderboard.name));
runOnUiThread(new Runnable() {
@Override
@@ -196,8 +193,8 @@ public class RegattaActivity extends BaseActivity {
* Store image for quicker retrieval later.
* @param images
*/
private void storeImage(Bitmap image) {
File pictureFile = getMediaFile();
private void storeImage(Bitmap image, String fileName) {
File pictureFile = getImageFile(fileName);
if (pictureFile == null) {
Log.d(TAG,
"Error creating media file, check storage permissions: ");// e.getMessage());
@@ -215,12 +212,12 @@ public class RegattaActivity extends BaseActivity {
}
/**
* Get leaderboard image if there's one saved.
* Get stored image if there's one saved.
* @return
*/
private Bitmap getStoredImage()
private Bitmap getStoredImage(String fileName)
{
File pictureFile = getMediaFile();
File pictureFile = getImageFile(fileName);
if (pictureFile == null)
{
return null;
@@ -228,21 +225,44 @@ public class RegattaActivity extends BaseActivity {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap image = BitmapFactory.decodeFile(pictureFile.getAbsolutePath(),
options);
Bitmap image = BitmapFactory.decodeFile(pictureFile.getAbsolutePath(), options);
return image;
}
// private File getMediaFile(){
//
//
// }
/**
* Get Path for cached leaderbaord image.
* @return
*/
private File getMediaFile(){
public File getImageFile(String fileName) {
File mediaStorageDir;
File mediaStorageDir = getMediaStorageDir();
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())) {
File mediaFile;
String mImageName="MI_"+ fileName +".png";
mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName);
return mediaFile;
}
public void deleteImageFile(String fileName) {
File mediaStorageDir = getMediaStorageDir();
File mediaFile;
String mImageName = "MI_" + fileName + ".png";
mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName);
mediaFile.delete();
}
public File getMediaStorageDir() {
File mediaStorageDir;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
mediaStorageDir = new File(
Environment.getExternalStorageDirectory()
+ "/Android/data/"
@@ -257,12 +277,9 @@ public class RegattaActivity extends BaseActivity {
return null;
}
}
File mediaFile;
String mImageName="MI_"+ leaderboard.name +".png";
mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName);
return mediaFile;
}
return mediaStorageDir;
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
@@ -274,6 +291,7 @@ public class RegattaActivity extends BaseActivity {
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
@@ -285,11 +303,15 @@ public class RegattaActivity extends BaseActivity {
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
storeImage(result);
storeImage(result, getLeaderboardImageFileName(leaderboard.name));
userImageUpdated();
}
}
private String getLeaderboardImageFileName(String leaderboardName) {
return LEADERBOARD_IMAGE_FILENAME_PREFIX + leaderboardName;
}
/**
* Check out from regatta;
*/
@@ -320,6 +342,7 @@ public class RegattaActivity extends BaseActivity {
public void onResponse(JSONObject response) {
DatabaseHelper.getInstance().deleteRegattaFromDatabase(
RegattaActivity.this, event, competitor, leaderboard);
deleteImageFile(getLeaderboardImageFileName(leaderboard.name));
dismissProgressDialog();
finish();
}
@@ -331,15 +354,23 @@ public class RegattaActivity extends BaseActivity {
showErrorPopup(
R.string.error,
R.string.error_could_not_complete_operation_on_server_try_again);
}
});
VolleyHelper.getInstance(this).addRequest(checkoutRequest);
}
public EventInfo getEvent()
{
return event;
@Override
public void onBackPressed() {
RegattaFragment fragment = (RegattaFragment) getSupportFragmentManager().findFragmentById(R.id.content_frame);
if (fragment.isShowingBigCheckoutButton())
{
//do nothing, user must checkout himself at this point.
}
else
{
super.onBackPressed();
}
}
}
@@ -14,9 +14,7 @@ import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v7.widget.Toolbar;
import android.view.MotionEvent;
import android.widget.TextView;
import android.widget.Toast;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.BuildConfig;
@@ -52,6 +50,9 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
private final static String TAG = TrackingActivity.class.getName();
private final static String SIS_TRACKING_FRAGMENT = "savedInstanceTrackingFragment";
private final static String SIS_LAST_VIEWPAGER_ITEM = "instanceStateLastViewPagerItem";
private final static String SIS_LAST_SPEED_TEXT = "instanceStateLastSpeedText";
private final static String SIS_LAST_COMPASS_TEXT = "instanceStateLastCompassText";
private ViewPager mPager;
private ScreenSlidePagerAdapter mPagerAdapter;
@@ -59,9 +60,20 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
private String eventId;
private AppPreferences prefs;
private TrackingFragment trackingFragment;
private TrackingFragment trackingFragment;
private TimerRunnable timer;
private int lastViewPagerItem;
/**
* This isn't nice. The callbacks for fragments inside a
* view pager are unreliable, but I want the values
* to be displayed immediately after device rotation.
* Thus they are cached here and the fragments can pick
* them up.
*/
public String lastSpeedIndicatorText = "";
public String lastCompassIndicatorText = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -101,12 +113,8 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
}
mPager.setAdapter(mPagerAdapter);
mPager.setOffscreenPageLimit(3);
mPager.setOffscreenPageLimit(0);
// ok, here be dragons.
// however, I haven't found out how the ViewPager retains and dismisses fragments, so I check for every case.
// The easy way (if savedInstanceState != null) takeFromBundle(); doesn't work, since only some fragments are
// saved to the bundle it seems.
if (savedInstanceState != null) {
Fragment tFragment = getSupportFragmentManager().getFragment(savedInstanceState, SIS_TRACKING_FRAGMENT);
if (tFragment != null) {
@@ -114,6 +122,10 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
} else {
trackingFragment = new TrackingFragment();
}
lastViewPagerItem = savedInstanceState.getInt(SIS_LAST_VIEWPAGER_ITEM);
lastSpeedIndicatorText = savedInstanceState.getString(SIS_LAST_SPEED_TEXT);
lastCompassIndicatorText = savedInstanceState.getString(SIS_LAST_COMPASS_TEXT);
} else {
trackingFragment = new TrackingFragment();
}
@@ -125,6 +137,7 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
@Override
public void onPageSelected(int arg0) {
lastViewPagerItem = arg0;
}
@Override
@@ -140,23 +153,17 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
ServiceHelper.getInstance().startTrackingService(this, eventId);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
// HudFragment hudFragment = (HudFragment)
// getSupportFragmentManager().findFragmentById(R.id.hud_content_frame);
// hudFragment.layoutOverlay();
//
// hudFragment.setSpeedOverGround(0);
// hudFragment.setHeading(0);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(SIS_LAST_VIEWPAGER_ITEM, lastViewPagerItem);
outState.putString(SIS_LAST_SPEED_TEXT, lastSpeedIndicatorText);
outState.putString(SIS_LAST_COMPASS_TEXT, lastCompassIndicatorText);
if (trackingFragment.isAdded()) {
getSupportFragmentManager().putFragment(outState, SIS_TRACKING_FRAGMENT, trackingFragment);
}
}
}
@Override
@@ -219,7 +226,9 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
{
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
}
mPager.setAdapter(mPagerAdapter);
mPager.setCurrentItem(lastViewPagerItem);
}
@Override
@@ -367,14 +376,6 @@ public class TrackingActivity extends BaseActivity implements GPSQualityListener
return 3;
}
// public StopTrackingButtonFragment getStopTrackingButtonFragment() {
// if (stbFragment != null && stbFragment.isAdded())
// {
// return stbFragment;
// }
// else return null;
// }
public CompassFragment getCompassFragment() {
if (cFragment != null && cFragment.isAdded())
{
@@ -7,11 +7,11 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.ui.activities.TrackingActivity;
public class CompassFragment extends BaseFragment {
private String TAG = CompassFragment.class.getName();
private final String SIS_HEADING_TEXTIVEW = "CompassFragmentHeadingTextView";
//private String TAG = CompassFragment.class.getName();
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
@@ -25,6 +25,12 @@ public class CompassFragment extends BaseFragment {
{
TextView headingText = (TextView) getActivity().findViewById(R.id.compass_bearing_text_view);
headingText.setText(String.valueOf(Math.round(heading)) + "°");
TrackingActivity activity = (TrackingActivity) getActivity();
if (activity != null) {
activity.lastCompassIndicatorText = headingText.getText().toString();
}
}
}
@@ -32,20 +38,12 @@ public class CompassFragment extends BaseFragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
TextView headingText = (TextView)getActivity().findViewById(R.id.compass_bearing_text_view);
if (savedInstanceState != null)
{
headingText.setText(savedInstanceState.getString(SIS_HEADING_TEXTIVEW));
TrackingActivity activity = (TrackingActivity) getActivity();
if (activity != null) {
headingText.setText(activity.lastCompassIndicatorText);
} else {
headingText.setText("");
}
else
{
headingText.setText("---°");
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
TextView headingText = (TextView)getActivity().findViewById(R.id.compass_bearing_text_view);
outState.putString(SIS_HEADING_TEXTIVEW, headingText.getText().toString());
}
}
}
@@ -2,7 +2,6 @@ package com.sap.sailing.android.tracking.app.ui.fragments;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@@ -14,19 +13,13 @@ import org.json.JSONObject;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.provider.BaseColumns;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -49,17 +42,17 @@ 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.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.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.UniqueDeviceUuid;
import com.sap.sailing.android.tracking.app.utils.VolleyHelper;
import com.sap.sailing.android.tracking.app.valueobjects.CheckinData;
import com.sap.sailing.domain.racelog.tracking.DeviceIdentifier;
import com.sap.sailing.domain.racelog.tracking.impl.SmartphoneUUIDIdentifierImpl;
@@ -83,8 +76,6 @@ public class HomeFragment extends BaseFragment implements
prefs = new AppPreferences(getActivity());
getActivity().getContentResolver();
View view = inflater.inflate(R.layout.fragment_home, container, false);
Button scanButton = (Button) view.findViewById(R.id.scanQr);
@@ -103,8 +94,7 @@ public class HomeFragment extends BaseFragment implements
listView.addHeaderView(inflater.inflate(
R.layout.regatta_listview_header, null));
adapter = new RegattaAdapter(getActivity(),
R.layout.ragatta_listview_row, null, 0);
adapter = new RegattaAdapter(getActivity(), R.layout.ragatta_listview_row, null, 0);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new ItemClickListener());
}
@@ -184,17 +174,13 @@ public class HomeFragment extends BaseFragment implements
String leaderboardNameFromQR;
try {
leaderboardNameFromQR = URLEncoder.encode(
uri.getQueryParameter(CheckinHelper.LEADERBOARD_NAME),
"UTF-8").replace("+", "%20");
leaderboardNameFromQR = URLEncoder.encode(uri.getQueryParameter(CheckinHelper.LEADERBOARD_NAME),"UTF-8").replace("+", "%20");
} catch (UnsupportedEncodingException e) {
ExLog.e(getActivity(), TAG, "Failed to encode leaderboard name: "
+ e.getMessage());
ExLog.e(getActivity(), TAG, "Failed to encode leaderboard name: " + e.getMessage());
leaderboardNameFromQR = "";
}
final String competitorId = uri
.getQueryParameter(CheckinHelper.COMPETITOR_ID);
final String competitorId = uri.getQueryParameter(CheckinHelper.COMPETITOR_ID);
final String checkinURLStr = prefs.getServerURL()
+ prefs.getServerCheckinPath().replace("{leaderboard-name}",
leaderboardNameFromQR);
@@ -435,30 +421,6 @@ public class HomeFragment extends BaseFragment implements
alert.show();
}
/**
* Return true if the combination of eventId, leaderboardName and
* competitorId does not exist in the DB.
*
* @param eventId
* @param leaderboardName
* @param competitorId
* @return
*/
private boolean eventLeaderboardCompetitorCombnationAvailable(
String eventId, String leaderboardName, String competitorId) {
ContentResolver cr = getActivity().getContentResolver();
String sel = "leaderboards.leaderboard_name = \"" + leaderboardName
+ "\" AND competitors.competitor_id = \"" + competitorId
+ "\" AND events.event_id = \"" + eventId + "\"";
int count = cr.query(
AnalyticsContract.EventLeaderboardCompetitorJoined.CONTENT_URI,
null, sel, null, null).getCount();
return count == 0;
}
/**
* Perform a checkin request and launch RegattaAcitivity afterwards
*
@@ -468,74 +430,21 @@ public class HomeFragment extends BaseFragment implements
*/
private void checkInWithAPIAndDisplayTrackingActivity(
CheckinData checkinData) {
if (eventLeaderboardCompetitorCombnationAvailable(checkinData.eventId,
checkinData.leaderboardName, checkinData.competitorId)) {
// inserting leaderboard first in order to get the ID.
// This should be atomic, but couldn't get withValueBackReference to
// work yet.
ContentResolver cr = getActivity().getContentResolver();
ContentValues clv = new ContentValues();
clv.put(Leaderboard.LEADERBOARD_NAME, checkinData.leaderboardName);
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.
ArrayList<ContentProviderOperation> opList = new ArrayList<ContentProviderOperation>();
ContentValues cev = new ContentValues();
cev.put(Event.EVENT_ID, checkinData.eventId);
cev.put(Event.EVENT_NAME, checkinData.eventName);
cev.put(Event.EVENT_DATE_START, Long.parseLong(checkinData.eventStartDateStr));
cev.put(Event.EVENT_DATE_END, Long.parseLong(checkinData.eventEndDateStr));
cev.put(Event.EVENT_SERVER, checkinData.eventServerUrl);
cev.put(Event.EVENT_IMAGE_URL, checkinData.eventFirstImageUrl);
cev.put(Event.EVENT_LEADERBOARD_FK, lastLeaderboardId);
opList.add(ContentProviderOperation.newInsert(Event.CONTENT_URI)
.withValues(cev).build());
ContentValues ccv = new ContentValues();
ccv.put(Competitor.COMPETITOR_COUNTRY_CODE,checkinData.competitorCountryCode);
ccv.put(Competitor.COMPETITOR_DISPLAY_NAME,checkinData.competitorName);
ccv.put(Competitor.COMPETITOR_ID, checkinData.competitorId);
ccv.put(Competitor.COMPETITOR_NATIONALITY,checkinData.competitorNationality);
ccv.put(Competitor.COMPETITOR_SAIL_ID, checkinData.competitorSailId);
ccv.put(Competitor.COMPETITOR_LEADERBOARD_FK, lastLeaderboardId);
opList.add(ContentProviderOperation
.newInsert(Competitor.CONTENT_URI).withValues(ccv).build());
if (DatabaseHelper.getInstance().eventLeaderboardCompetitorCombnationAvailable(
getActivity(), checkinData.eventId, checkinData.leaderboardName, checkinData.competitorId)) {
try {
cr.applyBatch(AnalyticsContract.CONTENT_AUTHORITY, opList);
DatabaseHelper.getInstance().storeCheckinRow(getActivity(), checkinData.getEvent(),
checkinData.getCompetitor(), checkinData.getLeaderboard());
adapter.notifyDataSetChanged();
} catch (RemoteException e1) {
ExLog.e(getActivity(), TAG, "Batch insert failed: " + e1.getMessage());
displayDatabaseError();
return;
} catch (OperationApplicationException e1) {
ExLog.e(getActivity(), TAG, "Batch insert failed: " + e1.getMessage());
} catch (GeneralDatabaseHelperException e) {
ExLog.e(getActivity(), TAG, "Batch insert failed: " + e.getMessage());
displayDatabaseError();
return;
}
if (BuildConfig.DEBUG) {
ExLog.i(getActivity(), TAG,
"Batch-insert of checkinData completed.");
ExLog.i(getActivity(), TAG, "Batch-insert of checkinData completed.");
}
} else {
ExLog.w(getActivity(), TAG,
@@ -554,7 +463,6 @@ public class HomeFragment extends BaseFragment implements
Date date = new Date();
StartActivity startActivity = (StartActivity)getActivity();
startActivity.showProgressDialog(R.string.please_wait, R.string.checking_in);
try {
@@ -625,8 +533,7 @@ public class HomeFragment extends BaseFragment implements
* @param regattaName
* @param eventName
*/
private void startRegatta(String leaderboardName, String eventId,
String competitorId) {
private void startRegatta(String leaderboardName, String eventId, String competitorId) {
Intent intent = new Intent(getActivity(), RegattaActivity.class);
intent.putExtra(getString(R.string.leaderboard_name), leaderboardName);
intent.putExtra(getString(R.string.event_id), eventId);
@@ -638,7 +545,8 @@ public class HomeFragment extends BaseFragment implements
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
switch (loaderId) {
case REGATTA_LOADER:
String[] projection = new String[] { "events.event_id",
String[] projection = new String[] {
"events.event_id",
"events._id", "events.event_name", "events.event_server",
"competitors.competitor_display_name",
"competitors.competitor_id",
@@ -678,25 +586,7 @@ public class HomeFragment extends BaseFragment implements
break;
}
}
private class CheckinData {
// public String gcmId;
public String leaderboardName;
public String eventId;
public String eventName;
public String eventStartDateStr;
public String eventEndDateStr;
public String eventFirstImageUrl;
public String eventServerUrl;
public String checkinURL;
public String competitorName;
public String competitorId;
public String competitorSailId;
public String competitorNationality;
public String competitorCountryCode;
public String deviceUid;
}
private class ClickListener implements OnClickListener {
@Override
@@ -757,6 +647,7 @@ public class HomeFragment extends BaseFragment implements
public void onResponse(JSONObject response) {
StartActivity startActivity = (StartActivity)getActivity();
startActivity.dismissProgressDialog();
startRegatta(leaderboardName, eventId, competitorId);
}
}
@@ -0,0 +1,67 @@
package com.sap.sailing.android.tracking.app.ui.fragments;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.ui.activities.LeaderboardWebViewActivity;
public class LeaderboardFragment extends BaseFragment {
private static final String TAG = LeaderboardFragment.class.getName();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_leaderboard, container, false);
return view;
}
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onResume() {
super.onResume();
WebView webview = (WebView)getActivity().findViewById(R.id.web_view);
WebSettings webSettings = webview.getSettings();
webSettings.setJavaScriptEnabled(true);
webview.setWebViewClient(new WebViewClient() {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Toast.makeText(getActivity(), description, Toast.LENGTH_SHORT).show();
}
});
try {
String url = getLeaderboardURL();
webview.loadUrl(url);
} catch (UnsupportedEncodingException e) {
LeaderboardWebViewActivity activity = (LeaderboardWebViewActivity)getActivity();
activity.showErrorPopup(R.string.error, R.string.error_could_not_complete_operation_on_server_try_again);
ExLog.e(getActivity(), TAG, "UnsupportedEncodingException when creating leaderboard-url: " + e);
}
}
private String getLeaderboardURL() throws UnsupportedEncodingException
{
LeaderboardWebViewActivity activity = (LeaderboardWebViewActivity)getActivity();
String eventId = URLEncoder.encode(activity.eventId, "UTF-8");
String leaderboardName = URLEncoder.encode(activity.leaderboardName, "UTF-8");
String serverUrl = activity.serverUrl;
String formatString = "%s/gwt/Home.html#LeaderboardPlace:eventId=%s" +
"&leaderboardName=%s&showRaceDetails=true&showSettings=true";
return String.format(formatString, serverUrl, eventId, leaderboardName);
}
}
@@ -1,6 +1,8 @@
package com.sap.sailing.android.tracking.app.ui.fragments;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
@@ -24,7 +26,10 @@ import android.widget.RelativeLayout;
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;
@@ -34,7 +39,11 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
private final int CAMERA_REQUEST_CODE = 3118;
private final int SELECT_PHOTO_REQUEST_CODE = 1693;
private final int IMAGE_MAX_SIZE = 2000;
private final String CAMERA_TEMP_FILE = "cameraTempFile";
private boolean showingThankYouNote;
private TimerRunnable timer;
@@ -48,6 +57,9 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
Button startTrackingButton = (Button) view.findViewById(R.id.start_tracking);
startTrackingButton.setOnClickListener(this);
Button showLeaderboardButton = (Button) view.findViewById(R.id.show_leaderboards_button);
showLeaderboardButton.setOnClickListener(this);
Button changePhotoButton = (Button) view.findViewById(R.id.change_photo_button);
changePhotoButton.setOnClickListener(this);
@@ -63,7 +75,7 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
private void checkAndSwitchToThankYouScreenIfRegattaOver() {
RegattaActivity regattaActivity = (RegattaActivity) getActivity();
long regattaEnd = regattaActivity.getEvent().endMillis;
long regattaEnd = regattaActivity.event.endMillis;
if (System.currentTimeMillis() > regattaEnd) {
switchToThankYouScreen();
@@ -77,7 +89,7 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
TextView textView = (TextView)getActivity().findViewById(R.id.regatta_starts_in);
RegattaActivity regattaActivity = (RegattaActivity) getActivity();
long regattaStart = regattaActivity.getEvent().startMillis;
long regattaStart = regattaActivity.event.startMillis;
LinearLayout threeBoxesLayout = (LinearLayout) getActivity()
.findViewById(R.id.three_boxes_regatta_starts);
@@ -94,6 +106,10 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
}
}
public boolean isShowingBigCheckoutButton() {
return showingThankYouNote;
}
private void switchToThankYouScreen() {
showingThankYouNote = true;
@@ -130,6 +146,9 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.show_leaderboards_button:
startLeaderboardActivity();
break;
case R.id.start_tracking:
if (showingThankYouNote) {
RegattaActivity regattaActivity = (RegattaActivity) getActivity();
@@ -198,6 +217,8 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
private void showTakePhotoActivity() {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File photoFile = ((RegattaActivity)getActivity()).getImageFile(CAMERA_TEMP_FILE);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE);
}
@@ -207,39 +228,99 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
RegattaActivity activity = (RegattaActivity) getActivity();
if (requestCode == CAMERA_REQUEST_CODE) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
activity.updatePictureChosenByUser(photo);
File photoFile = ((RegattaActivity)getActivity()).getImageFile(CAMERA_TEMP_FILE);
Bitmap photo;
try {
photo = decodeUri(Uri.fromFile(photoFile));
activity.updateLeaderboardPictureChosenByUser(photo);
} catch (FileNotFoundException e) {
if (BuildConfig.DEBUG)
{
ExLog.i(getActivity(), TAG, "update photo, file not found: " + photoFile);
}
} catch (IOException e) {
if (BuildConfig.DEBUG)
{
ExLog.i(getActivity(), TAG, "update photo, io exception: " + e.getMessage());
}
} finally {
((RegattaActivity)getActivity()).deleteFile(CAMERA_TEMP_FILE);
}
} else if (requestCode == SELECT_PHOTO_REQUEST_CODE) {
Uri selectedImage = data.getData();
InputStream imageStream;
try {
imageStream = getActivity().getContentResolver()
.openInputStream(selectedImage);
Bitmap photo = BitmapFactory.decodeStream(imageStream);
activity.updatePictureChosenByUser(photo);
Bitmap photo = decodeUri(selectedImage);
activity.updateLeaderboardPictureChosenByUser(photo);
} catch (FileNotFoundException e) {
ExLog.e(getActivity(), TAG,
"File not found exception after picking image from gallery");
return;
} catch (IOException e) {
if (BuildConfig.DEBUG)
{
ExLog.i(getActivity(), TAG, "update photo, io exception: " + e.getMessage());
}
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* Decode image with maximum size
* @param uri
* @return
* @throws IOException
*/
private Bitmap decodeUri(Uri uri) throws IOException{
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream fis = getActivity().getContentResolver().openInputStream(uri);
BitmapFactory.decodeStream(fis, null, options);
fis.close();
int scale = 1;
if (options.outHeight > IMAGE_MAX_SIZE || options.outWidth > IMAGE_MAX_SIZE) {
scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE /
(double) Math.max(options.outHeight, options.outWidth)) / Math.log(0.5)));
}
BitmapFactory.Options options2 = new BitmapFactory.Options();
options2.inSampleSize = scale;
fis = getActivity().getContentResolver().openInputStream(uri);
bitmap = BitmapFactory.decodeStream(fis, null, options2);
fis.close();
return bitmap;
}
private void startLeaderboardActivity() {
RegattaActivity activity = (RegattaActivity)getActivity();
Intent intent = new Intent(getActivity(), LeaderboardWebViewActivity.class);
intent.putExtra(LeaderboardWebViewActivity.LEADERBOARD_EXTRA_SERVER_URL, activity.event.server);
intent.putExtra(LeaderboardWebViewActivity.LEADERBOARD_EXTRA_EVENT_ID, activity.event.id);
intent.putExtra(LeaderboardWebViewActivity.LEADERBOARD_EXTRA_LEADERBOARD_NAME, activity.leaderboard.name);
getActivity().startActivity(intent);
}
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.getEvent().id);
regattaActivity.event.id);
getActivity().startActivity(intent);
}
private void timerFired() {
RegattaActivity regattaActivity = (RegattaActivity) getActivity();
updateCountdownTimer(regattaActivity.getEvent().startMillis);
updateCountdownTimer(regattaActivity.event.startMillis);
}
private void updateCountdownTimer(long startTime) {
@@ -258,19 +339,13 @@ public class RegattaFragment extends BaseFragment implements OnClickListener {
}
private void setCountdownTime(int days, int hours, int minutes) {
TextView daysTextView = (TextView) getActivity().findViewById(
R.id.starts_in_days);
TextView hoursTextView = (TextView) getActivity().findViewById(
R.id.starts_in_hours);
TextView minutesTextView = (TextView) getActivity().findViewById(
R.id.starts_in_minutes);
TextView daysTextView = (TextView) getActivity().findViewById(R.id.starts_in_days);
TextView hoursTextView = (TextView) getActivity().findViewById(R.id.starts_in_hours);
TextView minutesTextView = (TextView) getActivity().findViewById(R.id.starts_in_minutes);
TextView daysTextViewLabel = (TextView) getActivity().findViewById(
R.id.starts_in_days_label);
TextView hoursTextViewLabel = (TextView) getActivity().findViewById(
R.id.starts_in_hours_label);
TextView minutesTextViewLabel = (TextView) getActivity().findViewById(
R.id.starts_in_minutes_label);
TextView daysTextViewLabel = (TextView) getActivity().findViewById(R.id.starts_in_days_label);
TextView hoursTextViewLabel = (TextView) getActivity().findViewById(R.id.starts_in_hours_label);
TextView minutesTextViewLabel = (TextView) getActivity().findViewById(R.id.starts_in_minutes_label);
daysTextView.setText(String.format("%02d", days));
hoursTextView.setText(String.format("%02d", hours));
@@ -11,22 +11,20 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.ui.activities.TrackingActivity;
public class SpeedFragment extends BaseFragment {
private String TAG = SpeedFragment.class.getName();
private final String SIS_SPEED_TEXTIVEW = "SpeedFragmentSpeedTextView";
//private String TAG = SpeedFragment.class.getName();
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
ViewGroup view = (ViewGroup) inflater.inflate(R.layout.fragment_speed, container, false);
ViewGroup view = (ViewGroup) inflater.inflate(R.layout.fragment_speed, container, false);
return view;
}
public void setSpeed(float speedInMetersPerSecond)
{
if (isAdded())
{
public void setSpeed(float speedInMetersPerSecond) {
if (isAdded()) {
float speedInKnots = speedInMetersPerSecond * 1.9438444924574f;
NumberFormat df = DecimalFormat.getInstance();
@@ -34,30 +32,29 @@ public class SpeedFragment extends BaseFragment {
df.setMaximumFractionDigits(2);
df.setRoundingMode(RoundingMode.HALF_UP);
String formattedSpeed = df.format(speedInKnots);
TextView speedText = (TextView) getActivity().findViewById(R.id.speed_text_view);
speedText.setText(formattedSpeed + "kn");
TrackingActivity activity = (TrackingActivity) getActivity();
if (activity != null) {
activity.lastSpeedIndicatorText = speedText.getText().toString();
}
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
TextView speedText = (TextView)getActivity().findViewById(R.id.speed_text_view);
if (savedInstanceState != null)
{
speedText.setText(savedInstanceState.getString(SIS_SPEED_TEXTIVEW));
}
else
{
speedText.setText("--kn");
TextView speedText = (TextView) getActivity().findViewById(R.id.speed_text_view);
TrackingActivity activity = (TrackingActivity) getActivity();
if (activity != null) {
speedText.setText(activity.lastSpeedIndicatorText);
} else {
speedText.setText("");
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
TextView speedText = (TextView)getActivity().findViewById(R.id.speed_text_view);
outState.putString(SIS_SPEED_TEXTIVEW, speedText.getText().toString());
}
}
}
@@ -31,7 +31,6 @@ public class TrackingFragment extends BaseFragment {
static final String SIS_GPS_ACCURACY = "instanceStateGpsAccuracy";
static final String SIS_GPS_UNSENT_FIXES = "instanceStateGpsUnsentFixes";
private AppPreferences prefs;
private long lastGPSQualityUpdate;
@@ -247,11 +246,8 @@ public class TrackingFragment extends BaseFragment {
{
unsentGpsFixesTextView.setText(String.valueOf(count));
}
}
});
}
}
}
@@ -3,16 +3,20 @@ 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;
import com.sap.sailing.android.shared.logging.ExLog;
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;
@@ -195,18 +199,19 @@ public class DatabaseHelper {
return result;
}
public EventInfo getEventInfo(Context context, String eventId) {
public EventInfo getEventInfo(Context context, String eventId) {
EventInfo event = new EventInfo();
event.id = eventId;
Cursor cursor = context.getContentResolver().query(Event.CONTENT_URI,
null, "event_id = \"" + eventId + "\"", null, null);
null, "event_id = \"" + eventId + "\"", null, null);
if (cursor.moveToFirst()) {
event.name = cursor.getString(cursor.getColumnIndex(Event.EVENT_NAME));
event.imageUrl = cursor.getString(cursor.getColumnIndex(Event.EVENT_IMAGE_URL));
event.startMillis = cursor.getLong(cursor.getColumnIndex(Event.EVENT_DATE_START));
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));
}
@@ -277,4 +282,107 @@ public class DatabaseHelper {
ExLog.i(context, TAG, "Checkout, number of leaderbards deleted: " + d3);
}
}
/**
* Attempt to check in to a race.
*
* @param context
* @param event
* @param competitor
* @param leaderboard
* @return success or failure
* @throws GeneralDatabaseHelperException
* @throws OperationApplicationException
* @throws RemoteException
*/
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.
ContentResolver cr = context.getContentResolver();
ContentValues clv = new ContentValues();
clv.put(Leaderboard.LEADERBOARD_NAME, leaderboard.name);
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.
ArrayList<ContentProviderOperation> opList = new ArrayList<ContentProviderOperation>();
ContentValues cev = new ContentValues();
cev.put(Event.EVENT_ID, event.id);
cev.put(Event.EVENT_NAME, event.name);
cev.put(Event.EVENT_DATE_START, event.startMillis);
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);
opList.add(ContentProviderOperation.newInsert(Event.CONTENT_URI).withValues(cev).build());
ContentValues ccv = new ContentValues();
ccv.put(Competitor.COMPETITOR_COUNTRY_CODE, competitor.countryCode);
ccv.put(Competitor.COMPETITOR_DISPLAY_NAME, competitor.name);
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);
opList.add(ContentProviderOperation.newInsert(Competitor.CONTENT_URI).withValues(ccv).build());
try {
cr.applyBatch(AnalyticsContract.CONTENT_AUTHORITY, opList);
} catch (RemoteException e) {
throw new GeneralDatabaseHelperException(e.getMessage());
} catch (OperationApplicationException e) {
throw new GeneralDatabaseHelperException(e.getMessage());
}
}
/**
* Return true if the combination of eventId, leaderboardName and
* competitorId does not exist in the DB.
*
* @param eventId
* @param leaderboardName
* @param competitorId
* @return
*/
public boolean eventLeaderboardCompetitorCombnationAvailable(Context context,
String eventId, String leaderboardName, String competitorId) {
ContentResolver cr = context.getContentResolver();
String sel = "leaderboards.leaderboard_name = \"" + leaderboardName
+ "\" AND competitors.competitor_id = \"" + competitorId
+ "\" AND events.event_id = \"" + eventId + "\"";
int count = cr.query(
AnalyticsContract.EventLeaderboardCompetitorJoined.CONTENT_URI,
null, sel, null, null).getCount();
return count == 0;
}
public class GeneralDatabaseHelperException extends Exception {
private static final long serialVersionUID = 4333494334720305541L;
public GeneralDatabaseHelperException(String message) {
super(message);
}
}
}
@@ -0,0 +1,495 @@
package com.sap.sailing.android.tracking.app.utils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.media.ExifInterface;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Base64;
/**
* Todo: Use this class?
*
*/
public class ImageController {
public static String getRealPathFromURI(ContentResolver contentResolver,
Uri contentUri) {
String res = null;
String[] proj = { MediaStore.Images.Media.DATA };
Cursor cursor = contentResolver.query(contentUri, proj, null, null,
null);
if (cursor.moveToFirst()) {
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
res = cursor.getString(column_index);
}
cursor.close();
return res;
}
// -----------------------------------------------------------------------
public static Bitmap scaleCenterCrop(Bitmap source, int newWidth,
int newHeight) {
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
// Compute the scaling factors to fit the new height and width,
// respectively.
// To cover the final image, the final scaling will be the bigger
// of these two.
float xScale = (float) newWidth / sourceWidth;
float yScale = (float) newHeight / sourceHeight;
float scale = Math.max(xScale, yScale);
// Now get the size of the source bitmap when scaled
float scaledWidth = scale * sourceWidth;
float scaledHeight = scale * sourceHeight;
// Let's find out the upper left coordinates if the scaled bitmap
// should be centered in the new size give by the parameters
float left = (newWidth - scaledWidth) / 2;
float top = (newHeight - scaledHeight) / 2;
// The target rectangle for the new, scaled version of the source bitmap
// will now
// be
RectF targetRect = new RectF(left, top, left + scaledWidth, top
+ scaledHeight);
// Finally, we create a new bitmap of the specified size and draw our
// new,
// scaled bitmap onto it.
Bitmap dest = Bitmap.createBitmap(newWidth, newHeight,
source.getConfig());
Canvas canvas = new Canvas(dest);
canvas.drawBitmap(source, null, targetRect, null);
return dest;
}
public static Bitmap scaleBitmap(Bitmap bitmap, int newWidth, int newHeight) {
Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight,
Config.ARGB_8888);
float ratioX = newWidth / (float) bitmap.getWidth();
float ratioY = newHeight / (float) bitmap.getHeight();
float middleX = newWidth / 2.0f;
float middleY = newHeight / 2.0f;
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
Canvas canvas = new Canvas(scaledBitmap);
canvas.setMatrix(scaleMatrix);
float left = middleX - (float) bitmap.getWidth() / 2;
float top = middleY - (float) bitmap.getHeight() / 2;
canvas.drawBitmap(bitmap, left, top,
new Paint(Paint.FILTER_BITMAP_FLAG));
return scaledBitmap;
}
public static Bitmap scaleBitmapAspectRatio(Bitmap bitmap, int targetWidth,
int targetHeight) {
float ratioX = targetWidth / (float) bitmap.getWidth();
float ratioY = targetHeight / (float) bitmap.getHeight();
float aspectRatio = (float) bitmap.getWidth() / bitmap.getHeight();
if (ratioX < ratioY) {
aspectRatio = ratioX;
} else {
aspectRatio = ratioY;
}
int newWidth = Math.round(bitmap.getWidth() * aspectRatio);
if (newWidth < 1) {
newWidth = 1;
}
int newHeight = Math.round(bitmap.getHeight() * aspectRatio);
if (newHeight < 1) {
newHeight = 1;
}
return scaleBitmap(bitmap, newWidth, newHeight);
}
// -----------------------------------------------------------------------
// http://stackoverflow.com/a/10028267
public static Bitmap fastblur(Bitmap sentBitmap, int radius) {
// Stack Blur v1.0 from
// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
//
// Java Author: Mario Klingemann <mario at quasimondo.com>
// http://incubator.quasimondo.com
// created Feburary 29, 2004
// Android port : Yahel Bouaziz <yahel at kayenko.com>
// http://www.kayenko.com
// ported april 5th, 2012
// This is a compromise between Gaussian Blur and Box blur
// It creates much better looking blurs than Box Blur, but is
// 7x faster than my Gaussian Blur implementation.
//
// I called it Stack Blur because this describes best how this
// filter works internally: it creates a kind of moving stack
// of colors whilst scanning through the image. Thereby it
// just has to add one new block of color to the right side
// of the stack and remove the leftmost color. The remaining
// colors on the topmost layer of the stack are either added on
// or reduced by one, depending on if they are on the right or
// on the left side of the stack.
//
// If you are using this algorithm in your code please add
// the following line:
//
// Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
if (radius < 1) {
return (null);
}
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int wm = w - 1;
int hm = h - 1;
int wh = w * h;
int div = radius + radius + 1;
int r[] = new int[wh];
int g[] = new int[wh];
int b[] = new int[wh];
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
int vmin[] = new int[Math.max(w, h)];
int divsum = (div + 1) >> 1;
divsum *= divsum;
int dv[] = new int[256 * divsum];
for (i = 0; i < 256 * divsum; i++) {
dv[i] = (i / divsum);
}
yw = yi = 0;
int[][] stack = new int[div][3];
int stackpointer;
int stackstart;
int[] sir;
int rbs;
int r1 = radius + 1;
int routsum, goutsum, boutsum;
int rinsum, ginsum, binsum;
for (y = 0; y < h; y++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
for (i = -radius; i <= radius; i++) {
p = pix[yi + Math.min(wm, Math.max(i, 0))];
sir = stack[i + radius];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rbs = r1 - Math.abs(i);
rsum += sir[0] * rbs;
gsum += sir[1] * rbs;
bsum += sir[2] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
}
stackpointer = radius;
for (x = 0; x < w; x++) {
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (y == 0) {
vmin[x] = Math.min(x + radius + 1, wm);
}
p = pix[yw + vmin[x]];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[(stackpointer) % div];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi++;
}
yw += w;
}
for (x = 0; x < w; x++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
yp = -radius * w;
for (i = -radius; i <= radius; i++) {
yi = Math.max(0, yp) + x;
sir = stack[i + radius];
sir[0] = r[yi];
sir[1] = g[yi];
sir[2] = b[yi];
rbs = r1 - Math.abs(i);
rsum += r[yi] * rbs;
gsum += g[yi] * rbs;
bsum += b[yi] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
if (i < hm) {
yp += w;
}
}
yi = x;
stackpointer = radius;
for (y = 0; y < h; y++) {
// Preserve alpha channel: ( 0xff000000 & pix[yi] )
pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16)
| (dv[gsum] << 8) | dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (x == 0) {
vmin[y] = Math.min(y + r1, hm) * w;
}
p = x + vmin[y];
sir[0] = r[p];
sir[1] = g[p];
sir[2] = b[p];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[stackpointer];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi += w;
}
}
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
return (bitmap);
}
// -----------------------------------------------------------------------
// http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and
// keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromFile(
ContentResolver contentResolver, Uri uri, String filename,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
if (filename == null) {
try {
InputStream inputStream = contentResolver.openInputStream(uri);
BitmapFactory.decodeStream(inputStream, null, options);
} catch (IOException e) {
}
} else {
BitmapFactory.decodeFile(filename, options);
}
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bitmap = null;
if (filename == null) {
try {
InputStream inputStream = contentResolver.openInputStream(uri);
bitmap = BitmapFactory.decodeStream(inputStream, null, options);
} catch (IOException e) {
}
} else {
bitmap = BitmapFactory.decodeFile(filename, options);
}
// check orientation
if (bitmap != null && filename != null) {
try {
ExifInterface exif = new ExifInterface(filename);
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION, 1);
int angle = 0;
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
angle = -90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
angle = -180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
angle = -270;
break;
default:
break;
}
if (angle != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotatedBitmap = Bitmap
.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
bitmap.recycle();
bitmap = rotatedBitmap;
}
} catch (IOException e) {
}
}
return bitmap;
}
// -----------------------------------------------------------------------
// http://stackoverflow.com/a/8557147
private final static String[] projection = new String[] {
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE };
public static File latestImage(Context context) {
final Cursor cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null,
null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
// Put it in the image view
if (cursor.moveToFirst()) {
String imageLocation = cursor.getString(1);
File imageFile = new File(imageLocation);
return imageFile;
}
return null;
}
// -----------------------------------------------------------------------
public static String encodeTobase64(Bitmap image) {
Bitmap immagex = image;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
immagex.compress(Bitmap.CompressFormat.JPEG, 60, baos);
byte[] b = baos.toByteArray();
String imageEncoded = Base64.encodeToString(b, Base64.NO_WRAP);
return "data:image/jpeg;base64," + imageEncoded;
}
}
@@ -0,0 +1,49 @@
package com.sap.sailing.android.tracking.app.valueobjects;
public class CheckinData {
// public String gcmId;
public String leaderboardName;
public String eventId;
public String eventName;
public String eventStartDateStr;
public String eventEndDateStr;
public String eventFirstImageUrl;
public String eventServerUrl;
public String checkinURL;
public String competitorName;
public String competitorId;
public String competitorSailId;
public String competitorNationality;
public String competitorCountryCode;
public String deviceUid;
public EventInfo getEvent()
{
EventInfo event = new EventInfo();
event.name = eventName;
event.id = eventId;
event.startMillis = Long.parseLong(eventStartDateStr);
event.endMillis = Long.parseLong(eventEndDateStr);
event.imageUrl = eventFirstImageUrl;
event.server = eventServerUrl;
return event;
}
public LeaderboardInfo getLeaderboard()
{
LeaderboardInfo leaderboard = new LeaderboardInfo();
leaderboard.name = leaderboardName;
return leaderboard;
}
public CompetitorInfo getCompetitor()
{
CompetitorInfo competitor = new CompetitorInfo();
competitor.name = competitorName;
competitor.id = competitorId;
competitor.sailId = competitorSailId;
competitor.nationality = competitorNationality;
competitor.countryCode = competitorCountryCode;
return competitor;
}
}
@@ -5,6 +5,7 @@ public class CompetitorInfo {
public String id;
public String name;
public String countryCode;
public String nationality;
public String sailId;
public int rowId;
@@ -9,6 +9,7 @@ public class EventInfo {
public long startMillis;
public long endMillis;
public int rowId;
public String server;
@Override
public String toString() {
@@ -16,6 +17,6 @@ public class EventInfo {
+ leaderboardName + ", eventImageUrl: " + imageUrl
+ ", eventStartMillis: " + startMillis
+ ", eventEndMillis: " + endMillis + ", eventRowId: "
+ rowId;
+ rowId + ", server: " + server;
}
}