Merge branch 'ubilabs--tracking_app--feature--reload_data_service' into ubilabs--tracking_app--develop

This commit is contained in:
Bennet Brunsen committed 2015-06-22 18:06:36 +02:00
commit 9bbed64e45
14 files changed
+254 -69

No files matched your search

@@ -84,6 +84,10 @@
</intent-filter>
</service>
<service
android:name=".service.MarkerService"
android:exported="false"/>
<provider
android:name="com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsProvider"
android:authorities="com.sap.sailing.android.buoy.positioning.app.provider.db"
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="database_changed">database_changed</string>
</resources>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="preference_data_refresh_interval_millis_key">messageResendIntervalMillis</string>
<integer name="preference_data_refresh_interval_millis_default">120000</integer>
<string name="check_in_url_key">check_in_url</string>
</resources>
@@ -5,7 +5,6 @@ import android.database.Cursor;
import android.support.v4.widget.ResourceCursorAdapter;
import android.view.View;
import android.widget.TextView;
import com.sap.sailing.android.buoy.positioning.app.R;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract;
import com.sap.sailing.android.buoy.positioning.app.util.DatabaseHelper;
@@ -3,7 +3,6 @@ package com.sap.sailing.android.buoy.positioning.app.adapter;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import com.sap.sailing.android.buoy.positioning.app.R;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract;
import com.sap.sailing.android.shared.ui.adapters.AbstractRegattaAdapter;
@@ -4,13 +4,7 @@ import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.CheckinUriColumns;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.Leaderboard;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.LeaderboardColumns;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.Mark;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.MarkColums;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.MarkPingColumns;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.*;
import com.sap.sailing.android.shared.logging.ExLog;
public class AnalyticsDatabase extends SQLiteOpenHelper {
@@ -9,7 +9,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;
import com.sap.sailing.android.buoy.positioning.app.BuildConfig;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.CheckinUri;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.Leaderboard;
@@ -0,0 +1,107 @@
package com.sap.sailing.android.buoy.positioning.app.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import com.sap.sailing.android.buoy.positioning.app.R;
import com.sap.sailing.android.buoy.positioning.app.util.AppPreferences;
import com.sap.sailing.android.buoy.positioning.app.util.CheckinManager;
import com.sap.sailing.android.buoy.positioning.app.util.DatabaseHelper;
import com.sap.sailing.android.buoy.positioning.app.valueobjects.CheckinData;
import com.sap.sailing.android.shared.data.AbstractCheckinData;
import java.util.Calendar;
public class MarkerService extends Service implements CheckinManager.DataChangedListner{
private static String TAG = MarkerService.class.getName();
private MarkerBinder markerBinder;
private CheckinManager manager;
private TimerRunnable timerRunnable;
private AppPreferences preferences;
private long lastCheck;
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "MarkService created");
markerBinder = new MarkerBinder();
preferences = new AppPreferences(this);
lastCheck = Calendar.getInstance().getTimeInMillis();
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "MarkService is bound");
return markerBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
String url = intent.getStringExtra(getString(R.string.check_in_url_key));
manager = new CheckinManager(url, this);
manager.setDataChangedListner(this);
timerRunnable = new TimerRunnable();
new Thread(timerRunnable).start();
return START_STICKY;
}
@Override
public void onDestroy() {
timerRunnable.stop();
super.onDestroy();
}
@Override
public void handleData(AbstractCheckinData data) {
try {
CheckinData checkinData = (CheckinData) data;
DatabaseHelper helper = DatabaseHelper.getInstance();
helper.deleteRegattaFromDatabase(this, checkinData.checkinDigest);
helper.storeCheckinRow(this, checkinData.marks, checkinData.getLeaderboard(), checkinData.getCheckinUrl(), checkinData.pings);
}
catch (DatabaseHelper.GeneralDatabaseHelperException e) {
e.printStackTrace();
}
}
private class MarkerBinder extends Binder {
public MarkerService getService() {
return MarkerService.this;
}
}
private class TimerRunnable implements Runnable {
;
private boolean running;
public TimerRunnable(){
running = true;
}
@Override
public void run() {
while (running) {
long timeDiff = Calendar.getInstance().getTimeInMillis() - lastCheck;
if(timeDiff > preferences.getDataRefreshInterval()) {
manager.callServerAndGenerateCheckinData();
lastCheck = Calendar.getInstance().getTimeInMillis();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop() {
running = false;
}
}
}
@@ -9,9 +9,9 @@ import android.os.IBinder;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.sap.sailing.android.buoy.positioning.app.BuildConfig;
import com.sap.sailing.android.buoy.positioning.app.R;
import com.sap.sailing.android.buoy.positioning.app.service.MarkerService;
import com.sap.sailing.android.buoy.positioning.app.ui.fragments.RegattaFragment;
import com.sap.sailing.android.buoy.positioning.app.util.AppPreferences;
import com.sap.sailing.android.buoy.positioning.app.util.CheckinManager;
@@ -86,6 +86,9 @@ public class RegattaActivity extends AbstractRegattaActivity {
@Override
public void onStart() {
super.onStart();
Intent intent = new Intent(this, MarkerService.class);
intent.putExtra(getString(R.string.check_in_url_key), checkinUrl);
startService(intent);
Intent messageSendingServiceIntent = new Intent(this, MessageSendingService.class);
bindService(messageSendingServiceIntent, messageSendingServiceConnection, Context.BIND_AUTO_CREATE);
}
@@ -103,6 +106,8 @@ public class RegattaActivity extends AbstractRegattaActivity {
ExLog.i(this, TAG, "Unbound transmitting Service");
}
}
Intent intent = new Intent(this, MarkerService.class);
stopService(intent);
}
@Override
@@ -1,19 +1,23 @@
package com.sap.sailing.android.buoy.positioning.app.ui.fragments;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;
import com.sap.sailing.android.buoy.positioning.app.R;
import com.sap.sailing.android.buoy.positioning.app.adapter.MarkAdapter;
@@ -23,9 +27,11 @@ import com.sap.sailing.android.buoy.positioning.app.ui.activities.RegattaActivit
import com.sap.sailing.android.ui.fragments.BaseFragment;
public class RegattaFragment extends BaseFragment implements LoaderCallbacks<Cursor> {
private static final String TAG = RegattaFragment.class.getName();
private static final int MARKER_LOADER = 1;
private MarkAdapter adapter;
private TextView courseTypeTextView;
private IntentReceiver mReceiver;
@SuppressLint("InflateParams")
@Override
@@ -47,10 +53,20 @@ public class RegattaFragment extends BaseFragment implements LoaderCallbacks<Cur
public void onResume() {
super.onResume();
getLoaderManager().restartLoader(MARKER_LOADER, null, this);
mReceiver = new IntentReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(getString(R.string.database_changed));
LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).registerReceiver(mReceiver, filter);
// TODO: Set course type, once backend is ready
courseTypeTextView.setText(getString(R.string.no_information));
}
@Override
public void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).unregisterReceiver(mReceiver);
}
public MarkAdapter getAdapter() {
return adapter;
}
@@ -108,4 +124,16 @@ public class RegattaFragment extends BaseFragment implements LoaderCallbacks<Cur
}
}
// Broadcast receiver to update ui on data changed
private class IntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Adapter will be notified");
String action = intent.getAction();
if(action.equals(getString(R.string.database_changed))) {
adapter.notifyDataSetChanged();
}
}
}
}
@@ -1,7 +1,6 @@
package com.sap.sailing.android.buoy.positioning.app.util;
import android.content.Context;
import com.sap.sailing.android.buoy.positioning.app.R;
import com.sap.sailing.android.shared.util.BaseAppPreferences;
@@ -28,6 +27,16 @@ public class AppPreferences extends BaseAppPreferences {
.replace("{leaderboard_name}", cleanLeaderBoardName).replace("{mark-id}", markID);
}
public void setDataRefreshInterval(long interval){
preferences.edit().putLong(context.getString(R.string.preference_data_refresh_interval_millis_key), interval)
.apply();
}
public long getDataRefreshInterval(){
return preferences.getLong(context.getString(R.string.preference_data_refresh_interval_millis_key),
context.getResources().getInteger(R.integer.preference_data_refresh_interval_millis_default));
}
public static boolean getPrintDatabaseOperationDebugMessages() {
return false;
}
@@ -1,10 +1,10 @@
package com.sap.sailing.android.buoy.positioning.app.util;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
import android.widget.Toast;
import com.sap.sailing.android.buoy.positioning.app.R;
import com.sap.sailing.android.buoy.positioning.app.valueobjects.CheckinData;
import com.sap.sailing.android.buoy.positioning.app.valueobjects.MarkInfo;
@@ -19,7 +19,6 @@ import com.sap.sailing.android.shared.util.UniqueDeviceUuid;
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 org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -39,13 +38,22 @@ public class CheckinManager {
private final static String TAG = CheckinManager.class.getName();
private AbstractCheckinData checkinData;
private CheckinDataActivity activity;
private Context mContext;
private AppPreferences prefs;
private String url;
private DataChangedListner dataChangedListner;
public CheckinManager(String url, Context context){
this.url = url;
mContext = context;
prefs = new AppPreferences(context);
}
public CheckinManager(String url, CheckinDataActivity activity) {
this.activity = activity;
this.url = url;
prefs = new AppPreferences(activity);
mContext = activity;
prefs = new AppPreferences(mContext);
}
public void callServerAndGenerateCheckinData() {
@@ -62,15 +70,15 @@ public class CheckinManager {
setCheckinData(null);
return;
}
activity.showProgressDialog(R.string.please_wait, R.string.getting_marks);
if (activity != null) {
activity.showProgressDialog(R.string.please_wait, R.string.getting_marks);
}
try {
HttpGetRequest getLeaderboardRequest = new HttpGetRequest(new URL(urlData.getLeaderboardUrl), activity);
HttpGetRequest getLeaderboardRequest = new HttpGetRequest(new URL(urlData.getLeaderboardUrl), mContext);
getLeaderBoardFromServer(urlData, getLeaderboardRequest);
} catch (MalformedURLException e) {
ExLog.e(activity, TAG,
ExLog.e(mContext, TAG,
"Error: Failed to perform checking due to a MalformedURLException: " + e.getMessage());
}
}
@@ -87,17 +95,17 @@ public class CheckinManager {
leaderboardNameFromQR = URLEncoder.encode(
uri.getQueryParameter(DeviceMappingConstants.URL_LEADERBOARD_NAME), "UTF-8").replace("+", "%20");
} catch (UnsupportedEncodingException e) {
ExLog.e(activity, TAG, "Failed to encode leaderboard name: " + e.getMessage());
ExLog.e(mContext, TAG, "Failed to encode leaderboard name: " + e.getMessage());
} catch (NullPointerException e) {
ExLog.e(activity, TAG, "Invalid Barcode (no leaderboard-name set): " + e.getMessage());
Toast.makeText(activity, activity.getString(R.string.error_invalid_qr_code), Toast.LENGTH_LONG).show();
ExLog.e(mContext, TAG, "Invalid Barcode (no leaderboard-name set): " + e.getMessage());
Toast.makeText(mContext, mContext.getString(R.string.error_invalid_qr_code), Toast.LENGTH_LONG).show();
urlData = null;
}
if (urlData != null) {
urlData.leaderboardName = leaderboardNameFromQR;
urlData.deviceUuid = new SmartphoneUUIDIdentifierImpl(UUID.fromString(UniqueDeviceUuid
.getUniqueId(activity)));
.getUniqueId(mContext)));
urlData.getLeaderboardUrl = urlData.hostWithPort + prefs.getServerLeaderboardPath(urlData.leaderboardName);
}
@@ -105,7 +113,7 @@ public class CheckinManager {
}
private void getLeaderBoardFromServer(final URLData urlData, HttpGetRequest getLeaderboardRequest) {
NetworkHelper.getInstance(activity).executeHttpJsonRequestAsnchronously(getLeaderboardRequest,
NetworkHelper.getInstance(mContext).executeHttpJsonRequestAsnchronously(getLeaderboardRequest,
new NetworkHelper.NetworkHelperSuccessListener() {
@Override
@@ -118,20 +126,21 @@ public class CheckinManager {
leaderboardName = response.getString("name");
urlData.getMarkUrl = urlData.hostWithPort + prefs.getServerMarkPath(leaderboardName);
} catch (JSONException e) {
ExLog.e(activity, TAG, "Error getting data from call on URL: " + urlData.getLeaderboardUrl
ExLog.e(mContext, TAG, "Error getting data from call on URL: " + urlData.getLeaderboardUrl
+ ", Error: " + e.getMessage());
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
if (activity != null) {
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
}
return;
}
HttpGetRequest getMarksRequest;
try {
getMarksRequest = new HttpGetRequest(new URL(urlData.getMarkUrl), activity);
getMarksRequest = new HttpGetRequest(new URL(urlData.getMarkUrl), mContext);
getMarksFromServer(leaderboardName, getMarksRequest, urlData);
} catch (MalformedURLException e1) {
ExLog.e(activity, TAG, "Error: Failed to perform checking due to a MalformedURLException: "
+ e1.getMessage());
ExLog.e(mContext, TAG, "Error: Failed to perform checking due to a MalformedURLException: " + e1.getMessage());
}
}
@@ -139,15 +148,17 @@ public class CheckinManager {
@Override
public void performAction(NetworkHelper.NetworkHelperError e) {
ExLog.e(activity, TAG, "Failed to get event from API: " + e.getMessage());
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
ExLog.e(mContext, TAG, "Failed to get event from API: " + e.getMessage());
if (activity != null) {
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
}
}
});
}
private void getMarksFromServer(final String leaderboardName, HttpGetRequest getMarksRequest, final URLData urlData) {
NetworkHelper.getInstance(activity).executeHttpJsonRequestAsnchronously(getMarksRequest,
NetworkHelper.getInstance(mContext).executeHttpJsonRequestAsnchronously(getMarksRequest,
new NetworkHelperSuccessListener() {
@Override
@@ -184,10 +195,12 @@ public class CheckinManager {
saveCheckinDataAndNotifyListeners(urlData, leaderboardName);
} catch (JSONException e) {
ExLog.e(activity, TAG, "Error getting data from call on URL: " + urlData.getMarkUrl
ExLog.e(mContext, TAG, "Error getting data from call on URL: " + urlData.getMarkUrl
+ ", Error: " + e.getMessage());
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
if (activity != null) {
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
}
return;
}
@@ -196,9 +209,11 @@ public class CheckinManager {
@Override
public void performAction(NetworkHelper.NetworkHelperError e) {
ExLog.e(activity, TAG, "Failed to get marks from API: " + e.getMessage());
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
ExLog.e(mContext, TAG, "Failed to get marks from API: " + e.getMessage());
if (activity != null) {
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
}
}
});
}
@@ -213,24 +228,43 @@ public class CheckinManager {
data.uriString = urlData.uriStr;
try {
data.setCheckinDigestFromString(urlData.uriStr);
activity.dismissProgressDialog();
if (activity != null) {
activity.dismissProgressDialog();
}
setCheckinData(data);
} catch (UnsupportedEncodingException e) {
ExLog.e(activity, TAG,
ExLog.e(mContext, TAG,
"Failed to get generate digest of qr-code string (" + urlData.uriStr + "). " + e.getMessage());
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
if (activity != null) {
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
}
} catch (NoSuchAlgorithmException e) {
ExLog.e(activity, TAG,
ExLog.e(mContext, TAG,
"Failed to get generate digest of qr-code string (" + urlData.uriStr + "). " + e.getMessage());
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
if (activity != null) {
activity.dismissProgressDialog();
displayAPIErrorRecommendRetry();
}
}
}
public void setCheckinData(AbstractCheckinData data) {
checkinData = data;
activity.onCheckinDataAvailable(getCheckinData());
if (activity != null) {
activity.onCheckinDataAvailable(getCheckinData());
}
else if (dataChangedListner != null){
dataChangedListner.handleData(data);
}
}
public interface DataChangedListner{
void handleData(AbstractCheckinData data);
}
public void setDataChangedListner(DataChangedListner listner){
dataChangedListner = listner;
}
public AbstractCheckinData getCheckinData() {
@@ -241,10 +275,10 @@ public class CheckinManager {
* Shows a pop-up-dialog that informs the user than an API-call has failed and recommends a retry.
*/
private void displayAPIErrorRecommendRetry() {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(activity.getString(R.string.notify_user_api_call_failed));
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage(mContext.getString(R.string.notify_user_api_call_failed));
builder.setCancelable(true);
builder.setPositiveButton(activity.getString(R.string.ok), new DialogInterface.OnClickListener() {
builder.setPositiveButton(mContext.getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
@@ -278,10 +312,6 @@ public class CheckinManager {
return checkinDigest;
}
public interface CheckinDataHandler {
public void onCheckinDataAvailable(CheckinData data);
}
private class URLData {
public String uriStr;
public String server;
@@ -1,15 +1,12 @@
package com.sap.sailing.android.buoy.positioning.app.util;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.content.*;
import android.database.Cursor;
import android.os.RemoteException;
import android.provider.BaseColumns;
import android.support.v4.content.LocalBroadcastManager;
import com.sap.sailing.android.buoy.positioning.app.BuildConfig;
import com.sap.sailing.android.buoy.positioning.app.R;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.CheckinUri;
import com.sap.sailing.android.buoy.positioning.app.provider.AnalyticsContract.Leaderboard;
@@ -153,8 +150,12 @@ public class DatabaseHelper {
public void storeCheckinRow(Context context, List<MarkInfo> markList, LeaderboardInfo leaderboard,
CheckinUrlInfo checkinURL, List<MarkPingInfo> pings) throws GeneralDatabaseHelperException {
if (BuildConfig.DEBUG) {
ExLog.i(context, TAG, "New data stored");
}
// inserting leaderboard first
ContentResolver cr = context.getContentResolver();
ContentValues clv = new ContentValues();
@@ -195,6 +196,8 @@ public class DatabaseHelper {
try {
cr.applyBatch(AnalyticsContract.CONTENT_AUTHORITY, opList);
LocalBroadcastManager.getInstance(context.getApplicationContext()).sendBroadcast(new Intent(context.getString(R.string.database_changed)));
} catch (RemoteException e) {
throw new GeneralDatabaseHelperException(e.getMessage());
} catch (OperationApplicationException e) {
@@ -1,19 +1,17 @@
package com.sap.sailing.android.buoy.positioning.app.util;
import java.util.UUID;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.location.Location;
import com.sap.sailing.android.buoy.positioning.app.util.DatabaseHelper.GeneralDatabaseHelperException;
import com.sap.sailing.android.buoy.positioning.app.valueobjects.MarkInfo;
import com.sap.sailing.android.buoy.positioning.app.valueobjects.MarkPingInfo;
import com.sap.sailing.android.shared.data.LeaderboardInfo;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessageSendingService;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.UUID;
public class PingHelper {
private static String TAG = PingHelper.class.getName();