Panagiotis Triantafyllou

telematics fixes

......@@ -2,8 +2,10 @@ package ly.warp.sdk.activities;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.Sensor;
......@@ -14,9 +16,9 @@ import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
......@@ -30,6 +32,14 @@ import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.location.LocationSettingsStatusCodes;
import com.google.android.gms.tasks.Task;
import com.google.android.material.snackbar.Snackbar;
import org.jetbrains.annotations.NotNull;
......@@ -37,9 +47,6 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -69,8 +76,8 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
mTvOrientationCount, mTvTouchCount;
private SensorManager mSensorManager;
private Sensor mSensor;
private Handler mHandler, mLocationHandler, mTouchHandler;
private Runnable mRunnable, mLocationRunnable, mTouchRunnable;
private Handler mHandler, mTouchHandler;
private Runnable mRunnable, mTouchRunnable;
private long lastUpdate = 0;
private float lastX, lastY, lastZ;
private float velocity = 0;
......@@ -95,6 +102,7 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
private final int RECORDS_INTERVAL = 5000;
private EditText mEtLimit, mEtSampleTime;
private RelativeLayout mRlMainScroll;
private int REQUEST_LOCATION_SETTINGS = 4000;
// ===========================================================
......@@ -148,8 +156,8 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
public void onBackPressed() {
super.onBackPressed();
if (mIsTripStarted) {
unregisterSensor();
stopLocationUpdates();
unregisterSensor();
}
}
......@@ -215,13 +223,13 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
}
if (view.getId() == R.id.ll_activate_button) {
if (mIsTripStarted) {
mIsTripStarted = false;
mTvTripButton.setText(R.string.cos_dlg_start_trip);
mEtLimit.setEnabled(true);
mEtSampleTime.setEnabled(true);
unregisterSensor();
stopLocationUpdates();
unregisterSensor();
initViews();
mIsTripStarted = false;
mTvTripButton.setText(R.string.cos_dlg_start_trip);
} else {
if (mEtLimit.getText().length() == 0) {
Snackbar.make(mLlTelematicsMain, "Please fill the Cut off field", Snackbar.LENGTH_SHORT).show();
......@@ -237,9 +245,6 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
}
};
requestLocationUpdates();
registerSensor();
mIsTripStarted = true;
mTvTripButton.setText(R.string.cos_dlg_stop_trip);
}
}
}
......@@ -253,18 +258,9 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// saveAccelerationDataToExternalStorage(jsonArray);
sendAccelerationDataToServer(jsonArray);
} else {
Snackbar.make(mLlTelematicsMain, "Storage Permission Denied", Snackbar.LENGTH_SHORT).show();
}
return;
}
if (requestCode == PERMISSION_REQUEST_ACCESS_FINE_LOCATION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startLocationUpdates();
enableLocationSettings();
} else {
Snackbar.make(mLlTelematicsMain, "Location Permission Denied", Snackbar.LENGTH_SHORT).show();
}
......@@ -273,17 +269,15 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
@Override
public void onLocationChanged(Location location) {
//TODO: comment the first block and uncomment the second block if needs revert to handler implementation
if (mLatitude != 0 && mLongitude != 0) {
mSpeed = calculateSpeed(mLatitude, mLongitude, location.getLatitude(), location.getLongitude(), (LOCATION_UPDATE_INTERVAL / 1000));
mTvAvgVelocity.setText(String.format("%.1f", Math.floor(mSpeed)) + " km/h");
}
if (mIsTripStarted) {
if (mLatitude != 0 && mLongitude != 0) {
mSpeed = calculateSpeed(mLatitude, mLongitude, location.getLatitude(), location.getLongitude(), (LOCATION_UPDATE_INTERVAL / 1000));
mTvAvgVelocity.setText(String.format("%.1f", Math.floor(mSpeed)) + " km/h");
}
// if (mLatitude != 0 && mLongitude != 0)
// requestLocationUpdatePeriodically(location);
mLatitude = location.getLatitude();
mLongitude = location.getLongitude();
mLatitude = location.getLatitude();
mLongitude = location.getLongitude();
}
}
@Override
......@@ -312,45 +306,33 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
}
}
// @Override
// public boolean dispatchTouchEvent(MotionEvent event) {
// if (mIsTripStarted) {
// switch (event.getAction()) {
// case MotionEvent.ACTION_DOWN:
// touchCount++;
// mTvTouchCount.setText(String.valueOf(touchCount));
// mTouchHandler.post(mTouchRunnable);
// return true;
// case MotionEvent.ACTION_UP:
// mTouchHandler.removeCallbacks(mTouchRunnable);
// return true;
// }
// }
// return super.dispatchTouchEvent(event);
// }
// @Override
// public boolean onTouchEvent(MotionEvent event) {
//// return super.onTouchEvent(event);
// if (mIsTripStarted) {
//// touchCount++;
//// mTvTouchCount.setText(String.valueOf(touchCount));
//// return true;
//
// switch (event.getAction()) {
// case MotionEvent.ACTION_DOWN:
// touchCount++;
// mTvTouchCount.setText(String.valueOf(touchCount));
// mTouchHandler.post(mTouchRunnable);
// return true;
// case MotionEvent.ACTION_UP:
// mTouchHandler.removeCallbacks(mTouchRunnable);
// return true;
// }
// return false;
// }
// return false;
// }
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_LOCATION_SETTINGS) {
switch (resultCode) {
case Activity.RESULT_OK:
mIsTripStarted = true;
mTvTripButton.setText(R.string.cos_dlg_stop_trip);
startLocationUpdates();
registerSensor();
break;
case Activity.RESULT_CANCELED:
if (!isFinishing()) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.cos_telematics));
builder.setMessage(getString(R.string.lbl_telematics_no_location))
.setCancelable(false)
.setNegativeButton(getString(R.string.cos_dlg_positive_button2), (dialog, id) -> {
});
AlertDialog alert = builder.create();
alert.show();
}
break;
default:
break;
}
}
}
// ===========================================================
// Methods
......@@ -359,6 +341,9 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
private void initViews() {
mTvVelocity.setText("0.0 m/s^2");
mTvAvgVelocity.setText("0.0 km/h");
mTvOrientationCount.setText(String.valueOf(orientationCount));
mTvTouchCount.setText(String.valueOf(touchCount));
mTvRecordsSaved.setText(String.valueOf(touchCount));
}
private void requestLocationUpdates() {
......@@ -368,7 +353,7 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSION_REQUEST_ACCESS_FINE_LOCATION);
} else {
startLocationUpdates();
enableLocationSettings();
}
}
......@@ -383,19 +368,6 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
}
}
private void requestLocationUpdatePeriodically(Location location) {
mLocationHandler = new Handler();
mLocationRunnable = new Runnable() {
@Override
public void run() {
double speed = calculateSpeed(mLatitude, mLongitude, location.getLatitude(), location.getLongitude(), (LOCATION_UPDATE_INTERVAL / 1000));
mTvAvgVelocity.setText(String.format("%.1f", Math.floor(speed)) + " km/h");
mLocationHandler.postDelayed(this, LOCATION_UPDATE_INTERVAL);
}
};
mLocationHandler.postDelayed(mLocationRunnable, LOCATION_UPDATE_INTERVAL);
}
private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
double x = Math.toRadians(lon2 - lon1) * Math.cos(Math.toRadians((lat1 + lat2) / 2));
double y = Math.toRadians(lat2 - lat1);
......@@ -403,24 +375,13 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
}
// Function to calculate speed in meters per second
private double calculateSpeed(double lat1, double lon1, double lat2, double lon2, double timeDifferenceInSeconds) {
private double calculateSpeed(double lat1, double lon1, double lat2, double lon2, int timeDifferenceInSeconds) {
double distance = calculateDistance(lat1, lon1, lat2, lon2);
return (distance / timeDifferenceInSeconds) * 3.6f; // Convert to km/h;
}
private void requestSingleLocationUpdate() {
try {
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, this, null);
} catch (SecurityException e) {
e.printStackTrace();
Snackbar.make(mLlTelematicsMain, "requestSingleLocationUpdate Exception", Snackbar.LENGTH_SHORT).show();
}
}
private void stopLocationUpdates() {
locationManager.removeUpdates(this);
if (mLocationHandler != null)
mLocationHandler.removeCallbacks(mLocationRunnable);
}
private String getCutOffLimit() {
......@@ -509,40 +470,7 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
}
jsonArray.put(jsonObject);
}
//TODO: uncomment if needed to write to file
// if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
// != PackageManager.PERMISSION_GRANTED) {
// ActivityCompat.requestPermissions(this,
// new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
// PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE);
// } else {
// saveAccelerationDataToExternalStorage(jsonArray); //TODO: uncomment if needed to write to file
sendAccelerationDataToServer(jsonArray);
// }
}
private void saveAccelerationDataToExternalStorage(JSONArray jsonArray) {
//TODO: comment if needed to write to file
// WarplyDBHelper.getInstance(this).saveTelematics(jsonArray);
//TODO: uncomment if needed to write to file
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
File documentsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
File file = new File(documentsDir, "telematics_data" + String.valueOf(System.currentTimeMillis()) + ".json");
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(jsonArray.toString().getBytes());
fileOutputStream.close();
Snackbar.make(mLlTelematicsMain, "Success saving data to file", Snackbar.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
Snackbar.make(mLlTelematicsMain, "Error saving acceleration data to file", Snackbar.LENGTH_SHORT).show();
}
} else {
Snackbar.make(mLlTelematicsMain, "External storage is not accessible", Snackbar.LENGTH_SHORT).show();
}
}
private void sendAccelerationDataToServer(JSONArray jsonArray) {
......@@ -594,12 +522,83 @@ public class TelematicsActivity extends Activity implements View.OnClickListener
}
};
// // Low-pass filter function using Exponential Moving Average (EMA)
// private float lowPassFilter(float currentValue) {
// float filteredValue = alpha * currentValue + (1 - alpha) * previousFilteredValue;
// previousFilteredValue = filteredValue;
// return filteredValue;
// }
private void enableLocationSettings() {
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationSettingsRequest.Builder locationBuilder = new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest);
Task<LocationSettingsResponse> result =
LocationServices.getSettingsClient(this).checkLocationSettings(locationBuilder.build());
result.addOnCompleteListener(task -> {
try {
LocationSettingsResponse response = task.getResult(ApiException.class);
// All location settings are satisfied. The client can initialize location
// requests here.
mIsTripStarted = true;
mTvTripButton.setText(R.string.cos_dlg_stop_trip);
startLocationUpdates();
registerSensor();
} catch (ApiException exception) {
switch (exception.getStatusCode()) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the
// user a dialog.
try {
// Cast to a resolvable exception.
ResolvableApiException resolvable = (ResolvableApiException) exception;
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
resolvable.startResolutionForResult(TelematicsActivity.this, REQUEST_LOCATION_SETTINGS);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
Log.v("SendIntentException", e.getMessage());
if (!isFinishing()) {
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this);
alertBuilder.setTitle(getString(R.string.cos_telematics));
alertBuilder.setMessage(getString(R.string.lbl_telematics_generic_error))
.setCancelable(false)
.setNegativeButton(getString(R.string.cos_dlg_positive_button2), (dialog, id) -> {
});
AlertDialog alert = alertBuilder.create();
alert.show();
}
} catch (ClassCastException e) {
// Ignore, should be an impossible error.
Log.v("ClassCastException", e.getMessage());
if (!isFinishing()) {
AlertDialog.Builder alertBuilder2 = new AlertDialog.Builder(this);
alertBuilder2.setTitle(getString(R.string.cos_telematics));
alertBuilder2.setMessage(getString(R.string.lbl_telematics_generic_error))
.setCancelable(false)
.setNegativeButton(getString(R.string.cos_dlg_positive_button2), (dialog, id) -> {
});
AlertDialog alert = alertBuilder2.create();
alert.show();
}
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
// Location settings are not satisfied. However, we have no way to fix the
// settings so we won't show the dialog.
Log.v("SETTINGS_CHANGE_UNAVAILABLE", "SETTINGS_CHANGE_UNAVAILABLE");
if (!isFinishing()) {
AlertDialog.Builder alertBuilder3 = new AlertDialog.Builder(this);
alertBuilder3.setTitle(getString(R.string.cos_telematics));
alertBuilder3.setMessage(getString(R.string.lbl_telematics_generic_error))
.setCancelable(false)
.setNegativeButton(getString(R.string.cos_dlg_positive_button2), (dialog, id) -> {
});
AlertDialog alert = alertBuilder3.create();
alert.show();
}
break;
}
}
});
}
// ===========================================================
// Inner and Anonymous Classes
......
......@@ -187,6 +187,8 @@
<string name="cos_telematics_history">Ιστορικό</string>
<string name="cos_telematics_history_analysis_title">Ανάλυση παραμέτρων ασφαλούς οδήγησης</string>
<string name="cos_telematics_history_analysis_rate">Αξιολογήστε την ανάλυση</string>
<string name="lbl_telematics_no_location">Για να ξεκινήσετε θα πρέπει να ενεργοποιήσετε την τοποθεσία σας</string>
<string name="lbl_telematics_generic_error">Άγνωστο σφάλμα</string>
<string-array name="coupons_array">
<item>Κουπόνια</item>
......