// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. package com.microsoft.asurerun.ui; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Point; import android.location.Location; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Looper; import android.provider.Settings; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.view.ViewTreeObserver; import android.widget.ImageButton; import android.widget.TextView; import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.model.LatLngBounds; import com.microsoft.appcenter.analytics.Analytics; import com.microsoft.asurerun.model.ApplicationState; import com.microsoft.asurerun.R; import com.microsoft.asurerun.model.RunItem; import com.microsoft.asurerun.model.ServerCalculations; import com.microsoft.asurerun.service.KeyGenService; import com.microsoft.asurerun.service.RunService; import com.google.android.gms.location.FusedLocationProviderClient; import com.google.android.gms.location.LocationCallback; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationResult; import com.google.android.gms.location.LocationServices; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.PolylineOptions; import com.microsoft.asurerun.service.SyncDataService; import com.microsoft.asurerun.util.CacheUtil; import com.microsoft.asurerun.util.Checks; import com.microsoft.asurerun.util.DateUtil; import com.microsoft.asurerun.util.DialogUtil; import com.microsoft.asurerun.util.MapUtil; import com.microsoft.asurerun.util.Utils; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import static android.view.View.GONE; import static com.microsoft.asurerun.service.KeyGenService.STATE_KEY; import static com.microsoft.asurerun.util.CacheUtil.KILOMETERS_VALUE; import static com.microsoft.asurerun.util.CacheUtil.KM_MILES_KEY; import static com.microsoft.asurerun.util.CacheUtil.MILES_VALUE; import static com.microsoft.asurerun.util.CacheUtil.SHAREDPREFFILE; import static com.microsoft.asurerun.util.CacheUtil.cacheMaxRunNumber; import static com.microsoft.asurerun.util.CacheUtil.loadMaxRunNumber; import static com.microsoft.asurerun.util.MapUtil.MAP_THUMBNAIL_HEIGHT; import static com.microsoft.asurerun.util.MapUtil.MAP_THUMBNAIL_WIDTH; import static com.microsoft.asurerun.util.ServiceUtil.*; import static java.lang.Math.cos; import static java.lang.Math.sin; import static java.lang.Math.toRadians; public class RunActivity extends AppCompatActivity implements OnMapReadyCallback { /** * Google maps variable */ private GoogleMap mMap; // TODO: we need to investigate if data is being sent to google in plain text. /* * View variable declaration */ private ImageButton mControlButton; private SupportMapFragment mapFragment; private TextView mTimeText, mDistanceText, mElevationGainText, mRunPace, mTextMessage, mDistanceInfo; /** * Constants declaration */ private final static int MAP_ZOOM = 18; private final long UPDATE_TIME = 5000; private final int PATH_WIDTH = 20; private final int MINIMUM_GPS_THRESHOLD = 30; // in meters private final static int MAP_Y_OFFSET = 300; // the offset required to center map public final static int POWER_SAVE_SETTING_REQUEST = 1100; private final static String TAG = "RunActivity"; private LatLng mLastLocation; // shared crypto context private long mCryptoContext; /* * Indicates the state of the RunActivity: */ public enum State { Stopped, Running } public State mCurrentState; protected List<LatLng> polylinePoints = new ArrayList<>(); private Intent backgroundServiceIntent; private LocationRequest mLocationRequest; private FusedLocationProviderClient mFusedLocationClient; private ArrayList<Double> cartesianX; private ArrayList<Double> cartesianY; private ArrayList<Double> cartesianZ; private ArrayList<float[]> accelerometerValues; private ArrayList<float[]> gyroscopeValues; private ArrayList<Double> timeStamps; private ArrayList<Double> elevationGainDeltas; private ArrayList<Double> avgPaceDeltas; private double mElevationGainSum; private double mDistance; private String mElapsedTime; private String unitMeasure; private long mTotalTime; private Bitmap mMapSnapshot; private static int FRAME_UPDATE_INTERVAL = 2; private int mFrameUpdateInterval = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_run_layout); ApplicationState.initializeRunData(); // retrieve crypto context from arguments Intent i = getIntent(); mCryptoContext = ApplicationState.getCryptoContext(); // set UI component mControlButton = (ImageButton) findViewById(R.id.stop); mTimeText = (TextView) findViewById(R.id.time); mDistanceText = (TextView) findViewById(R.id.distance); mElevationGainText = (TextView) findViewById(R.id.elevation); mRunPace = (TextView) findViewById(R.id.avg); mTextMessage = (TextView) findViewById(R.id.textMessageRun); //set distance label value mDistanceInfo = (TextView) findViewById(R.id.distanceInfo); SharedPreferences mSharedPref = getSharedPreferences(SHAREDPREFFILE, Context.MODE_PRIVATE); unitMeasure = mSharedPref.getString(KM_MILES_KEY, MILES_VALUE); mDistanceInfo.setText((unitMeasure.equals(MILES_VALUE) ? getString(R.string.run_activity_distance_miles) : getString(R.string.run_activity_distance_km))); if (ApplicationState.isKeysCreated()) { mTextMessage.setVisibility(GONE); } // check if power save mode is on if (Checks.isPowerSaveMode(this)) { Analytics.trackEvent(TAG + " power save mode is on"); showBatterySaveModeWarning(); } else { startRunService(); } mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); // Get the SupportMapFragment and request notification // when the map is ready to be used. mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map_run); mapFragment.getMapAsync(this); mControlButton.setOnClickListener(new View.OnClickListener() { @SuppressLint("MissingPermission") @Override public void onClick(View view) { if (mCurrentState == State.Running) { stopRunService(); if (!ApplicationState.isKeysCreated()) { // checks if the keys are created DialogUtil.showWarningDialog(RunActivity.this,getString(R.string.run_activity_keys_warning)); } else if (!Checks.IsNetworkConnected(RunActivity.this)) { startRunSummaryActivity(); sendRunData(); } else { createMapSnapshot(); } } } }); cartesianX = new ArrayList<Double>(); cartesianY = new ArrayList<Double>(); cartesianZ = new ArrayList<Double>(); timeStamps = new ArrayList<Double>(); gyroscopeValues = new ArrayList<>(); accelerometerValues = new ArrayList<>(); elevationGainDeltas = new ArrayList<>(); avgPaceDeltas = new ArrayList<>(); } private void startRunSummaryActivity() { unregisterReceiver(mBroadcastReceiver); ApplicationState.setAvgPaceValList(avgPaceDeltas); Intent myIntent = new Intent(RunActivity.this, RunSummaryActivity.class); myIntent.putExtra(DISTANCE_SUMMARY_KEY, mDistance); myIntent.putExtra(TIME_SUMMARY_KEY, mTotalTime); myIntent.putExtra(ELEVATION_GAIN_SUMMARY_KEY, mElevationGainSum); Bundle bundle = new Bundle(); bundle.putParcelableArrayList(RUN_COORDINATES_EXTRA_KEY, (ArrayList<LatLng>) polylinePoints); myIntent.putExtras(bundle); RunActivity.this.startActivity(myIntent); RunActivity.this.finish(); } @SuppressLint("MissingPermission") private void createMapSnapshot() { mMap.setMyLocationEnabled(false); LatLngBounds.Builder builder = new LatLngBounds.Builder(); for (LatLng latLng : polylinePoints) { builder.include(latLng); } final GoogleMap.SnapshotReadyCallback snapshotCallback = new GoogleMap.SnapshotReadyCallback() { @Override public void onSnapshotReady(Bitmap snapshot) { mMapSnapshot = Bitmap.createScaledBitmap(snapshot, MAP_THUMBNAIL_WIDTH, MAP_THUMBNAIL_HEIGHT, true); // scale bitmap startRunSummaryActivity(); sendRunData(); } }; //Sometimes the user has not moved. Causes a crash in builder.build. if (polylinePoints.size() > 0) { // resize the map final View mMapView = mapFragment.getView(); final LatLngBounds bounds = builder.build(); mMapView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //At this point the layout is complete and the //dimensions of map view and any child views are known. if (mMapView != null) { int width = mMapView.getWidth(); int height = mMapView.getHeight() / 2; Log.d(TAG, "h: " + height + " w: " + width); int padding = (int) (width * 0.12); // offset from edges of the map 12% of screen CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, width, height, padding); mMap.moveCamera(cu); mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() { @Override public void onMapLoaded() { mMap.snapshot(snapshotCallback); } }); } } }); } else { // take the snapshot even when the map is empty mMap.snapshot(snapshotCallback); } } private void startRunService() { // set running state mCurrentState = State.Running; // start RunService backgroundServiceIntent = new Intent(RunActivity.this, RunService.class); backgroundServiceIntent.setAction(START_TRACKER); startService(backgroundServiceIntent); } /** * Sends the runItem on server */ private void sendRunData() { SendRunItemsAsyncTask task = new SendRunItemsAsyncTask(); Utils.runAsyncTask(task); } private void stopRunService() { if (mCurrentState != State.Stopped) { removeLocationListener(); mCurrentState = State.Stopped; //stop background service backgroundServiceIntent = new Intent(RunActivity.this, RunService.class); backgroundServiceIntent.setAction(STOP_TRACKER); stopService(backgroundServiceIntent); } } /** * Register the broadcast receiver to Service intents */ BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(CONNECTIVITY_CHANGED_ACTION) || intent.getAction().equals(AIRPLANE_MODE_ACTION)){ if(Checks.IsNetworkConnected(RunActivity.this)){ // hide error label mTextMessage.setVisibility(View.GONE); }else if (mCurrentState == State.Running) { // show error label mTextMessage.setText(R.string.run_activity_internet_connection_warning); mTextMessage.setVisibility(View.VISIBLE); } }else if ( (intent.getAction().equals(GPS_PROVIDER_CHANGED_ACTION)) && !Checks.isLocationEnabled(RunActivity.this) ) { Analytics.trackEvent(TAG + " GPS error"); showGpsAccuracyWarning(); } else if (intent.getAction().equals(UPDATE_ACTION)) { updateUI(intent); updateSensorsData(intent); } else if (intent.getAction().equals(KEY_GEN_UPDATE_ACTION)) { if (intent.hasExtra(STATE_KEY)) { String event = intent.getStringExtra(STATE_KEY); if (event.equals(STATE_DONE)) { mTextMessage.setVisibility(GONE); // if run is stopped if (mCurrentState == State.Stopped) { createMapSnapshot(); } } else if (event.equals(KEY_GEN_STATE_ERROR)) { Analytics.trackEvent(TAG + " Keygen error"); showErrorDialog(); } } } } }; /** * Clears all the variables that are used to encrypt the GPS data */ private void clearGPSVariables() { cartesianX.clear(); cartesianY.clear(); cartesianZ.clear(); timeStamps.clear(); } @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; MapUtil.changeMapStyle(TAG, mMap, this); mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(UPDATE_TIME); mLocationRequest.setFastestInterval(UPDATE_TIME); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { //Location Permission already granted mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper()); mMap.setMyLocationEnabled(true); } } else { mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper()); mMap.setMyLocationEnabled(true); } } /** * updates sensors data * @param intent the update intent from Service */ private void updateSensorsData(Intent intent){ float accelerometerMatrix[] = intent.getFloatArrayExtra(ACCELEROMETER_KEY); Log.d(TAG,"Accelerometer x = "+accelerometerMatrix[0]+" y = "+accelerometerMatrix[1]+" z = "+accelerometerMatrix[2]); accelerometerValues.add(accelerometerMatrix); float gyroscopeMatrix[] = intent.getFloatArrayExtra(GYROSCOPE_KEY); Log.d(TAG,"Gyroscope x = "+gyroscopeMatrix[0]+" y = "+gyroscopeMatrix[1]+" z = "+gyroscopeMatrix[2]); gyroscopeValues.add(gyroscopeMatrix); } /** * This function allows to update UI * @param intent the update intent from Service */ private void updateUI(Intent intent) { // set elapsed time label mTotalTime = intent.getLongExtra(TIME_KEY, 0); mElapsedTime = String.format("%02d:%02d", TimeUnit.SECONDS.toMinutes(mTotalTime), TimeUnit.SECONDS.toSeconds(mTotalTime - TimeUnit.MINUTES.toSeconds(TimeUnit.SECONDS.toMinutes(mTotalTime)) )); if (mElapsedTime != null) { // shows the elapsed time on the screen mTimeText.setText(mElapsedTime); } double latitude = intent.getDoubleExtra(LATITUDE_KEY, 0); double longitude = intent.getDoubleExtra(LONGITUDE_KEY, 0); LatLng latLng = new LatLng(latitude, longitude); // update map every second if (latitude != 0 && longitude != 0) { mLastLocation = latLng; Log.e(TAG, latLng.toString()); updateMap(latLng); } else if (mLastLocation != null) { Log.e(TAG, mLastLocation.toString()); updateMap(mLastLocation); } if(++mFrameUpdateInterval != FRAME_UPDATE_INTERVAL) { return; } mFrameUpdateInterval = 0; if (latitude != 0 && longitude != 0) { polylinePoints.add(latLng); // converting coordinates convertCartesian(latLng.latitude, latLng.longitude); mLastLocation = latLng; Log.e(TAG, latLng.toString()); updateMap(latLng); } // add new elevation gain delta mDistance = intent.getDoubleExtra(DISTANCE_KEY, 0.0); // return distance in km mElevationGainSum += intent.getDoubleExtra(ELEVATION_GAIN_KEY, 0.0); elevationGainDeltas.add(intent.getDoubleExtra(ELEVATION_GAIN_KEY, 0.0)); if (unitMeasure.equals(KILOMETERS_VALUE)) { // shows distance in miles mDistanceText.setText(String.format("%.1f", mDistance)); mElevationGainText.setText(String.format("%.1f ", mElevationGainSum)); mElevationGainText.append(getString(R.string.run_settings_distance_unit_meter)); // save elevation gain deltas to draw graph ApplicationState.getElevationGainDeltas().add(intent.getDoubleExtra(ELEVATION_GAIN_KEY, 0.0)); } else { // shows distance in miles mDistanceText.setText(String.format("%.1f", mDistance / MILES_KILOMETERS_CONVERSION_RATE)); mElevationGainText.setText(String.format("%.1f ", mElevationGainSum / METER_FOOT_CONVERSION_RATE)); mElevationGainText.append(getString(R.string.run_settings_distance_unit_foot)); // save elevation gain deltas to draw graph ApplicationState.getElevationGainDeltas().add(intent.getDoubleExtra(ELEVATION_GAIN_KEY, 0.0) / METER_FOOT_CONVERSION_RATE); } if (mDistance > 0.01) { double avgPace = 0; if (unitMeasure.equals(MILES_VALUE)) { double distance = mDistance / MILES_KILOMETERS_CONVERSION_RATE; // convert in Miles avgPace = (long) (mTotalTime / distance); } else { avgPace = (long) (mTotalTime / mDistance); } // shows the avgPace on the screen mRunPace.setText(DateUtil.getFormattedTimeFromSeconds((long) avgPace)); // save avg pace deltas to draw graph avgPaceDeltas.add(avgPace); } else { avgPaceDeltas.add(0d); } // Adding timestamp if (intent.hasExtra(TIME_KEY)) { double timestamp = intent.getDoubleExtra(TIMESTAMP_KEY, 0); Log.e(TAG, "TIME_ENTRY : " + timestamp); timeStamps.add(timestamp); } } private void updateMap(LatLng latLng) { // center map to current location autoCenterMap(latLng); drawRoute(); } private void autoCenterMap(LatLng latLng) { //move map camera Point mMapPoint = mMap.getProjection().toScreenLocation(latLng); // add y offset to avoid overlay with mDistanceText mMapPoint.set(mMapPoint.x, mMapPoint.y + MAP_Y_OFFSET); mMap.moveCamera(CameraUpdateFactory.newLatLng(mMap.getProjection().fromScreenLocation(mMapPoint))); } /** * @param latitudeDegrees latitude in degrees * @param longitudeDegrees longitude in degrees */ private void convertCartesian(double latitudeDegrees, double longitudeDegrees) { Log.e(TAG, "add coordinates"); double latitudeRadians = toRadians(latitudeDegrees); double longitudeRadians = toRadians(longitudeDegrees); cartesianX.add(ServerCalculations.EARTH_RADIUS * sin(longitudeRadians) * cos(latitudeRadians)); cartesianY.add(ServerCalculations.EARTH_RADIUS * sin(latitudeRadians)); cartesianZ.add(ServerCalculations.EARTH_RADIUS * cos(longitudeRadians) * cos(latitudeRadians)); } @Override protected void onStart() { super.onStart(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(UPDATE_ACTION); intentFilter.addAction(AIRPLANE_MODE_ACTION); intentFilter.addAction(GPS_PROVIDER_CHANGED_ACTION); intentFilter.addAction(KEY_GEN_UPDATE_ACTION); intentFilter.addAction(CONNECTIVITY_CHANGED_ACTION); registerReceiver(mBroadcastReceiver, intentFilter); } /** * This function allows to draw a route on a map */ private void drawRoute() { if (polylinePoints.size() > 1) { mMap.addPolyline(new PolylineOptions() .addAll(polylinePoints) .width(PATH_WIDTH) .color(Color.argb(255, 255, 140, 0)) ); } } LocationCallback mLocationCallback = new LocationCallback() { @Override public void onLocationResult(LocationResult locationResult) { List<Location> locationList = locationResult.getLocations(); if (locationList.size() > 0) { Location location = locationList.get(locationList.size() - 1); if (location.hasAccuracy() && location.getAccuracy() >= MINIMUM_GPS_THRESHOLD) { showGpsAccuracyWarning(); } Log.i(TAG, "Location: " + location.getLatitude() + " " + location.getLongitude()); //Place current location marker LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude()); //move map camera mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, MAP_ZOOM)); Point mMapPoint = mMap.getProjection().toScreenLocation(latLng); mMapPoint.set(mMapPoint.x, mMapPoint.y + MAP_Y_OFFSET); mMap.moveCamera(CameraUpdateFactory.newLatLng(mMap.getProjection().fromScreenLocation(mMapPoint))); // remove GPS listener removeLocationListener(); //} } } }; /** * Stop location updates when Activity is no longer active */ private void removeLocationListener() { if (mFusedLocationClient != null) { mFusedLocationClient.removeLocationUpdates(mLocationCallback); } } @Override protected void onStop() { super.onStop(); removeLocationListener(); } @Override protected void onDestroy() { super.onDestroy(); stopService(new Intent(this, RunService.class)); removeLocationListener(); } /** * Shows the error and asks the user if to exit or try again */ private void showErrorDialog() { DialogInterface.OnClickListener positiveListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // start keygenService Intent intent = new Intent(RunActivity.this, KeyGenService.class); RunActivity.this.startService(intent); } }; DialogInterface.OnClickListener negativeListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // delete user token and info CacheUtil.deleteUserTokenAndInfo(RunActivity.this); finishActivity(true); } }; // show error dialog DialogUtil.showDialogPositiveAndNegativeButtons(RunActivity.this, getString(R.string.set_keys_error_title), getString(R.string.set_keys_error_message), positiveListener, getString(R.string.retry_button_error), negativeListener, getString(R.string.exit_button_error)); } /** * Shows the gps warning and asks the user if to exit or continue */ private void showGpsAccuracyWarning() { DialogInterface.OnClickListener positiveListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }; DialogInterface.OnClickListener negativeListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finishActivity(false); } }; // show error dialog DialogUtil.showDialogPositiveAndNegativeButtons(RunActivity.this, RunActivity.this.getString(R.string.run_activity_warning_title), RunActivity.this.getString(R.string.run_activity_warning_gps_message), positiveListener, "ok", negativeListener, RunActivity.this.getString(R.string.exit_button_error)); } /** * Shows the gps warning and asks the user if to exit or continue */ private void showBatterySaveModeWarning() { DialogInterface.OnClickListener positiveListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // show Android settings RunActivity.this.startActivityForResult(new Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS), POWER_SAVE_SETTING_REQUEST); } }; DialogInterface.OnClickListener negativeListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finishActivity(false); } }; // show error dialog DialogUtil.showDialogPositiveAndNegativeButtons(RunActivity.this, RunActivity.this.getString(R.string.run_activity_warning_title), RunActivity.this.getString(R.string.run_activity_warning_battery_save_mode_message), positiveListener, RunActivity.this.getString(R.string.run_activity_positive_battery_save_mode_text), negativeListener, RunActivity.this.getString(R.string.exit_button_error)); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == POWER_SAVE_SETTING_REQUEST) { if (Checks.isPowerSaveMode(this)) { showBatterySaveModeWarning(); } else { startRunService(); } } } @Override public void onBackPressed() { // we want to disable back button in this activity } @Override public void onResume() { super.onResume(); // check onResume if the keys are created if (ApplicationState.isKeysCreated()) { //hide message mTextMessage.setVisibility(View.GONE); } } /** * @param finishAll if true finish all activities, if false finish only RunActivity */ private void finishActivity(boolean finishAll) { // get the activity context Context context = RunActivity.this; //unregister receiver unregisterReceiver(mBroadcastReceiver); if (finishAll) { // finish this activity ((Activity) context).finish(); } else { // finish this activity ((Activity) context).finishAffinity(); } } private class SendRunItemsAsyncTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { try { // Create a new item final RunItem item = new RunItem(); ServerCalculations calculations = new ServerCalculations(item); calculations.processAndEncrypt(cartesianX, cartesianY, cartesianZ, timeStamps, gyroscopeValues, accelerometerValues, elevationGainDeltas, avgPaceDeltas, mElevationGainSum, mMapSnapshot); clearGPSVariables(); Log.e(TAG, "Inserting run data on Server "); Analytics.trackEvent("Inserting run data on Server "); // Get the encrypted cipher texts as base64 strings int runNumber = loadMaxRunNumber(RunActivity.this); runNumber++; cacheMaxRunNumber(RunActivity.this, runNumber); item.setKeyId(ApplicationState.getInsertedKeyItem().getId()); item.setRunNumber(runNumber); //These are fields used to temporarily show the item not yet loaded on the server item.setIsLocalItem(true); Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); item.setDate(calendar); //item.setCoordinates(polylinePoints); item.setTotalDistance(mDistance); item.setElevationGain(mElevationGainSum); item.setTotalTime(mTotalTime); if (mMapSnapshot != null) item.setMapSnapshot(mMapSnapshot); // add the current run item in the local memory ApplicationState.getLocalRunItems().add(0, item); ApplicationState.storeLocalRunItem(item); // send data on server SyncDataService.startSendDataOnServer(RunActivity.this); } catch (final Exception e) { Log.e(TAG, "Error inserting run data on Server " + e.getLocalizedMessage()); } return null; } } }