diff --git a/apk/GPSLogger-3.0.0.apk b/apk/GPSLogger-3.0.0.apk new file mode 100644 index 00000000..7f994db4 Binary files /dev/null and b/apk/GPSLogger-3.0.0.apk differ diff --git a/apk/GPSLogger-latest.apk b/apk/GPSLogger-latest.apk index ea313e72..7f994db4 100644 Binary files a/apk/GPSLogger-latest.apk and b/apk/GPSLogger-latest.apk differ diff --git a/app/build.gradle b/app/build.gradle index b570b24a..75cc2d4b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,8 +41,8 @@ android { // ----------------------------------------------------------------------------------------- // We use the Semantic Versioning (https://semver.org/): - versionName '2.3.2' - versionCode 37 + versionName '3.0.0' + versionCode 38 // ----------------------------------------------------------------------------------------- vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dd69ed18..792a95c7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,10 @@ " + newLine); - KMLbw.write("" + newLine); + kmlBW.write("" + newLine); - KMLbw.write("" + newLine); - KMLbw.write(" " + newLine); - KMLbw.write(" GPS Logger " + track.getName() + "" + newLine); - KMLbw.write(" " + - String.valueOf(track.getNumberOfPlacemarks()) + " Placemarks]]>" + newLine); + kmlBW.write("" + newLine); + kmlBW.write(" " + newLine); + kmlBW.write(" GPS Logger " + track.getName() + "" + newLine); + kmlBW.write(" " + newLine); if (track.getNumberOfLocations() > 0) { - KMLbw.write(" " + newLine); + kmlBW.write(" " + newLine); } if (track.getNumberOfPlacemarks() > 0) { - KMLbw.write(" " + newLine); + kmlBW.write(" " + newLine); } - KMLbw.write(newLine); + kmlBW.write(newLine); } - if (ExportGPX) { + if (exportGPX) { // Writing head of GPX file - GPXbw.write("" + newLine); - GPXbw.write("" + newLine); - GPXbw.write("" + newLine); + gpxBW.write("" + newLine + newLine); if (track.getNumberOfLocations() > 0) { - GPXbw.write("" + newLine); + gpxBW.write("" + newLine); PhysicalDataFormatter phdformatter = new PhysicalDataFormatter(); PhysicalData phdDuration; PhysicalData phdDurationMoving; @@ -296,40 +358,41 @@ public void run() { PhysicalData phdAltitudeGap; PhysicalData phdOverallDirection; phdDuration = phdformatter.format(track.getDuration(), PhysicalDataFormatter.FORMAT_DURATION); - phdDurationMoving = phdformatter.format(track.getDuration_Moving(), PhysicalDataFormatter.FORMAT_DURATION); + phdDurationMoving = phdformatter.format(track.getDurationMoving(), PhysicalDataFormatter.FORMAT_DURATION); phdSpeedMax = phdformatter.format(track.getSpeedMax(), PhysicalDataFormatter.FORMAT_SPEED); phdSpeedAvg = phdformatter.format(track.getSpeedAverage(), PhysicalDataFormatter.FORMAT_SPEED_AVG); phdSpeedAvgMoving = phdformatter.format(track.getSpeedAverageMoving(), PhysicalDataFormatter.FORMAT_SPEED_AVG); phdDistance = phdformatter.format(track.getEstimatedDistance(), PhysicalDataFormatter.FORMAT_DISTANCE); - phdAltitudeGap = phdformatter.format(track.getEstimatedAltitudeGap(GPSApp.getPrefEGM96AltitudeCorrection()), PhysicalDataFormatter.FORMAT_ALTITUDE); + phdAltitudeGap = phdformatter.format(track.getEstimatedAltitudeGap(gpsApp.getPrefEGM96AltitudeCorrection()), PhysicalDataFormatter.FORMAT_ALTITUDE); phdOverallDirection = phdformatter.format(track.getBearing(), PhysicalDataFormatter.FORMAT_BEARING); - if (!phdDistance.Value.isEmpty()) - GPXbw.write("" + newLine); - if (!phdDuration.Value.isEmpty()) - GPXbw.write("" + newLine); - if (!phdAltitudeGap.Value.isEmpty()) - GPXbw.write("" + newLine); - if (!phdSpeedMax.Value.isEmpty()) - GPXbw.write("" + newLine); - if (!phdSpeedAvg.Value.isEmpty()) - GPXbw.write("" + newLine); - if (!phdOverallDirection.Value.isEmpty()) - GPXbw.write("" + newLine); - GPXbw.write(newLine); + if (!phdDistance.value.isEmpty()) + gpxBW.write("" + newLine); + if (!phdDuration.value.isEmpty()) + gpxBW.write("" + newLine); + if (!phdAltitudeGap.value.isEmpty()) + gpxBW.write("" + newLine); + if (!phdSpeedMax.value.isEmpty()) + gpxBW.write("" + newLine); + if (!phdSpeedAvg.value.isEmpty()) + gpxBW.write("" + newLine); + if (!phdOverallDirection.value.isEmpty()) + gpxBW.write("" + newLine); + gpxBW.write(newLine); } if (getPrefGPXVersion == GPX1_0) { // GPX 1.0 - GPXbw.write("" + newLine); - GPXbw.write("GPS Logger " + track.getName() + "" + newLine); - GPXbw.write("" + newLine + newLine); + gpxBW.write("GPS Logger " + track.getName() + "" + newLine); + if (!track.getDescription().isEmpty()) gpxBW.write("" + stringToXML(track.getDescription()) + "" + newLine); + gpxBW.write("" + newLine + newLine); } if (getPrefGPXVersion == GPX1_1) { // GPX 1.1 - GPXbw.write("" + newLine); // - GPXbw.write(" " + newLine); // GPX Metadata - GPXbw.write(" GPS Logger " + track.getName() + "" + newLine); - GPXbw.write(" " + newLine); - GPXbw.write("" + newLine + newLine); + gpxBW.write(" " + newLine); // GPX Metadata + gpxBW.write(" GPS Logger " + track.getName() + "" + newLine); + if (!track.getDescription().isEmpty()) gpxBW.write(" " + stringToXML(track.getDescription()) + "" + newLine); + gpxBW.write(" " + newLine); + gpxBW.write("" + newLine + newLine); } } - if (ExportTXT) { + if (exportTXT) { // Writing head of TXT file - TXTbw.write("type,date time,latitude,longitude,accuracy(m),altitude(m),geoid_height(m),speed(m/s),bearing(deg),sat_used,sat_inview,name,desc" + newLine); + txtBW.write("type,date time,latitude,longitude,accuracy(m),altitude(m),geoid_height(m),speed(m/s),bearing(deg),sat_used,sat_inview,name,desc" + newLine); } String formattedLatitude = ""; @@ -362,111 +426,101 @@ public void run() { // Writes track headings - List placemarkList = new ArrayList<>(GroupOfLocations); + List placemarkList = new ArrayList<>(groupOfLocations); - for (int i = 0; i <= track.getNumberOfPlacemarks(); i += GroupOfLocations) { + for (int i = 0; i <= track.getNumberOfPlacemarks(); i += groupOfLocations) { //Log.w("myApp", "[#] Exporter.java - " + (i + GroupOfLocations)); - placemarkList.addAll(GPSApp.GPSDataBase.getPlacemarksList(track.getId(), i, i + GroupOfLocations - 1)); + placemarkList.addAll(gpsApp.gpsDataBase.getPlacemarksList(track.getId(), i, i + groupOfLocations - 1)); if (!placemarkList.isEmpty()) { for (LocationExtended loc : placemarkList) { formattedLatitude = String.format(Locale.US, "%.8f", loc.getLocation().getLatitude()); formattedLongitude = String.format(Locale.US, "%.8f", loc.getLocation().getLongitude()); - if (loc.getLocation().hasAltitude()) formattedAltitude = String.format(Locale.US, "%.3f", loc.getLocation().getAltitude() + AltitudeManualCorrection - (((loc.getAltitudeEGM96Correction() == NOT_AVAILABLE) || (!EGMAltitudeCorrection)) ? 0 : loc.getAltitudeEGM96Correction())); - if(ExportGPX || ExportTXT) { + if (loc.getLocation().hasAltitude()) formattedAltitude = String.format(Locale.US, "%.3f", loc.getLocation().getAltitude() + altitudeManualCorrection - (((loc.getAltitudeEGM96Correction() == NOT_AVAILABLE) || (!egmAltitudeCorrection)) ? 0 : loc.getAltitudeEGM96Correction())); + if(exportGPX || exportTXT) { if (loc.getLocation().hasSpeed()) formattedSpeed = String.format(Locale.US, "%.3f", loc.getLocation().getSpeed()); } // KML - if (ExportKML) { - KMLbw.write(" " + newLine); - KMLbw.write(" "); - KMLbw.write(loc.getDescription() - .replace("<","<") - .replace("&","&") - .replace(">",">") - .replace("\"",""") - .replace("'","'")); - KMLbw.write("" + newLine); - KMLbw.write(" #PlacemarkStyle" + newLine); - KMLbw.write(" " + newLine); - KMLbw.write(" " + (getPrefKMLAltitudeMode == 1 ? "clampToGround" : "absolute") + "" + newLine); - KMLbw.write(" "); + if (exportKML) { + kmlBW.write(" " + newLine); + kmlBW.write(" "); + kmlBW.write(stringToXML(loc.getDescription())); + kmlBW.write("" + newLine); + kmlBW.write(" #PlacemarkStyle" + newLine); + kmlBW.write(" " + newLine); + kmlBW.write(" " + (getPrefKMLAltitudeMode == 1 ? "clampToGround" : "absolute") + "" + newLine); + kmlBW.write(" "); if (loc.getLocation().hasAltitude()) { - KMLbw.write(formattedLongitude + "," + formattedLatitude + "," + formattedAltitude); + kmlBW.write(formattedLongitude + "," + formattedLatitude + "," + formattedAltitude); } else { - KMLbw.write(formattedLongitude + "," + formattedLatitude + ",0"); + kmlBW.write(formattedLongitude + "," + formattedLatitude + ",0"); } - KMLbw.write("" + newLine); - KMLbw.write(" 1" + newLine); - KMLbw.write(" " + newLine); - KMLbw.write(" " + newLine + newLine); + kmlBW.write("" + newLine); + kmlBW.write(" 1" + newLine); + kmlBW.write(" " + newLine); + kmlBW.write(" " + newLine + newLine); } // GPX - if (ExportGPX) { - GPXbw.write(""); + if (exportGPX) { + gpxBW.write(""); if (loc.getLocation().hasAltitude()) { - GPXbw.write(""); // Elevation - GPXbw.write(formattedAltitude); - GPXbw.write(""); + gpxBW.write(""); // Elevation + gpxBW.write(formattedAltitude); + gpxBW.write(""); } - GPXbw.write(""); + gpxBW.write(""); // Name + gpxBW.write(stringToXML(loc.getDescription())); + gpxBW.write(""); if (loc.getNumberOfSatellitesUsedInFix() > 0) { // Satellites used in fix - GPXbw.write(""); - GPXbw.write(String.valueOf(loc.getNumberOfSatellitesUsedInFix())); - GPXbw.write(""); + gpxBW.write(""); + gpxBW.write(String.valueOf(loc.getNumberOfSatellitesUsedInFix())); + gpxBW.write(""); } - GPXbw.write("" + newLine + newLine); + gpxBW.write("" + newLine + newLine); } // TXT - if (ExportTXT) { + if (exportTXT) { //type,time,latitude,longitude,altitude (m),geoid_height (m),speed (m/s),sat_used,sat_inview,name,desc - //TXTbw.write("W," + dfdtTXT.format(loc.getLocation().getTime()) + "," + formattedLatitude + "," + formattedLongitude + ","); - TXTbw.write("W," + (((loc.getLocation().getTime() % 1000L) == 0L) ? + //txtBW.write("W," + dfdtTXT.format(loc.getLocation().getTime()) + "," + formattedLatitude + "," + formattedLongitude + ","); + txtBW.write("W," + (((loc.getLocation().getTime() % 1000L) == 0L) ? dfdtTXT_NoMillis.format(loc.getLocation().getTime()) : dfdtTXT.format(loc.getLocation().getTime())) + "," + formattedLatitude + "," + formattedLongitude + ","); if (loc.getLocation().hasAccuracy()) - TXTbw.write(String.format(Locale.US, "%.0f", loc.getLocation().getAccuracy())); - TXTbw.write(","); + txtBW.write(String.format(Locale.US, "%.0f", loc.getLocation().getAccuracy())); + txtBW.write(","); if (loc.getLocation().hasAltitude()) - TXTbw.write(formattedAltitude); - TXTbw.write(","); - if ((loc.getAltitudeEGM96Correction() != NOT_AVAILABLE) && (EGMAltitudeCorrection)) - TXTbw.write(String.format(Locale.US, "%.3f",loc.getAltitudeEGM96Correction())); - TXTbw.write(","); + txtBW.write(formattedAltitude); + txtBW.write(","); + if ((loc.getAltitudeEGM96Correction() != NOT_AVAILABLE) && (egmAltitudeCorrection)) + txtBW.write(String.format(Locale.US, "%.3f",loc.getAltitudeEGM96Correction())); + txtBW.write(","); if (loc.getLocation().hasSpeed()) - TXTbw.write(formattedSpeed); - TXTbw.write(","); + txtBW.write(formattedSpeed); + txtBW.write(","); if (loc.getLocation().hasBearing()) - TXTbw.write(String.format(Locale.US, "%.0f", loc.getLocation().getBearing())); - TXTbw.write(","); + txtBW.write(String.format(Locale.US, "%.0f", loc.getLocation().getBearing())); + txtBW.write(","); if (loc.getNumberOfSatellitesUsedInFix() > 0) - TXTbw.write(String.valueOf(loc.getNumberOfSatellitesUsedInFix())); - TXTbw.write(","); + txtBW.write(String.valueOf(loc.getNumberOfSatellitesUsedInFix())); + txtBW.write(","); if (loc.getNumberOfSatellites() > 0) - TXTbw.write(String.valueOf(loc.getNumberOfSatellites())); - TXTbw.write(","); + txtBW.write(String.valueOf(loc.getNumberOfSatellites())); + txtBW.write(","); // Name is an empty field - TXTbw.write(","); - TXTbw.write(loc.getDescription().replace(",","_")); - TXTbw.write(newLine); + txtBW.write(","); + txtBW.write(loc.getDescription().replace(",","_")); + txtBW.write(newLine); } placemark_id++; @@ -490,7 +544,7 @@ public void run() { if (track.getNumberOfLocations() > 0) { // Writes track headings - if (ExportKML) { + if (exportKML) { PhysicalDataFormatter phdformatter = new PhysicalDataFormatter(); PhysicalData phdDuration; PhysicalData phdDurationMoving; @@ -501,146 +555,148 @@ public void run() { PhysicalData phdAltitudeGap; PhysicalData phdOverallDirection; phdDuration = phdformatter.format(track.getDuration(),PhysicalDataFormatter.FORMAT_DURATION); - phdDurationMoving = phdformatter.format(track.getDuration_Moving(),PhysicalDataFormatter.FORMAT_DURATION); + phdDurationMoving = phdformatter.format(track.getDurationMoving(),PhysicalDataFormatter.FORMAT_DURATION); phdSpeedMax = phdformatter.format(track.getSpeedMax(),PhysicalDataFormatter.FORMAT_SPEED); phdSpeedAvg = phdformatter.format(track.getSpeedAverage(),PhysicalDataFormatter.FORMAT_SPEED_AVG); phdSpeedAvgMoving = phdformatter.format(track.getSpeedAverageMoving(),PhysicalDataFormatter.FORMAT_SPEED_AVG); phdDistance = phdformatter.format(track.getEstimatedDistance(),PhysicalDataFormatter.FORMAT_DISTANCE); - phdAltitudeGap = phdformatter.format(track.getEstimatedAltitudeGap(GPSApp.getPrefEGM96AltitudeCorrection()),PhysicalDataFormatter.FORMAT_ALTITUDE); + phdAltitudeGap = phdformatter.format(track.getEstimatedAltitudeGap(gpsApp.getPrefEGM96AltitudeCorrection()),PhysicalDataFormatter.FORMAT_ALTITUDE); phdOverallDirection = phdformatter.format(track.getBearing(),PhysicalDataFormatter.FORMAT_BEARING); - String TrackDesc = GPSApp.getApplicationContext().getString(R.string.distance) + " = " + phdDistance.Value + " " + phdDistance.UM + - "
" + GPSApp.getApplicationContext().getString(R.string.duration) + " = " + phdDuration.Value + " | " + phdDurationMoving.Value + - "
" + GPSApp.getApplicationContext().getString(R.string.altitude_gap) + " = " + phdAltitudeGap.Value + " " + phdAltitudeGap.UM + - "
" + GPSApp.getApplicationContext().getString(R.string.max_speed) + " = " + phdSpeedMax.Value + " " + phdSpeedMax.UM + - "
" + GPSApp.getApplicationContext().getString(R.string.average_speed) + " = " + phdSpeedAvg.Value + " | " + phdSpeedAvgMoving.Value + " " + phdSpeedAvg.UM + - "
" + GPSApp.getApplicationContext().getString(R.string.direction) + " = " + phdOverallDirection.Value + " " + phdOverallDirection.UM + - "

" + track.getNumberOfLocations() + " " + GPSApp.getApplicationContext().getString(R.string.trackpoints) + "" ; - - KMLbw.write(" " + newLine); - KMLbw.write(" " + GPSApp.getApplicationContext().getString(R.string.tab_track) + " " + track.getName() + "" + newLine); - KMLbw.write(" " + newLine); - KMLbw.write(" #TrackStyle" + newLine); - KMLbw.write(" " + newLine); - KMLbw.write(" 0" + newLine); - KMLbw.write(" 0" + newLine); - KMLbw.write(" " + (getPrefKMLAltitudeMode == 1 ? "clampToGround" : "absolute") + "" + newLine); - KMLbw.write(" " + newLine); + String TrackDesc = (track.getDescription().isEmpty() ? "" : "" + stringToCDATA(track.getDescription()) + "

") + + gpsApp.getApplicationContext().getString(R.string.distance) + " = " + phdDistance.value + " " + phdDistance.um + + "
" + gpsApp.getApplicationContext().getString(R.string.duration) + " = " + phdDuration.value + " | " + phdDurationMoving.value + + "
" + gpsApp.getApplicationContext().getString(R.string.altitude_gap) + " = " + phdAltitudeGap.value + " " + phdAltitudeGap.um + + "
" + gpsApp.getApplicationContext().getString(R.string.max_speed) + " = " + phdSpeedMax.value + " " + phdSpeedMax.um + + "
" + gpsApp.getApplicationContext().getString(R.string.average_speed) + " = " + phdSpeedAvg.value + " | " + phdSpeedAvgMoving.value + " " + phdSpeedAvg.um + + "
" + gpsApp.getApplicationContext().getString(R.string.direction) + " = " + phdOverallDirection.value + " " + phdOverallDirection.um + + "

" + track.getNumberOfLocations() + " " + gpsApp.getApplicationContext().getString(R.string.trackpoints) + "" ; + + kmlBW.write(" " + newLine); + kmlBW.write(" " + gpsApp.getApplicationContext().getString(R.string.tab_track) + " " + track.getName() + "" + newLine); + kmlBW.write(" " + newLine); + kmlBW.write(" #TrackStyle" + newLine); + kmlBW.write(" " + newLine); + kmlBW.write(" 0" + newLine); + kmlBW.write(" 0" + newLine); + kmlBW.write(" " + (getPrefKMLAltitudeMode == 1 ? "clampToGround" : "absolute") + "" + newLine); + kmlBW.write(" " + newLine); } - if (ExportGPX) { - GPXbw.write("" + newLine); - GPXbw.write(" " + GPSApp.getApplicationContext().getString(R.string.tab_track) + " " + track.getName() + "" + newLine); - GPXbw.write(" " + newLine); + if (exportGPX) { + gpxBW.write("" + newLine); + gpxBW.write(" " + gpsApp.getApplicationContext().getString(R.string.tab_track) + " " + track.getName() + "" + newLine); + gpxBW.write(" " + newLine); } LocationExtended loc; for (int i = 0; i < track.getNumberOfLocations(); i++) { - loc = ArrayGeopoints.take(); + loc = arrayGeopoints.take(); // Create formatted strings formattedLatitude = String.format(Locale.US, "%.8f", loc.getLocation().getLatitude()); formattedLongitude = String.format(Locale.US, "%.8f", loc.getLocation().getLongitude()); - if (loc.getLocation().hasAltitude()) formattedAltitude = String.format(Locale.US, "%.3f", loc.getLocation().getAltitude() + AltitudeManualCorrection - (((loc.getAltitudeEGM96Correction() == NOT_AVAILABLE) || (!EGMAltitudeCorrection)) ? 0 : loc.getAltitudeEGM96Correction())); - if(ExportGPX || ExportTXT) { + if (loc.getLocation().hasAltitude()) formattedAltitude = String.format(Locale.US, "%.3f", loc.getLocation().getAltitude() + altitudeManualCorrection - (((loc.getAltitudeEGM96Correction() == NOT_AVAILABLE) || (!egmAltitudeCorrection)) ? 0 : loc.getAltitudeEGM96Correction())); + if(exportGPX || exportTXT) { if (loc.getLocation().hasSpeed()) formattedSpeed = String.format(Locale.US, "%.3f", loc.getLocation().getSpeed()); } // KML - if (ExportKML) { - if (loc.getLocation().hasAltitude()) KMLbw.write(" " + formattedLongitude + "," + formattedLatitude + "," + formattedAltitude + newLine); - else KMLbw.write(" " + formattedLongitude + "," + formattedLatitude + ",0" + newLine); + if (exportKML) { + if (loc.getLocation().hasAltitude()) kmlBW.write(" " + formattedLongitude + "," + formattedLatitude + "," + formattedAltitude + newLine); + else kmlBW.write(" " + formattedLongitude + "," + formattedLatitude + ",0" + newLine); } // GPX - if (ExportGPX) { - GPXbw.write(" "); + if (exportGPX) { + gpxBW.write(" "); if (loc.getLocation().hasAltitude()) { - GPXbw.write(""); // Elevation - GPXbw.write(formattedAltitude); - GPXbw.write(""); + gpxBW.write(""); // Elevation + gpxBW.write(formattedAltitude); + gpxBW.write(""); } - GPXbw.write(""); if (getPrefGPXVersion == GPX1_0) { if (loc.getLocation().hasSpeed()) { - GPXbw.write(""); // Speed - GPXbw.write(formattedSpeed); - GPXbw.write(""); + gpxBW.write(""); // Speed + gpxBW.write(formattedSpeed); + gpxBW.write(""); } } if (loc.getNumberOfSatellitesUsedInFix() > 0) { // GPX standards requires sats used for FIX. - GPXbw.write(""); // and NOT the number of satellites in view!!! - GPXbw.write(String.valueOf(loc.getNumberOfSatellitesUsedInFix())); - GPXbw.write(""); + gpxBW.write(""); // and NOT the number of satellites in view!!! + gpxBW.write(String.valueOf(loc.getNumberOfSatellitesUsedInFix())); + gpxBW.write(""); } /* if (getPrefGPXVersion == GPX1_1) { // GPX 1.1 doesn't support speed tags. Let's switch to Garmin extensions :( if (loc.getLocation().hasSpeed()) { - GPXbw.write(""); // Speed (as Garmin extension) - GPXbw.write(formattedSpeed); - GPXbw.write(""); + gpxBW.write(""); // Speed (as Garmin extension) + gpxBW.write(formattedSpeed); + gpxBW.write(""); } } */ - GPXbw.write("" + newLine); + gpxBW.write("" + newLine); } // TXT - if (ExportTXT) { + if (exportTXT) { //type,time,latitude,longitude,altitude (m),geoid_height (m),speed (m/s),sat_used,sat_inview,name,desc - //TXTbw.write("T," + dfdtTXT.format(loc.getLocation().getTime()) + "," + formattedLatitude + "," + formattedLongitude + ","); - TXTbw.write("T," + (((loc.getLocation().getTime() % 1000L) == 0L) ? + //txtBW.write("T," + dfdtTXT.format(loc.getLocation().getTime()) + "," + formattedLatitude + "," + formattedLongitude + ","); + txtBW.write("T," + (((loc.getLocation().getTime() % 1000L) == 0L) ? dfdtTXT_NoMillis.format(loc.getLocation().getTime()) : dfdtTXT.format(loc.getLocation().getTime())) + "," + formattedLatitude + "," + formattedLongitude + ","); if (loc.getLocation().hasAccuracy()) - TXTbw.write(String.format(Locale.US, "%.0f", loc.getLocation().getAccuracy())); - TXTbw.write(","); + txtBW.write(String.format(Locale.US, "%.0f", loc.getLocation().getAccuracy())); + txtBW.write(","); if (loc.getLocation().hasAltitude()) - TXTbw.write(formattedAltitude); - TXTbw.write(","); - if ((loc.getAltitudeEGM96Correction() != NOT_AVAILABLE) && (EGMAltitudeCorrection)) - TXTbw.write(String.format(Locale.US, "%.3f",loc.getAltitudeEGM96Correction())); - TXTbw.write(","); + txtBW.write(formattedAltitude); + txtBW.write(","); + if ((loc.getAltitudeEGM96Correction() != NOT_AVAILABLE) && (egmAltitudeCorrection)) + txtBW.write(String.format(Locale.US, "%.3f",loc.getAltitudeEGM96Correction())); + txtBW.write(","); if (loc.getLocation().hasSpeed()) - TXTbw.write(formattedSpeed); - TXTbw.write(","); + txtBW.write(formattedSpeed); + txtBW.write(","); if (loc.getLocation().hasBearing()) - TXTbw.write(String.format(Locale.US, "%.0f", loc.getLocation().getBearing())); - TXTbw.write(","); + txtBW.write(String.format(Locale.US, "%.0f", loc.getLocation().getBearing())); + txtBW.write(","); if (loc.getNumberOfSatellitesUsedInFix() > 0) - TXTbw.write(String.valueOf(loc.getNumberOfSatellitesUsedInFix())); - TXTbw.write(","); + txtBW.write(String.valueOf(loc.getNumberOfSatellitesUsedInFix())); + txtBW.write(","); if (loc.getNumberOfSatellites() > 0) - TXTbw.write(String.valueOf(loc.getNumberOfSatellites())); - TXTbw.write(","); - if (TXTFirstTrackpointFlag) { // First trackpoint of the track: add the description - TXTbw.write(track.getName() + ",GPS Logger: " + track.getName()); - TXTFirstTrackpointFlag = false; - } else TXTbw.write(","); - TXTbw.write(newLine); + txtBW.write(String.valueOf(loc.getNumberOfSatellites())); + txtBW.write(","); + if (txtFirstTrackpointFlag) { // First trackpoint of the track: add the description + if (track.getDescription().isEmpty()) txtBW.write(track.getName() + ",GPS Logger: " + track.getName()); + else txtBW.write(track.getName() + ",GPS Logger: " + track.getName() + " - " + track.getDescription().replace(",", "_")); + txtFirstTrackpointFlag = false; + } else txtBW.write(","); + txtBW.write(newLine); } exportingTask.setNumberOfPoints_Processed(exportingTask.getNumberOfPoints_Processed() + 1); } exportingTask.setNumberOfPoints_Processed(track.getNumberOfPlacemarks() + track.getNumberOfLocations()); - ArrayGeopoints.clear(); + arrayGeopoints.clear(); - if (ExportKML) { - KMLbw.write(" " + newLine); - KMLbw.write(" " + newLine); - KMLbw.write(" " + newLine + newLine); + if (exportKML) { + kmlBW.write("
" + newLine); + kmlBW.write("
" + newLine); + kmlBW.write("
" + newLine + newLine); } - if (ExportGPX) { - GPXbw.write(" " + newLine); - GPXbw.write("" + newLine + newLine); + if (exportGPX) { + gpxBW.write(" " + newLine); + gpxBW.write("" + newLine + newLine); } } @@ -649,29 +705,29 @@ public void run() { // ------------------------------------------------------------ Writing tails and close Log.w("myApp", "[#] Exporter.java - Writing Tails and close files"); - if (ExportKML) { - KMLbw.write("
" + newLine); - KMLbw.write("
" + newLine + " "); - KMLbw.flush(); - KMLbw.close(); - KMLfw.flush(); - KMLfw.close(); + if (exportKML) { + kmlBW.write("
" + newLine); + kmlBW.write("
" + newLine + " "); + kmlBW.flush(); + kmlBW.close(); + kmlFW.flush(); + kmlFW.close(); } - if (ExportGPX) { - GPXbw.write("" + newLine + " "); - GPXbw.flush(); - GPXbw.close(); - GPXfw.flush(); - GPXfw.close(); + if (exportGPX) { + gpxBW.write("" + newLine + " "); + gpxBW.flush(); + gpxBW.close(); + gpxFW.flush(); + gpxFW.close(); } - if (ExportTXT) { - TXTbw.flush(); - TXTbw.close(); - TXTfw.flush(); - TXTfw.close(); + if (exportTXT) { + txtBW.flush(); + txtBW.close(); + txtFW.flush(); + txtFW.close(); } - Log.w("myApp", "[#] Exporter.java - Track "+ track.getId() +" exported in " + (System.currentTimeMillis() - start_Time) + " ms (" + elements_total + " pts @ " + ((1000L * elements_total) / (System.currentTimeMillis() - start_Time)) + " pts/s)"); + Log.w("myApp", "[#] Exporter.java - Track "+ track.getId() +" exported in " + (System.currentTimeMillis() - startTime) + " ms (" + elements_total + " pts @ " + ((1000L * elements_total) / (System.currentTimeMillis() - startTime)) + " pts/s)"); //EventBus.getDefault().post(new EventBusMSGNormal(EventBusMSG.TRACK_EXPORTED, track.getId())); exportingTask.setStatus(ExportingTask.STATUS_ENDED_SUCCESS); } catch (IOException e) { @@ -686,7 +742,15 @@ public void run() { } } - + /** + * This Thread feeds the arrayGeopoints list with the GeoPoints, by querying + * small blocks of points from the DB and keeping the list as full as possible. + * The thread is started as soon as the Exported is started in order to fill the + * list meanwhile the Exporter is initializing the files and is writing the file headers. + *

+ * The Database reading and the file writing are decoupled into two separate Threads + * in order to optimise the Exporter's performance. + */ private class AsyncGeopointsLoader extends Thread { public AsyncGeopointsLoader() { @@ -695,15 +759,15 @@ public AsyncGeopointsLoader() { public void run() { Thread.currentThread().setPriority(Thread.MIN_PRIORITY); - List lList = new ArrayList<>(GroupOfLocations); + List lList = new ArrayList<>(groupOfLocations); - for (int i = 0; i <= track.getNumberOfLocations(); i += GroupOfLocations) { + for (int i = 0; i <= track.getNumberOfLocations(); i += groupOfLocations) { //Log.w("myApp", "[#] Exporter.java - " + (i + GroupOfLocations)); - lList.addAll(GPSApplication.getInstance().GPSDataBase.getLocationsList(track.getId(), i, i + GroupOfLocations - 1)); + lList.addAll(GPSApplication.getInstance().gpsDataBase.getLocationsList(track.getId(), i, i + groupOfLocations - 1)); if (!lList.isEmpty()) { for (LocationExtended loc : lList) { try { - ArrayGeopoints.put(loc); + arrayGeopoints.put(loc); //Log.w("myApp", "[#] Exporter.java - " + ArrayGeopoints.size()); } catch (InterruptedException e) { Log.w("myApp", "[#] Exporter.java - Interrupted: " + e); diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExportingTask.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExportingTask.java index 6e3e7c05..075463ff 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExportingTask.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExportingTask.java @@ -1,6 +1,9 @@ -/** +/* * ExportingTask - Java Class for Android - * Created by G.Capelli (BasicAirData) on 15/6/2019 + * Created by G.Capelli on 15/6/2019 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +21,11 @@ package eu.basicairdata.graziano.gpslogger; +/** + * The data structure that stores all the information needed to export a Track. + * It stores the properties, the amount of work, + * and the status of the exportation. + */ public class ExportingTask { static final short STATUS_PENDING = 0; // Task not yet started @@ -26,11 +34,10 @@ public class ExportingTask { static final short STATUS_ENDED_FAILED = 3; // Task failed to export private long id = 0; - private long NumberOfPoints_Total = 0; - private long NumberOfPoints_Processed = 0; - private short Status = STATUS_PENDING; - private String Name = ""; - + private long numberOfPoints_Total = 0; + private long numberOfPoints_Processed = 0; + private short status = STATUS_PENDING; + private String name = ""; public long getId() { return id; @@ -41,35 +48,35 @@ public void setId(long id) { } public long getNumberOfPoints_Total() { - return NumberOfPoints_Total; + return numberOfPoints_Total; } public void setNumberOfPoints_Total(long numberOfPoints_Total) { - NumberOfPoints_Total = numberOfPoints_Total; + this.numberOfPoints_Total = numberOfPoints_Total; } public long getNumberOfPoints_Processed() { - return NumberOfPoints_Processed; + return numberOfPoints_Processed; } public void setNumberOfPoints_Processed(long numberOfPoints_Processed) { - NumberOfPoints_Processed = numberOfPoints_Processed; + this.numberOfPoints_Processed = numberOfPoints_Processed; } public short getStatus() { - return Status; + return status; } public void setStatus(short status) { - Status = status; + this.status = status; } public String getName() { - return Name; + return name; } public void setName(String name) { - Name = name; + this.name = name; } } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExternalViewer.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExternalViewer.java new file mode 100644 index 00000000..27b65cb5 --- /dev/null +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExternalViewer.java @@ -0,0 +1,36 @@ +/* + * ExternalViewer - Java Class for Android + * Created by G.Capelli on 23/9/2020 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package eu.basicairdata.graziano.gpslogger; + +import android.graphics.drawable.Drawable; + +/** + * The data structure that describes a Track Viewer. + */ +public class ExternalViewer { + String label = ""; // The name of the app + String packageName = ""; // The full package name + String mimeType = ""; // The mimetype to use with the ACTION_VIEW intent (for example "application/gpx+xml") + String fileType = ""; // "GPX" or "KML" + boolean requiresFileProvider = true; // True if the app requires the FileProvider method to open the track + Drawable icon = null; // The app's icon +} \ No newline at end of file diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/AppDialogList.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExternalViewerAdapter.java similarity index 64% rename from app/src/main/java/eu/basicairdata/graziano/gpslogger/AppDialogList.java rename to app/src/main/java/eu/basicairdata/graziano/gpslogger/ExternalViewerAdapter.java index 7de6a67a..f35599f2 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/AppDialogList.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExternalViewerAdapter.java @@ -1,6 +1,9 @@ -/** - * AppDialogList - Java Class for Android - * Created by G.Capelli (BasicAirData) on 23/9/2020 +/* + * ExternalViewerAdapter - Java Class for Android + * Created by G.Capelli on 23/9/2020 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,14 +34,20 @@ import static eu.basicairdata.graziano.gpslogger.GPSApplication.FILETYPE_KML; import static eu.basicairdata.graziano.gpslogger.GPSApplication.FILETYPE_GPX; - -public class AppDialogList extends BaseAdapter { - - private ArrayList listData; - - private LayoutInflater layoutInflater; - - public AppDialogList(Context context, ArrayList listData) { +/** + * The Adapter for the menu that lists the Track Viewers. + */ +public class ExternalViewerAdapter extends BaseAdapter { + private final ArrayList listData; + private final LayoutInflater layoutInflater; + + /** + * Creates a new ExternalViewerAdapter using the specified ArrayList of ExternalViewer. + * + * @param context the base context + * @param listData the ArrayList of ExternalViewer to be used as adapter data. + */ + public ExternalViewerAdapter(Context context, ArrayList listData) { this.listData = listData; this.layoutInflater = LayoutInflater.from(context); } @@ -58,30 +67,37 @@ public long getItemId(int position) { return position; } + /** + * Returns the View of the given position. + * + * @param position the position of the view + * @param convertView the view + * @param parent the parent ViewGroup + */ public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = layoutInflater.inflate(R.layout.appdialog_list_row, null); holder = new ViewHolder(); - holder.icon = (ImageView) convertView.findViewById(R.id.id_appdialog_row_imageView_icon); - holder.description = (TextView) convertView.findViewById(R.id.id_appdialog_row_textView_description); - holder.format = (TextView) convertView.findViewById(R.id.id_appdialog_row_textView_format); + holder.icon = convertView.findViewById(R.id.id_appdialog_row_imageView_icon); + holder.description = convertView.findViewById(R.id.id_appdialog_row_textView_description); + holder.format = convertView.findViewById(R.id.id_appdialog_row_textView_format); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } - holder.icon.setImageDrawable(listData.get(position).icon); holder.description.setText(listData.get(position).label); holder.format.setText(listData.get(position).fileType.equals(FILETYPE_GPX) ? "GPX" : listData.get(position).fileType.equals(FILETYPE_KML) ? "KML" : ""); - return convertView; } + /** + * The class used into the ExternalViewerAdapter Class. + */ static class ViewHolder { ImageView icon; TextView description; TextView format; } - } \ No newline at end of file diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExternalViewerChecker.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExternalViewerChecker.java index 7a0f36ac..1cd13722 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExternalViewerChecker.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/ExternalViewerChecker.java @@ -1,6 +1,9 @@ -/** +/* * ExternalViewerChecker - Java Class for Android - * Created by G.Capelli (BasicAirData) on 12/7/2020 + * Created by G.Capelli on 12/7/2020 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,70 +26,97 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.net.Uri; + +import androidx.core.content.FileProvider; +import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import static eu.basicairdata.graziano.gpslogger.GPSApplication.FILE_EMPTY_GPX; +import static eu.basicairdata.graziano.gpslogger.GPSApplication.FILE_EMPTY_KML; import static eu.basicairdata.graziano.gpslogger.GPSApplication.FILETYPE_KML; import static eu.basicairdata.graziano.gpslogger.GPSApplication.FILETYPE_GPX; - +/** + * A class that makes and stores the list of External Viewers available on the device. + * The list is made of ExternalViewer items. + */ public class ExternalViewerChecker { private final Context context; + private ArrayList externalViewerList = new ArrayList<>(); // The list of Viewers available + private final CustomComparator comparator = new CustomComparator(); // The comparator for the ExternalViewer list sorting - private ArrayList appInfoList = new ArrayList<>(); - - static private class CustomComparator implements Comparator { + /** + * The comparator used to orders the ExternalViewer items alphabetically by label (app name) + */ + static private class CustomComparator implements Comparator { @Override - public int compare(AppInfo o1, AppInfo o2) { + public int compare(ExternalViewer o1, ExternalViewer o2) { return o1.label.compareTo(o2.label); } } - private class FileType { - FileType (ArrayList _packages, String _mimeType, String _fileType) { - this.packages = _packages; - this.fileType = _fileType; - this.mimeType = _mimeType; + /** + * The class that defines a filtered research. + */ + static private class FileType { + + /** + * Creates a new filtered research. + * + * @param packages the ArrayList of string that contains the packages to research. + * A package, for example, could be "com.vecturagames.android.app.gpxviewer". + * Pass null to search all packages (no package filter). + * @param mimeType The Mime Type of the research (for example "application/gpx+xml"). + * A Mime Type must be specified. + * @param fileType The type of file that will be associated to the apps found by the research. + * The valid values are GPSApplication.FILETYPE_KML (the viewer supports KML) + * and GPSApplication.FILETYPE_GPX (the viewer supports GPX. + */ + FileType (ArrayList packages, String mimeType, String fileType) { + this.packages = packages; + this.fileType = fileType; + this.mimeType = mimeType; } + ArrayList packages; String mimeType; String fileType; } - private ArrayList fileTypeList = new ArrayList<>(); - - public ArrayList getAppInfoList() { - return appInfoList; + public ArrayList getExternalViewersList() { + return externalViewerList; } - private final CustomComparator Comparator = new CustomComparator(); - - public ExternalViewerChecker(Context context) { this.context = context; } - public int size() { - return appInfoList.size(); + return externalViewerList.size(); } - public boolean isEmpty() { - return (appInfoList.isEmpty()); + return (externalViewerList.isEmpty()); } - - public void makeAppInfoList() { + /** + * Creates the ExternalViewer list basing on the research criteria. + * The criteria are defined into this method. + * The list of ExternalViewer are stored here into this ExternalViewerChecker class, + * and can be obtained from outside using the getExternalViewersList() method. + */ + public void makeExternalViewersList() { final PackageManager pm = context.getPackageManager(); - appInfoList = new ArrayList<>(); + externalViewerList = new ArrayList<>(); + ArrayList fileTypeList = new ArrayList<>(); - fileTypeList = new ArrayList<>(); fileTypeList.add(new FileType(null, "application/gpx+xml", FILETYPE_GPX)); // The preferred format first fileTypeList.add(new FileType(null, "application/vnd.google-earth.kml+xml", FILETYPE_KML)); @@ -100,16 +130,24 @@ public void makeAppInfoList() { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); for (FileType ft : fileTypeList) { - intent.setType(ft.mimeType); + //intent.setType(ft.mimeType); + + File file = new File(ft.mimeType.equals(FILETYPE_GPX) ? FILE_EMPTY_GPX : FILE_EMPTY_KML); + Uri uri = FileProvider.getUriForFile(GPSApplication.getInstance(), "eu.basicairdata.graziano.gpslogger.fileprovider", file); + intent.setDataAndType(uri, ft.mimeType); + +// File file = new File(ft.mimeType.equals(FILETYPE_GPX) ? EMPTY_GPX : EMPTY_KML); +// intent.setDataAndType(Uri.fromFile(file), ft.mimeType); + //Log.w("myApp", "[#] ExternalViewerChecker.java - " + ft.mimeType); //Log.w("myApp", "[#] GPSApplication.java - Open with: " + ri.activityInfo.applicationInfo.loadLabel(getContext().getPackageManager())); - List KMLlri = pm.queryIntentActivities(intent, 0); - //Log.w("myApp", "[#] ExternalViewerChecker.java - Found " + KMLlri.size() + " viewers:"); - for (ResolveInfo tmpri : KMLlri) { + List kmlLRI = pm.queryIntentActivities(intent, 0); + //Log.w("myApp", "[#] ExternalViewerChecker.java - Found " + kmlLRI.size() + " viewers:"); + for (ResolveInfo tmpRI : kmlLRI) { boolean isPackageInList = false; if (ft.packages != null) { for (String s : ft.packages) { - if (s.equals(tmpri.activityInfo.applicationInfo.packageName)) { + if (s.equals(tmpRI.activityInfo.applicationInfo.packageName)) { isPackageInList = true; break; } @@ -117,44 +155,48 @@ public void makeAppInfoList() { } else isPackageInList = true; if (isPackageInList) { - AppInfo ainfo = new AppInfo(); - ainfo.packageName = tmpri.activityInfo.applicationInfo.packageName; - ainfo.label = tmpri.activityInfo.applicationInfo.loadLabel(pm).toString(); + ExternalViewer aInfo = new ExternalViewer(); + aInfo.packageName = tmpRI.activityInfo.applicationInfo.packageName; + aInfo.label = tmpRI.activityInfo.applicationInfo.loadLabel(pm).toString(); boolean found = false; - for (AppInfo a : appInfoList) { - if (a.label.equals(ainfo.label) && a.packageName.equals(ainfo.packageName)) { + for (ExternalViewer a : externalViewerList) { + if (a.label.equals(aInfo.label) && a.packageName.equals(aInfo.packageName)) { found = true; - //Log.w("myApp", "[#] ExternalViewerChecker.java - " + tmpri.activityInfo.applicationInfo.packageName); + //Log.w("myApp", "[#] ExternalViewerChecker.java - " + tmpRI.activityInfo.applicationInfo.packageName); break; } } if (!found) { - ainfo.mimeType = ft.mimeType; - ainfo.fileType = ft.fileType; - ainfo.icon = tmpri.activityInfo.applicationInfo.loadIcon(pm); - appInfoList.add(ainfo); - //Log.w("myApp", "[#] ExternalViewerChecker.java - + " + tmpri.activityInfo.applicationInfo.packageName); + aInfo.mimeType = ft.mimeType; + aInfo.fileType = ft.fileType; + aInfo.icon = tmpRI.activityInfo.applicationInfo.loadIcon(pm); + externalViewerList.add(aInfo); + //Log.w("myApp", "[#] ExternalViewerChecker.java - + " + tmpRI.activityInfo.applicationInfo.packageName); } } } } // Sort List by Package Name - Collections.sort(appInfoList, Comparator); + Collections.sort(externalViewerList, comparator); // Apply Exceptions - for (AppInfo a : appInfoList) { + for (ExternalViewer a : externalViewerList) { if (a.packageName.equals("at.xylem.mapin")) { // MAPinr is not opening GPX correctly! a.fileType = FILETYPE_KML; a.mimeType = "application/vnd.google-earth.kml+xml"; } - if (a.packageName.equals("com.google.earth")) { - // Google Earth opens file with fileProvider only - a.requiresFileProvider = true; + if (a.packageName.equals("com.vecturagames.android.app.gpxviewer")) { + // GPX Viewer saves a copy of the file if passed via FileProvider + a.requiresFileProvider = false; } +// if (a.packageName.equals("com.google.earth")) { +// // Google Earth opens file with fileProvider only +// a.requiresFileProvider = true; +// } } } } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentAboutDialog.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentAboutDialog.java index cd755503..8b394f3b 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentAboutDialog.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentAboutDialog.java @@ -1,6 +1,9 @@ -/** +/* * FragmentAboutDialog - Java Class for Android - * Created by G.Capelli (BasicAirData) on 26/7/2016 + * Created by G.Capelli on 26/7/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,33 +34,35 @@ import android.widget.TextView; import android.widget.Toast; +/** + * The About Dialog Fragment + */ public class FragmentAboutDialog extends DialogFragment { - TextView TVVersion; - TextView TVDescription; - //@SuppressLint("InflateParams") @Override public Dialog onCreateDialog(Bundle savedInstanceState) { + TextView tvVersion; + TextView tvDescription; AlertDialog.Builder createAboutAlert = new AlertDialog.Builder(getActivity(), R.style.MyMaterialThemeAbout); LayoutInflater inflater = getActivity().getLayoutInflater(); final View view = inflater.inflate(R.layout.fragment_about_dialog, null); - final GPSApplication app = GPSApplication.getInstance(); + final GPSApplication gpsApp = GPSApplication.getInstance(); - TVVersion = (TextView) view.findViewById(R.id.id_about_textView_Version); + tvVersion = view.findViewById(R.id.id_about_textView_Version); String versionName = BuildConfig.VERSION_NAME; - TVVersion.setText(getString(R.string.about_version) + " " + versionName); + tvVersion.setText(getString(R.string.about_version) + " " + versionName); - TVDescription = (TextView) view.findViewById(R.id.id_about_textView_description); - switch (app.getAppOrigin()) { + tvDescription = view.findViewById(R.id.id_about_textView_description); + switch (gpsApp.getAppOrigin()) { case GPSApplication.APP_ORIGIN_NOT_SPECIFIED: - TVDescription.setText(getString(R.string.about_description)); + tvDescription.setText(getString(R.string.about_description)); break; case GPSApplication.APP_ORIGIN_GOOGLE_PLAY_STORE: - TVDescription.setText(getString(R.string.about_description_googleplaystore)); + tvDescription.setText(getString(R.string.about_description) + "\n\n" + getString(R.string.about_description_googleplaystore)); break; } @@ -66,16 +71,16 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { public void onClick(DialogInterface dialog, int id) {} }); - if (app.getAppOrigin() != GPSApplication.APP_ORIGIN_NOT_SPECIFIED) { + if (gpsApp.getAppOrigin() != GPSApplication.APP_ORIGIN_NOT_SPECIFIED) { createAboutAlert.setView(view).setNegativeButton(R.string.about_rate_this_app, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { - if (app.getAppOrigin() == GPSApplication.APP_ORIGIN_GOOGLE_PLAY_STORE) { + if (gpsApp.getAppOrigin() == GPSApplication.APP_ORIGIN_GOOGLE_PLAY_STORE) { boolean marketfailed = false; try { getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + BuildConfig.APPLICATION_ID))); } catch (Exception e) { - // Unable to start the Google Play app for rating + // Unable to start the Google Play gpsApp for rating marketfailed = true; } if (marketfailed) { @@ -90,7 +95,6 @@ public void onClick(DialogInterface dialog, int id) { } }); } - return createAboutAlert.create(); } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentGPSFix.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentGPSFix.java index 652f5898..e18bec56 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentGPSFix.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentGPSFix.java @@ -1,6 +1,9 @@ -/** +/* * FragmentGPSFix - Java Class for Android - * Created by G.Capelli (BasicAirData) on 10/5/2016 + * Created by G.Capelli on 10/5/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,6 +36,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TableLayout; @@ -52,45 +56,44 @@ import static eu.basicairdata.graziano.gpslogger.GPSApplication.GPS_STABILIZING; import static eu.basicairdata.graziano.gpslogger.GPSApplication.GPS_OK; - +/** + * The Fragment that displays the information of the Fix + * on the first tab (GPS FIX) of the main Activity (GPSActivity). + */ public class FragmentGPSFix extends Fragment { - private PhysicalDataFormatter phdformatter = new PhysicalDataFormatter(); - + private final PhysicalDataFormatter phdformatter = new PhysicalDataFormatter(); + private final GPSApplication gpsApp = GPSApplication.getInstance(); private boolean isAWarningClicked = false; - private FrameLayout FLGPSFix; - - private TextView TVLatitude; - private TextView TVLongitude; - private TextView TVLatitudeUM; - private TextView TVLongitudeUM; - private TextView TVAltitude; - private TextView TVAltitudeUM; - private TextView TVSpeed; - private TextView TVSpeedUM; - private TextView TVBearing; - private TextView TVAccuracy; - private TextView TVAccuracyUM; - private TextView TVGPSFixStatus; - private TextView TVDirectionUM; - private TextView TVTime; - private TextView TVTimeLabel; - private TextView TVSatellites; - - private CardView CVWarningLocationDenied; - private CardView CVWarningGPSDisabled; - private CardView CVWarningBackgroundRestricted; - - private TableLayout TLCoordinates; - private TableLayout TLAltitude; - private TableLayout TLSpeed; - private TableLayout TLBearing; - private TableLayout TLAccuracy; - private TableLayout TLTime; - private TableLayout TLSatellites; - - private LinearLayout LLTimeSatellites; + private FrameLayout flGPSFix; + private TextView tvLatitude; + private TextView tvLongitude; + private TextView tvLatitudeUM; + private TextView tvLongitudeUM; + private TextView tvAltitude; + private TextView tvAltitudeUM; + private TextView tvSpeed; + private TextView tvSpeedUM; + private TextView tvBearing; + private TextView tvAccuracy; + private TextView tvAccuracyUM; + private TextView tvGPSFixStatus; + private TextView tvDirectionUM; + private TextView tvTime; + private TextView tvTimeLabel; + private TextView tvSatellites; + private TableLayout tlCoordinates; + private TableLayout tlAltitude; + private TableLayout tlSpeed; + private TableLayout tlBearing; + private TableLayout tlAccuracy; + private TableLayout tlTime; + private TableLayout tlSatellites; + private CardView cvWarningLocationDenied; + private CardView cvWarningGPSDisabled; + private CardView cvWarningBackgroundRestricted; + private LinearLayout llTimeSatellites; private PhysicalData phdLatitude; private PhysicalData phdLongitude; @@ -100,19 +103,53 @@ public class FragmentGPSFix extends Fragment { private PhysicalData phdAccuracy; private PhysicalData phdTime; - final GPSApplication gpsApplication = GPSApplication.getInstance(); + private LocationExtended location; + private double AltitudeManualCorrection; + private int prefDirections; + private int GPSStatus = GPS_DISABLED; + private boolean EGMAltitudeCorrection; + private boolean isValidAltitude; + private boolean isBackgroundActivityRestricted; + + /** + * The Observer that calculate the new available height when the layout is changed. + * If the height is enough, it set the setSpaceForExtraTilesAvailable flag + * that enable the visualization of the extra tiles: + *

+ */ + ViewTreeObserver.OnGlobalLayoutListener viewTreeObserverOnGLL = new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + flGPSFix.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } else { + flGPSFix.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } + //int width = flGPSFix.getMeasuredWidth(); + //int height = flGPSFix.getMeasuredHeight(); + //Log.w("myApp", "[#] FragmentGPSFix MEASURED: " + width + " x " + height); + int viewHeight = tlSpeed.getMeasuredHeight() + (int)(6*getResources().getDisplayMetrics().density); + int layoutHeight = flGPSFix.getHeight() - (int)(6*getResources().getDisplayMetrics().density); + boolean isTimeAndSatellitesVisible; + if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ + isTimeAndSatellitesVisible = layoutHeight >= 6*viewHeight; + //Log.w("myApp", "[#] FragmentGPSFix MEASURED: " + layoutHeight + " / " + 6*viewHeight + " -> " + isTimeAndSatellitesVisible); + } else { + isTimeAndSatellitesVisible = layoutHeight >= 3.9*viewHeight; + //Log.w("myApp", "[#] FragmentGPSFix MEASURED: " + layoutHeight + " / " + 3.9*viewHeight + " -> " + isTimeAndSatellitesVisible); + } + GPSApplication.getInstance().setSpaceForExtraTilesAvailable(isTimeAndSatellitesVisible); + update(); + } + }; public FragmentGPSFix() { // Required empty public constructor } - @Subscribe (threadMode = ThreadMode.MAIN) - public void onEvent(Short msg) { - if (msg == EventBusMSG.UPDATE_FIX) { - Update(); - } - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -125,43 +162,43 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, View view = inflater.inflate(R.layout.fragment_gpsfix, container, false); // FrameLayouts - FLGPSFix = view.findViewById(R.id.id_fragmentgpsfixFrameLayout); + flGPSFix = view.findViewById(R.id.id_fragmentgpsfixFrameLayout); // TextViews - TVLatitude = view.findViewById(R.id.id_textView_Latitude); - TVLongitude = view.findViewById(R.id.id_textView_Longitude); - TVLatitudeUM = view.findViewById(R.id.id_textView_LatitudeUM); - TVLongitudeUM = view.findViewById(R.id.id_textView_LongitudeUM); - TVAltitude = view.findViewById(R.id.id_textView_Altitude); - TVAltitudeUM = view.findViewById(R.id.id_textView_AltitudeUM); - TVSpeed = view.findViewById(R.id.id_textView_Speed); - TVSpeedUM = view.findViewById(R.id.id_textView_SpeedUM); - TVBearing = view.findViewById(R.id.id_textView_Bearing); - TVAccuracy = view.findViewById(R.id.id_textView_Accuracy); - TVAccuracyUM = view.findViewById(R.id.id_textView_AccuracyUM); - TVGPSFixStatus = view.findViewById(R.id.id_textView_GPSFixStatus); - TVDirectionUM = view.findViewById(R.id.id_textView_BearingUM); - TVTime = view.findViewById(R.id.id_textView_Time); - TVTimeLabel = view.findViewById(R.id.id_textView_TimeLabel); - TVSatellites = view.findViewById(R.id.id_textView_Satellites); - - CVWarningLocationDenied = view.findViewById(R.id.card_view_warning_location_denied); - CVWarningGPSDisabled = view.findViewById(R.id.card_view_warning_enable_location_service); - CVWarningBackgroundRestricted = view.findViewById(R.id.card_view_warning_background_restricted); + tvLatitude = view.findViewById(R.id.id_textView_Latitude); + tvLongitude = view.findViewById(R.id.id_textView_Longitude); + tvLatitudeUM = view.findViewById(R.id.id_textView_LatitudeUM); + tvLongitudeUM = view.findViewById(R.id.id_textView_LongitudeUM); + tvAltitude = view.findViewById(R.id.id_textView_Altitude); + tvAltitudeUM = view.findViewById(R.id.id_textView_AltitudeUM); + tvSpeed = view.findViewById(R.id.id_textView_Speed); + tvSpeedUM = view.findViewById(R.id.id_textView_SpeedUM); + tvBearing = view.findViewById(R.id.id_textView_Bearing); + tvAccuracy = view.findViewById(R.id.id_textView_Accuracy); + tvAccuracyUM = view.findViewById(R.id.id_textView_AccuracyUM); + tvGPSFixStatus = view.findViewById(R.id.id_textView_GPSFixStatus); + tvDirectionUM = view.findViewById(R.id.id_textView_BearingUM); + tvTime = view.findViewById(R.id.id_textView_Time); + tvTimeLabel = view.findViewById(R.id.id_textView_TimeLabel); + tvSatellites = view.findViewById(R.id.id_textView_Satellites); + + cvWarningLocationDenied = view.findViewById(R.id.card_view_warning_location_denied); + cvWarningGPSDisabled = view.findViewById(R.id.card_view_warning_enable_location_service); + cvWarningBackgroundRestricted = view.findViewById(R.id.card_view_warning_background_restricted); // TableLayouts - TLCoordinates = view.findViewById(R.id.id_TableLayout_Coordinates) ; - TLAltitude = view.findViewById(R.id.id_TableLayout_Altitude); - TLSpeed = view.findViewById(R.id.id_TableLayout_Speed); - TLBearing = view.findViewById(R.id.id_TableLayout_Bearing); - TLAccuracy = view.findViewById(R.id.id_TableLayout_Accuracy); - TLTime = view.findViewById(R.id.id_TableLayout_Time); - TLSatellites = view.findViewById(R.id.id_TableLayout_Satellites); + tlCoordinates = view.findViewById(R.id.id_TableLayout_Coordinates) ; + tlAltitude = view.findViewById(R.id.id_TableLayout_Altitude); + tlSpeed = view.findViewById(R.id.id_TableLayout_Speed); + tlBearing = view.findViewById(R.id.id_TableLayout_Bearing); + tlAccuracy = view.findViewById(R.id.id_TableLayout_Accuracy); + tlTime = view.findViewById(R.id.id_TableLayout_Time); + tlSatellites = view.findViewById(R.id.id_TableLayout_Satellites); // LinearLayouts - LLTimeSatellites = view.findViewById(R.id.id_linearLayout_Time_Satellites); + llTimeSatellites = view.findViewById(R.id.id_linearLayout_Time_Satellites); - CVWarningGPSDisabled.setOnClickListener(new View.OnClickListener() { + cvWarningGPSDisabled.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!isAWarningClicked && (GPSStatus == GPS_DISABLED)) { @@ -178,7 +215,7 @@ public void onClick(View v) { } }); - CVWarningBackgroundRestricted.setOnClickListener(new View.OnClickListener() { + cvWarningBackgroundRestricted.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!isAWarningClicked && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)) { @@ -198,13 +235,13 @@ public void onClick(View v) { } }); - CVWarningLocationDenied.setOnClickListener(new View.OnClickListener() { + cvWarningLocationDenied.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!isAWarningClicked) { //isAWarningClicked = true; GPSActivity gpsActivity = (GPSActivity) getActivity(); - gpsActivity.CheckLocationPermission(); + gpsActivity.checkLocationPermission(); } } }); @@ -226,32 +263,47 @@ public void onResume() { } EventBus.getDefault().register(this); - Update(); + + ViewTreeObserver vto = flGPSFix.getViewTreeObserver(); + vto.addOnGlobalLayoutListener(viewTreeObserverOnGLL); + + update(); } @Override public void onPause() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + flGPSFix.getViewTreeObserver().removeGlobalOnLayoutListener(viewTreeObserverOnGLL); + } else { + flGPSFix.getViewTreeObserver().removeOnGlobalLayoutListener(viewTreeObserverOnGLL); + } + EventBus.getDefault().unregister(this); super.onPause(); } + /** + * The EventBus receiver for Short Messages. + */ + @Subscribe (threadMode = ThreadMode.MAIN) + public void onEvent(Short msg) { + if (msg == EventBusMSG.UPDATE_FIX) { + update(); + } + } - private LocationExtended location; - private double AltitudeManualCorrection; - private int prefDirections; - private int GPSStatus = GPS_DISABLED; - private boolean EGMAltitudeCorrection; - private boolean isValidAltitude; - private boolean isBackgroundActivityRestricted; - - public void Update() { - //Log.w("myApp", "[#] FragmentGPSFix.java - Update(Location location)"); - location = gpsApplication.getCurrentLocationExtended(); - AltitudeManualCorrection = gpsApplication.getPrefAltitudeCorrection(); - prefDirections = gpsApplication.getPrefShowDirections(); - GPSStatus = gpsApplication.getGPSStatus(); - EGMAltitudeCorrection = gpsApplication.getPrefEGM96AltitudeCorrection(); - isBackgroundActivityRestricted = gpsApplication.isBackgroundActivityRestricted(); + /** + * Updates the user interface of the fragment. + * It takes care of visibility and value of each tile, messages, and GPS Status widgets. + */ + public void update() { + //Log.w("myApp", "[#] FragmentGPSFix.java - Update"); + location = gpsApp.getCurrentLocationExtended(); + AltitudeManualCorrection = gpsApp.getPrefAltitudeCorrection(); + prefDirections = gpsApp.getPrefShowDirections(); + GPSStatus = gpsApp.getGPSStatus(); + EGMAltitudeCorrection = gpsApp.getPrefEGM96AltitudeCorrection(); + isBackgroundActivityRestricted = gpsApp.isBackgroundActivityRestricted(); if (isAdded()) { if ((location != null) && (GPSStatus == GPS_OK)) { @@ -263,111 +315,90 @@ public void Update() { phdAccuracy = phdformatter.format(location.getAccuracy(), PhysicalDataFormatter.FORMAT_ACCURACY); phdTime = phdformatter.format(location.getTime(), PhysicalDataFormatter.FORMAT_TIME); - TVLatitude.setText(phdLatitude.Value); - TVLongitude.setText(phdLongitude.Value); - TVLatitudeUM.setText(phdLatitude.UM); - TVLongitudeUM.setText(phdLongitude.UM); - TVAltitude.setText(phdAltitude.Value); - TVAltitudeUM.setText(phdAltitude.UM); - TVSpeed.setText(phdSpeed.Value); - TVSpeedUM.setText(phdSpeed.UM); - TVBearing.setText(phdBearing.Value); - TVAccuracy.setText(phdAccuracy.Value); - TVAccuracyUM.setText(phdAccuracy.UM); - TVTime.setText(phdTime.Value); - TVTimeLabel.setText(phdTime.UM.isEmpty() ? getString(R.string.time) : String.format(Locale.getDefault(), "%s (%s)", getString(R.string.time), phdTime.UM)); - TVSatellites.setText(location.getNumberOfSatellitesUsedInFix() != NOT_AVAILABLE ? location.getNumberOfSatellitesUsedInFix() + "/" + location.getNumberOfSatellites() : ""); + tvLatitude.setText(phdLatitude.value); + tvLongitude.setText(phdLongitude.value); + tvLatitudeUM.setText(phdLatitude.um); + tvLongitudeUM.setText(phdLongitude.um); + tvAltitude.setText(phdAltitude.value); + tvAltitudeUM.setText(phdAltitude.um); + tvSpeed.setText(phdSpeed.value); + tvSpeedUM.setText(phdSpeed.um); + tvBearing.setText(phdBearing.value); + tvAccuracy.setText(phdAccuracy.value); + tvAccuracyUM.setText(phdAccuracy.um); + tvTime.setText(phdTime.value); + tvTimeLabel.setText(phdTime.um.isEmpty() ? getString(R.string.time) : String.format(Locale.getDefault(), "%s (%s)", getString(R.string.time), phdTime.um)); + tvSatellites.setText(location.getNumberOfSatellitesUsedInFix() != NOT_AVAILABLE ? location.getNumberOfSatellitesUsedInFix() + "/" + location.getNumberOfSatellites() : ""); // Colorize the Altitude textview depending on the altitude EGM Correction isValidAltitude = EGMAltitudeCorrection && (location.getAltitudeEGM96Correction() != NOT_AVAILABLE); - TVAltitude.setTextColor(isValidAltitude ? getResources().getColor(R.color.textColorPrimary) : getResources().getColor(R.color.textColorSecondary)); - TVAltitudeUM.setTextColor(isValidAltitude ? getResources().getColor(R.color.textColorPrimary) : getResources().getColor(R.color.textColorSecondary)); - - TVGPSFixStatus.setVisibility(View.GONE); - - TVDirectionUM.setVisibility(prefDirections == 0 ? View.GONE : View.VISIBLE); - - TLCoordinates.setVisibility(phdLatitude.Value.equals("") ? View.INVISIBLE : View.VISIBLE); - TLAltitude.setVisibility(phdAltitude.Value.equals("") ? View.INVISIBLE : View.VISIBLE); - TLSpeed.setVisibility(phdSpeed.Value.equals("") ? View.INVISIBLE : View.VISIBLE); - TLBearing.setVisibility(phdBearing.Value.equals("") ? View.INVISIBLE : View.VISIBLE); - TLAccuracy.setVisibility(phdAccuracy.Value.equals("") ? View.INVISIBLE : View.VISIBLE); - TLTime.setVisibility(View.VISIBLE); - TLSatellites.setVisibility(location.getNumberOfSatellitesUsedInFix() == NOT_AVAILABLE ? View.INVISIBLE : View.VISIBLE); - - FLGPSFix.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - FLGPSFix.removeOnLayoutChangeListener(this); - - int ViewHeight = TLTime.getMeasuredHeight() + (int)(6*getResources().getDisplayMetrics().density); - int LayoutHeight = FLGPSFix.getHeight() - (int)(6*getResources().getDisplayMetrics().density); - //Log.w("myApp", "[#]"); - //Log.w("myApp", "[#] -----------------------------------"); - boolean isTimeAndSatellitesVisible; - if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ - isTimeAndSatellitesVisible = LayoutHeight >= 6*ViewHeight; - //Log.w("myApp", "[#] " + LayoutHeight + " / " + 6*ViewHeight + " -> " + isTimeAndSatellitesVisible); - } else { - isTimeAndSatellitesVisible = LayoutHeight >= 4*ViewHeight; - //Log.w("myApp", "[#] " + LayoutHeight + " / " + 4*ViewHeight + " -> " + isTimeAndSatellitesVisible); - } - LLTimeSatellites.setVisibility(isTimeAndSatellitesVisible ? View.VISIBLE : View.GONE); - - //Log.w("myApp", "[#] -----------------------------------"); - //Log.w("myApp", "[#] Available Height = " + LayoutHeight + " px"); - //Log.w("myApp", "[#] Density = " + getResources().getDisplayMetrics().density); - //Log.w("myApp", "[#] Tile Height = " + ViewHeight + " px"); - } - }); - TVGPSFixStatus.setVisibility(View.INVISIBLE); - CVWarningBackgroundRestricted.setVisibility(View.GONE); - CVWarningGPSDisabled.setVisibility(View.GONE); - CVWarningLocationDenied.setVisibility(View.GONE); + tvAltitude.setTextColor(isValidAltitude ? getResources().getColor(R.color.textColorPrimary) : getResources().getColor(R.color.textColorSecondary)); + tvAltitudeUM.setTextColor(isValidAltitude ? getResources().getColor(R.color.textColorPrimary) : getResources().getColor(R.color.textColorSecondary)); + + tvGPSFixStatus.setVisibility(View.GONE); + + tvDirectionUM.setVisibility(prefDirections == 0 ? View.GONE : View.VISIBLE); + + tlCoordinates.setVisibility(phdLatitude.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlAltitude.setVisibility(phdAltitude.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlSpeed.setVisibility(phdSpeed.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlBearing.setVisibility(phdBearing.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlAccuracy.setVisibility(phdAccuracy.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlTime.setVisibility(View.VISIBLE); + tlSatellites.setVisibility(location.getNumberOfSatellitesUsedInFix() == NOT_AVAILABLE ? View.INVISIBLE : View.VISIBLE); + + llTimeSatellites.setVisibility(gpsApp.isSpaceForExtraTilesAvailable() ? View.VISIBLE : View.GONE); + + tvGPSFixStatus.setVisibility(View.INVISIBLE); + cvWarningBackgroundRestricted.setVisibility(View.GONE); + cvWarningGPSDisabled.setVisibility(View.GONE); + cvWarningLocationDenied.setVisibility(View.GONE); } else { - TLCoordinates.setVisibility(View.INVISIBLE); - TLAltitude.setVisibility(View.INVISIBLE); - TLSpeed.setVisibility(View.INVISIBLE); - TLBearing.setVisibility(View.INVISIBLE); - TLAccuracy.setVisibility(View.INVISIBLE); - TLTime.setVisibility(View.INVISIBLE); - TLSatellites.setVisibility(View.INVISIBLE); - - TVGPSFixStatus.setVisibility(View.VISIBLE); + tlCoordinates.setVisibility(View.INVISIBLE); + tlAltitude.setVisibility(View.INVISIBLE); + tlSpeed.setVisibility(View.INVISIBLE); + tlBearing.setVisibility(View.INVISIBLE); + tlAccuracy.setVisibility(View.INVISIBLE); + tlTime.setVisibility(View.INVISIBLE); + tlSatellites.setVisibility(View.INVISIBLE); + + String ssat = ""; + if (((GPSStatus == GPS_SEARCHING) || (GPSStatus == GPS_STABILIZING) || (GPSStatus == GPS_TEMPORARYUNAVAILABLE)) && (gpsApp.getNumberOfSatellitesUsedInFix() != NOT_AVAILABLE)) { + ssat = "\n\n" + gpsApp.getNumberOfSatellitesUsedInFix() + "/" + gpsApp.getNumberOfSatellitesTotal() + " " + getString(R.string.satellites); + } + + tvGPSFixStatus.setVisibility(View.VISIBLE); switch (GPSStatus) { case GPS_DISABLED: - TVGPSFixStatus.setText(R.string.gps_disabled); - CVWarningGPSDisabled.setVisibility(View.VISIBLE); + tvGPSFixStatus.setText(R.string.gps_disabled); + cvWarningGPSDisabled.setVisibility(View.VISIBLE); break; case GPS_OUTOFSERVICE: - TVGPSFixStatus.setText(R.string.gps_out_of_service); - CVWarningGPSDisabled.setVisibility(View.GONE); + tvGPSFixStatus.setText(R.string.gps_out_of_service); + cvWarningGPSDisabled.setVisibility(View.GONE); break; case GPS_TEMPORARYUNAVAILABLE: - //TVGPSFixStatus.setText(R.string.gps_temporary_unavailable); - //break; case GPS_SEARCHING: - TVGPSFixStatus.setText(R.string.gps_searching); - CVWarningGPSDisabled.setVisibility(View.GONE); + tvGPSFixStatus.setText(getString(R.string.gps_searching) + ssat); + cvWarningGPSDisabled.setVisibility(View.GONE); break; case GPS_STABILIZING: - TVGPSFixStatus.setText(R.string.gps_stabilizing); - CVWarningGPSDisabled.setVisibility(View.GONE); + tvGPSFixStatus.setText(getString(R.string.gps_stabilizing) + ssat); + cvWarningGPSDisabled.setVisibility(View.GONE); break; } if (isBackgroundActivityRestricted) { - CVWarningBackgroundRestricted.setVisibility(View.VISIBLE); + cvWarningBackgroundRestricted.setVisibility(View.VISIBLE); } else { - CVWarningBackgroundRestricted.setVisibility(View.GONE); + cvWarningBackgroundRestricted.setVisibility(View.GONE); } if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - TVGPSFixStatus.setText(R.string.gps_not_accessible); - CVWarningLocationDenied.setVisibility(View.VISIBLE); + tvGPSFixStatus.setText(R.string.gps_not_accessible); + cvWarningLocationDenied.setVisibility(View.VISIBLE); } else { - CVWarningLocationDenied.setVisibility(View.GONE); + cvWarningLocationDenied.setVisibility(View.GONE); } } } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentJobProgress.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentJobProgress.java index 8a8474e2..52d47e71 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentJobProgress.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentJobProgress.java @@ -1,6 +1,9 @@ -/** +/* * FragmentJobProgress - Java Class for Android - * Created by G.Capelli (BasicAirData) on 6/1/2019 + * Created by G.Capelli on 6/1/2019 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,22 +32,24 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +/** + * The Fragment that displays the bar that shows the progress of a ExportingTask + * on the third tab (Tracklist) of the main Activity (GPSActivity). + */ public class FragmentJobProgress extends Fragment { ProgressBar progressBar; - public FragmentJobProgress() { // Required empty public constructor } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_job_progress, container, false); - progressBar = (ProgressBar) view.findViewById(R.id.id_jobProgressBar); + progressBar = view.findViewById(R.id.id_jobProgressBar); progressBar.setProgress(GPSApplication.getInstance().getJobProgress()); return view; } @@ -70,6 +75,9 @@ public void onPause() { super.onPause(); } + /** + * The EventBus receiver for Short Messages. + */ @Subscribe (threadMode = ThreadMode.MAIN) public void onEvent(Short msg) { if (msg == EventBusMSG.UPDATE_JOB_PROGRESS) { @@ -77,6 +85,9 @@ public void onEvent(Short msg) { } } + /** + * Updates the status of the Progressbar. + */ public void Update() { if (isAdded()) { progressBar.setProgress((GPSApplication.getInstance().getJobProgress() == 1000) || (GPSApplication.getInstance().getJobsPending() == 0 ) ? 0 : GPSApplication.getInstance().getJobProgress()); diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentPlacemarkDialog.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentPlacemarkDialog.java index 843d4aaa..44f696ad 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentPlacemarkDialog.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentPlacemarkDialog.java @@ -1,6 +1,9 @@ -/** +/* * FragmentPlacemarkDialog - Java Class for Android - * Created by G.Capelli (BasicAirData) on 9/7/2016 + * Created by G.Capelli on 9/7/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +28,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.content.res.ResourcesCompat; import androidx.fragment.app.DialogFragment; import androidx.appcompat.app.AlertDialog; import android.view.LayoutInflater; @@ -36,30 +38,33 @@ import org.greenrobot.eventbus.EventBus; +/** + * The dialog that appears when the user adds a new Annotation (Placemark). + */ public class FragmentPlacemarkDialog extends DialogFragment { - EditText DescEditText; + EditText etDescription; //@SuppressLint("InflateParams") @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder createPlacemarkAlert = new AlertDialog.Builder(getActivity()); - createPlacemarkAlert.setTitle(R.string.dlg_add_placemark); - createPlacemarkAlert.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_add_location_24dp, getActivity().getTheme())); + createPlacemarkAlert.setTitle(R.string.dlg_add_annotation); + //createPlacemarkAlert.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_add_location_24dp, getActivity().getTheme())); LayoutInflater inflater = getActivity().getLayoutInflater(); - final View view = (View) inflater.inflate(R.layout.fragment_placemark_dialog, null); + final View view = inflater.inflate(R.layout.fragment_placemark_dialog, null); - DescEditText = (EditText) view.findViewById(R.id.placemark_description); - DescEditText.postDelayed(new Runnable() + etDescription = view.findViewById(R.id.placemark_description); + etDescription.postDelayed(new Runnable() { public void run() { if (isAdded()) { - DescEditText.requestFocus(); - InputMethodManager mgr = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); - mgr.showSoftInput(DescEditText, InputMethodManager.SHOW_IMPLICIT); + etDescription.requestFocus(); + InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.showSoftInput(etDescription, InputMethodManager.SHOW_IMPLICIT); } } }, 200); @@ -70,15 +75,15 @@ public void run() @Override public void onClick(DialogInterface dialog, int id) { if (isAdded()) { - String PlacemarkDescription = DescEditText.getText().toString(); - final GPSApplication GlobalVariables = (GPSApplication) getActivity().getApplicationContext(); - GlobalVariables.setPlacemarkDescription(PlacemarkDescription.trim()); + String placemarkDescription = etDescription.getText().toString(); + final GPSApplication gpsApp = GPSApplication.getInstance(); + gpsApp.setPlacemarkDescription(placemarkDescription.trim()); EventBus.getDefault().post(EventBusMSG.ADD_PLACEMARK); - //Log.w("myApp", "[#] FragmentPlacemarkDialog.java - posted ADD_PLACEMARK: " + PlacemarkDescription); + //Log.w("myApp", "[#] FragmentPlacemarkDialog.java - posted ADD_PLACEMARK: " + placemarkDescription); } } }) - .setNegativeButton(R.string.dlg_button_cancel, new DialogInterface.OnClickListener() { + .setNeutralButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentRecordingControls.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentRecordingControls.java index 66a5268b..0935a313 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentRecordingControls.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentRecordingControls.java @@ -1,6 +1,9 @@ -/** +/* * FragmentRecordingControls - Java Class for Android - * Created by G.Capelli (BasicAirData) on 20/5/2016 + * Created by G.Capelli on 20/5/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,37 +22,46 @@ package eu.basicairdata.graziano.gpslogger; - import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; import android.os.Bundle; + +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; + +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TableLayout; import android.widget.TextView; +import android.widget.Toast; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -public class FragmentRecordingControls extends Fragment{ +import static eu.basicairdata.graziano.gpslogger.GPSApplication.TOAST_VERTICAL_OFFSET; + +/** + * The Fragment that displays and manages the bottom bar. + */ +public class FragmentRecordingControls extends Fragment { + + private TextView tvGeoPointsNumber; + private TextView tvPlacemarksNumber; + private TextView tvLockButton; + private TextView tvStopButton; + private TextView tvAnnotateButton; + private TextView tvRecordButton; + final GPSApplication gpsApp = GPSApplication.getInstance(); public FragmentRecordingControls() { // Required empty public constructor } - TableLayout tableLayoutGeoPoints; - TableLayout tableLayoutPlacemarks; - - private TextView TVGeoPoints; - private TextView TVPlacemarks; - private TextView TVGeoPointsLabel; - private TextView TVPlacemarksLabel; - - final GPSApplication gpsApplication = GPSApplication.getInstance(); - - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -60,27 +72,36 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_recording_controls, container, false); - tableLayoutGeoPoints = (TableLayout) view.findViewById(R.id.id_TableLayout_GeoPoints); - tableLayoutGeoPoints.setOnClickListener(new View.OnClickListener() { + tvLockButton = view.findViewById(R.id.id_lock); + tvLockButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - ontoggleRecordGeoPoint(v); + onToggleLock(); } }); - - tableLayoutPlacemarks = (TableLayout) view.findViewById(R.id.id_TableLayout_Placemarks); - tableLayoutPlacemarks.setOnClickListener(new View.OnClickListener() { + tvStopButton = view.findViewById(R.id.id_stop); + tvStopButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - onPlacemarkRequest(v); + onRequestStop(); } }); - - TVGeoPoints = (TextView) view.findViewById(R.id.id_textView_GeoPoints); - TVPlacemarks = (TextView) view.findViewById(R.id.id_textView_Placemarks); - TVGeoPointsLabel = (TextView) view.findViewById(R.id.id_textView_GeoPointsLabel); - TVPlacemarksLabel = (TextView) view.findViewById(R.id.id_textView_PlacemarksLabel); - + tvAnnotateButton = view.findViewById(R.id.id_annotate); + tvAnnotateButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onRequestAnnotation(); + } + }); + tvRecordButton = view.findViewById(R.id.id_record); + tvRecordButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onToggleRecord(); + } + }); + tvGeoPointsNumber = view.findViewById(R.id.id_textView_GeoPoints); + tvPlacemarksNumber = view.findViewById(R.id.id_textView_Placemarks); return view; } @@ -94,7 +115,6 @@ public void onResume() { //Log.w("myApp", "[#] FragmentRecordingControls - EventBus: FragmentRecordingControls already registered"); EventBus.getDefault().unregister(this); } - EventBus.getDefault().register(this); Update(); } @@ -105,51 +125,222 @@ public void onPause() { super.onPause(); } - public void ontoggleRecordGeoPoint(View view) { + /** + * The EventBus receiver for Short Messages. + */ + @Subscribe (threadMode = ThreadMode.MAIN) + public void onEvent(Short msg) { + if (msg == EventBusMSG.UPDATE_TRACK) { + Update(); + } + } + + /** + * Toggles the status of the recording, by managing the button behaviour and + * the status of the recording process. + * It also displays some toasts to inform the user about some conditions. + */ + public void onToggleRecord() { if (isAdded()) { - final Boolean grs = gpsApplication.getRecording(); - boolean newRecordingState = !grs; - gpsApplication.setRecording(newRecordingState); - EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); - tableLayoutGeoPoints.setBackgroundColor(newRecordingState ? getResources().getColor(R.color.colorPrimary) : Color.TRANSPARENT); - TVGeoPoints.setTextColor(getResources().getColor(newRecordingState ? R.color.textColorRecControlPrimary_Active : R.color.textColorRecControlPrimary)); - TVGeoPointsLabel.setTextColor(getResources().getColor(newRecordingState ? R.color.textColorRecControlSecondary_Active : R.color.textColorRecControlSecondary)); + if (!gpsApp.isBottomBarLocked()) { + if (!gpsApp.isStopButtonFlag()) { + gpsApp.setRecording(!gpsApp.isRecording()); + if (!gpsApp.isFirstFixFound() && (gpsApp.isRecording())) { + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.toast_recording_when_gps_found, Toast.LENGTH_LONG); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + } + Update(); + } + } else { + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.toast_bottom_bar_locked, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + } } } - public void onPlacemarkRequest(View view) { + /** + * Toggles the status of the placemark request, by managing the button behaviour and + * the status of the request. + * It also displays some toasts to inform the user about some conditions. + */ + public void onRequestAnnotation() { if (isAdded()) { - final Boolean pr = gpsApplication.getPlacemarkRequest(); - boolean newPlacemarkRequestState = !pr; - gpsApplication.setPlacemarkRequest(newPlacemarkRequestState); - tableLayoutPlacemarks.setBackgroundColor(newPlacemarkRequestState ? getResources().getColor(R.color.colorPrimary) : Color.TRANSPARENT); - TVPlacemarks.setTextColor(getResources().getColor(newPlacemarkRequestState ? R.color.textColorRecControlPrimary_Active : R.color.textColorRecControlPrimary)); - TVPlacemarksLabel.setTextColor(getResources().getColor(newPlacemarkRequestState ? R.color.textColorRecControlSecondary_Active : R.color.textColorRecControlSecondary)); + if (!gpsApp.isBottomBarLocked()) { + if (!gpsApp.isStopButtonFlag()) { + gpsApp.setPlacemarkRequested(!gpsApp.isPlacemarkRequested()); + if (!gpsApp.isFirstFixFound() && (gpsApp.isPlacemarkRequested())) { + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.toast_annotate_when_gps_found, Toast.LENGTH_LONG); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + } + Update(); + } + } else { + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.toast_bottom_bar_locked, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + } + } + } + /** + * Manages the Stop button behaviour. + * It also displays some toasts to inform the user about some conditions. + */ + public void onRequestStop() { + if (isAdded()) { + if (!gpsApp.isBottomBarLocked()) { + if (!gpsApp.isStopButtonFlag()) { + gpsApp.setStopButtonFlag(true, gpsApp.getCurrentTrack().getNumberOfLocations() + gpsApp.getCurrentTrack().getNumberOfPlacemarks() > 0 ? 1000 : 300); + gpsApp.setRecording(false); + gpsApp.setPlacemarkRequested(false); + Update(); + if (gpsApp.getCurrentTrack().getNumberOfLocations() + gpsApp.getCurrentTrack().getNumberOfPlacemarks() > 0) { + FragmentManager fm = getActivity().getSupportFragmentManager(); + FragmentTrackPropertiesDialog tpDialog = new FragmentTrackPropertiesDialog(); + gpsApp.setTrackToEdit(gpsApp.getCurrentTrack()); + tpDialog.setTitleResource(R.string.finalize_track); + tpDialog.setFinalizeTrackWithOk(true); + tpDialog.show(fm, ""); + } else { + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.toast_nothing_to_save, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + } + } + } else { + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.toast_bottom_bar_locked, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + } } } - @Subscribe (threadMode = ThreadMode.MAIN) - public void onEvent(Short msg) { - if (msg == EventBusMSG.UPDATE_TRACK) { + /** + * Manages the Lock button behaviour. + */ + public void onToggleLock() { + if (isAdded()) { + gpsApp.setBottomBarLocked(!gpsApp.isBottomBarLocked()); + if (gpsApp.isBottomBarLocked()) { + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.toast_bottom_bar_locked, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + } Update(); } } + /** + * Sets the color of a drawable. + * + * @param drawable The Drawable + * @param color The new Color to set + */ + private void setTextViewDrawableColor(Drawable drawable, int color) { + if (drawable != null) { + drawable.clearColorFilter(); + drawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); + } + } + +// private void setButtonToClickedState(@NonNull TextView button, int imageId, int stringId) { +// ColorDrawable[] colorDrawables = {new ColorDrawable(getResources().getColor(R.color.colorPrimaryLight)), +// new ColorDrawable(getResources().getColor(R.color.colorPrimary))}; +// TransitionDrawable transitionDrawable = new TransitionDrawable(colorDrawables); +// +// button.setBackgroundDrawable(transitionDrawable); +// if (imageId != 0) button.setCompoundDrawablesWithIntrinsicBounds(0, imageId, 0, 0); +// button.setTextColor(getResources().getColor(R.color.textColorRecControlSecondary_Active)); +// if (stringId != 0) button.setText(getString(stringId)); +// setTextViewDrawableColor(button.getCompoundDrawables()[1], getResources().getColor(R.color.textColorRecControlPrimary_Active)); +// transitionDrawable.startTransition(500); +// } + + /** + * Sets the appearance of a button (TextView + upper compound Drawable) as "Clicked", + * by setting the specified Drawable and Text and applying the right colours. + * + * @param button The TextView button + * @param imageId The resource of the drawable + * @param stringId The resource of the string + */ + private void setButtonToClickedState(@NonNull TextView button, int imageId, int stringId) { + button.setBackgroundColor(getResources().getColor(R.color.colorPrimary)); + if (imageId != 0) button.setCompoundDrawablesWithIntrinsicBounds(0, imageId, 0, 0); + button.setTextColor(getResources().getColor(R.color.textColorRecControlSecondary_Active)); + if (stringId != 0) button.setText(getString(stringId)); + setTextViewDrawableColor(button.getCompoundDrawables()[1], getResources().getColor(R.color.textColorRecControlPrimary_Active)); + } + + /** + * Sets the appearance of a button (TextView + upper compound Drawable) as "Normal", + * by setting the specified Drawable and Text and applying the right colours. + * + * @param button The TextView button + * @param imageId The resource of the drawable + * @param stringId The resource of the string + */ + private void setButtonToNormalState(@NonNull TextView button, int imageId, int stringId) { + button.setBackgroundColor(Color.TRANSPARENT); + if (imageId != 0) button.setCompoundDrawablesWithIntrinsicBounds(0, imageId, 0, 0); + button.setTextColor(getResources().getColor(R.color.textColorRecControlSecondary)); + if (stringId != 0) button.setText(getString(stringId)); + setTextViewDrawableColor(button.getCompoundDrawables()[1], getResources().getColor(R.color.textColorRecControlPrimary)); + } + + /** + * Sets the appearance of a button (TextView + upper compound Drawable) as "Disabled" + * by setting the specified Drawable and Text and applying the right colours. + * + * @param button The TextView button + * @param imageId The resource of the drawable + * @param stringId The resource of the string + */ + private void setButtonToDisabledState(@NonNull TextView button, int imageId, int stringId) { + button.setBackgroundColor(Color.TRANSPARENT); + if (imageId != 0) button.setCompoundDrawablesWithIntrinsicBounds(0, imageId, 0, 0); + button.setTextColor(getResources().getColor(R.color.textColorRecControlDisabled)); + if (stringId != 0) button.setText(getString(stringId)); + setTextViewDrawableColor(button.getCompoundDrawables()[1], getResources().getColor(R.color.textColorRecControlDisabled)); + } + + /** + * Updates the user interface of the fragment. + * It takes care of the state of each button. + */ public void Update() { if (isAdded()) { - final Track track = gpsApplication.getCurrentTrack(); - final Boolean grs = gpsApplication.getRecording(); - final Boolean pr = gpsApplication.getPlacemarkRequest(); + final Track track = gpsApp.getCurrentTrack(); + final boolean isRec = gpsApp.isRecording(); + final boolean isAnnot = gpsApp.isPlacemarkRequested(); + final boolean isLck = gpsApp.isBottomBarLocked(); if (track != null) { - if (TVGeoPoints != null) TVGeoPoints.setText(String.valueOf(track.getNumberOfLocations())); - if (TVPlacemarks != null) TVPlacemarks.setText(String.valueOf(track.getNumberOfPlacemarks())); - if (tableLayoutGeoPoints != null) tableLayoutGeoPoints.setBackgroundColor(grs ? getResources().getColor(R.color.colorPrimary) : Color.TRANSPARENT); - if (tableLayoutPlacemarks != null) tableLayoutPlacemarks.setBackgroundColor(pr ? getResources().getColor(R.color.colorPrimary) : Color.TRANSPARENT); - if (TVPlacemarks != null) TVPlacemarks.setTextColor(getResources().getColor(pr ? R.color.textColorRecControlPrimary_Active : R.color.textColorRecControlPrimary)); - if (TVPlacemarksLabel != null) TVPlacemarksLabel.setTextColor(getResources().getColor(pr ? R.color.textColorRecControlSecondary_Active : R.color.textColorRecControlSecondary)); - if (TVGeoPoints != null) TVGeoPoints.setTextColor(getResources().getColor(grs ? R.color.textColorRecControlPrimary_Active : R.color.textColorRecControlPrimary)); - if (TVGeoPointsLabel != null) TVGeoPointsLabel.setTextColor(getResources().getColor(grs ? R.color.textColorRecControlSecondary_Active : R.color.textColorRecControlSecondary)); + if (tvGeoPointsNumber != null) tvGeoPointsNumber.setText(track.getNumberOfLocations() == 0 ? "" : String.valueOf(track.getNumberOfLocations())); + if (tvPlacemarksNumber != null) tvPlacemarksNumber.setText(String.valueOf(track.getNumberOfPlacemarks() == 0 ? "" : track.getNumberOfPlacemarks())); + if (tvRecordButton != null) { + if (isRec) setButtonToClickedState(tvRecordButton, R.drawable.ic_pause_24, R.string.pause); + else setButtonToNormalState(tvRecordButton, R.drawable.ic_record_24, R.string.record); + } + if (tvAnnotateButton != null) { + if (isAnnot) setButtonToClickedState(tvAnnotateButton, 0, 0); + else setButtonToNormalState(tvAnnotateButton, 0, 0); + } + if (tvLockButton != null) { + if (isLck) setButtonToClickedState(tvLockButton, R.drawable.ic_unlock_24, R.string.unlock); + else setButtonToNormalState(tvLockButton, R.drawable.ic_lock_24, R.string.lock); + } + if (tvStopButton != null) { + tvStopButton.setClickable(isRec || isAnnot || (track.getNumberOfLocations() + track.getNumberOfPlacemarks() > 0)); + if (isRec || isAnnot || (track.getNumberOfLocations() + track.getNumberOfPlacemarks() > 0) || gpsApp.isStopButtonFlag()) { + if (gpsApp.isStopButtonFlag()) setButtonToClickedState(tvStopButton, 0, 0); + else setButtonToNormalState(tvStopButton, 0, 0); + } else { + setButtonToDisabledState(tvStopButton, 0, 0); + } + } } } } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentSettings.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentSettings.java index d9e21070..cedac3dc 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentSettings.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentSettings.java @@ -1,6 +1,9 @@ -/** +/* * FragmentSettings - Java Class for Android - * Created by G.Capelli (BasicAirData) on 23/7/2016 + * Created by G.Capelli on 23/7/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,7 +30,6 @@ import android.graphics.drawable.ColorDrawable; import android.os.AsyncTask; import android.os.Bundle; -import android.os.Environment; import androidx.appcompat.app.AppCompatDelegate; import androidx.core.content.res.ResourcesCompat; import androidx.preference.EditTextPreference; @@ -55,23 +57,20 @@ import java.net.URL; import java.util.ArrayList; - import static eu.basicairdata.graziano.gpslogger.GPSApplication.FILETYPE_GPX; - +/** + * The Fragment that manages the Settings on the SettingsActivity + */ public class FragmentSettings extends PreferenceFragmentCompat { private static final float M_TO_FT = 3.280839895f; - SharedPreferences.OnSharedPreferenceChangeListener prefListener; - private SharedPreferences prefs; public double altcor; // manual offset - public double altcorm; // Manual offset in m - - private ProgressDialog mProgressDialog; - public boolean Downloaded = false; - + public double altcorm; // Manual offset in m + private ProgressDialog progressDialog; + public boolean isDownloaded = false; @Override public void onCreate(final Bundle savedInstanceState) { @@ -79,39 +78,34 @@ public void onCreate(final Bundle savedInstanceState) { addPreferencesFromResource(R.xml.app_preferences); - File tsd = new File(Environment.getExternalStorageDirectory() + "/GPSLogger"); - boolean isGPSLoggerFolder = true; - if (!tsd.exists()) { - isGPSLoggerFolder = tsd.mkdir(); - } - tsd = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); - if (!tsd.exists()) { - isGPSLoggerFolder = tsd.mkdir(); - } - Log.w("myApp", "[#] FragmentSettings.java - " + (isGPSLoggerFolder ? "Folder /GPSLogger/AppData OK" : "Unable to create folder /GPSLogger/AppData")); + File tsd = new File(GPSApplication.DIRECTORY_EXPORT); + if (!tsd.exists()) tsd.mkdir(); + tsd = new File(GPSApplication.DIRECTORY_TEMP); + if (!tsd.exists()) tsd.mkdir(); + //Log.w("myApp", "[#] FragmentSettings.java - " + (isGPSLoggerFolder ? "Folder /GPSLogger/AppData OK" : "Unable to create folder /GPSLogger/AppData")); prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - // Chech if EGM96 file is downloaded and complete; + // Check if EGM96 file is downloaded and the size of the file is correct; File sd = new File(getActivity().getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); - File sd_old = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/WW15MGH.DAC"); + File sd_old = new File(GPSApplication.DIRECTORY_TEMP + "/WW15MGH.DAC"); if ((sd.exists() && (sd.length() == 2076480)) || (sd_old.exists() && (sd_old.length() == 2076480))) { - Downloaded = true; + isDownloaded = true; } else { SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor1 = settings.edit(); editor1.putBoolean("prefEGM96AltitudeCorrection", false); editor1.commit(); - SwitchPreferenceCompat EGM96 = (SwitchPreferenceCompat) super.findPreference("prefEGM96AltitudeCorrection"); - EGM96.setChecked(false); + SwitchPreferenceCompat egm96 = super.findPreference("prefEGM96AltitudeCorrection"); + egm96.setChecked(false); } // Instantiate Progress dialog - mProgressDialog = new ProgressDialog(getActivity()); - mProgressDialog.setIndeterminate(true); - mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); - mProgressDialog.setCancelable(true); - mProgressDialog.setMessage(getString(R.string.pref_EGM96AltitudeCorrection_download_progress)); + progressDialog = new ProgressDialog(getActivity()); + progressDialog.setIndeterminate(true); + progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + progressDialog.setCancelable(true); + progressDialog.setMessage(getString(R.string.pref_EGM96AltitudeCorrection_download_progress)); prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { @@ -122,31 +116,27 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin SharedPreferences.Editor editor = prefs.edit(); editor.putString("prefAltitudeCorrectionRaw", String.valueOf(altcor)); editor.commit(); - EditTextPreference pAltitudeCorrection = (EditTextPreference) findPreference("prefAltitudeCorrectionRaw"); - pAltitudeCorrection.setText(prefs.getString("prefAltitudeCorrectionRaw", "0")); + EditTextPreference etpAltitudeCorrection = findPreference("prefAltitudeCorrectionRaw"); + etpAltitudeCorrection.setText(prefs.getString("prefAltitudeCorrectionRaw", "0")); } - if (key.equals("prefAltitudeCorrectionRaw")) { try { - double d = Double.parseDouble(sharedPreferences.getString("prefAltitudeCorrectionRaw", "0")); - altcor = d; + altcor = Double.parseDouble(sharedPreferences.getString("prefAltitudeCorrectionRaw", "0")); } catch(NumberFormatException nfe) { altcor = 0; - EditTextPreference Alt = (EditTextPreference) findPreference("prefAltitudeCorrectionRaw"); - Alt.setText("0"); + EditTextPreference etpAltitudeCorrection = findPreference("prefAltitudeCorrectionRaw"); + etpAltitudeCorrection.setText("0"); } - altcorm = prefs.getString("prefUM", "0").equals("0") ? altcor : altcor / M_TO_FT; SharedPreferences.Editor editor = prefs.edit(); editor.putString("prefAltitudeCorrection", String.valueOf(altcorm)); editor.commit(); } - if (key.equals("prefEGM96AltitudeCorrection")) { if (sharedPreferences.getBoolean(key, false)) { - if (!Downloaded) { + if (!isDownloaded) { // execute this when the downloader must be fired final DownloadTask downloadTask = new DownloadTask(getActivity()); // Original Link not available anymore @@ -155,7 +145,7 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin // The connection is not secured with HTTPS for now, we chosen to use it anyway. downloadTask.execute("http://download.osgeo.org/proj/vdatum/egm96_15/outdated/WW15MGH.DAC"); - mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { downloadTask.cancel(true); @@ -166,7 +156,6 @@ public void onCancel(DialogInterface dialog) { } } } - if (key.equals("prefColorTheme")) { SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor1 = settings.edit(); @@ -177,7 +166,6 @@ public void onCancel(DialogInterface dialog) { AppCompatDelegate.setDefaultNightMode(Integer.valueOf(PreferenceManager.getDefaultSharedPreferences(getContext()).getString("prefColorTheme", "2"))); //getActivity().recreate(); } - SetupPreferences(); } }; @@ -189,10 +177,9 @@ public void onResume() { // Remove dividers between preferences setDivider(new ColorDrawable(Color.TRANSPARENT)); setDividerHeight(0); - prefs.registerOnSharedPreferenceChangeListener(prefListener); //Log.w("myApp", "[#] FragmentSettings.java - onResume"); - GPSApplication.getInstance().getExternalViewerChecker().makeAppInfoList(); + GPSApplication.getInstance().getExternalViewerChecker().makeExternalViewersList(); SetupPreferences(); } @@ -204,33 +191,30 @@ public void onPause() { super.onPause(); } - @Override public void onCreatePreferences(Bundle bundle, String s) { Log.w("myApp", "[#] FragmentSettings.java - onCreatePreferences"); } public void SetupPreferences() { - - ListPreference pUM = (ListPreference) findPreference("prefUM"); - ListPreference pUMSpeed = (ListPreference) findPreference("prefUMSpeed"); - ListPreference pGPSDistance = (ListPreference) findPreference("prefGPSdistance"); - ListPreference pGPSUpdateFrequency = (ListPreference) findPreference("prefGPSupdatefrequency"); - ListPreference pKMLAltitudeMode = (ListPreference) findPreference("prefKMLAltitudeMode"); - ListPreference pGPXVersion = (ListPreference) findPreference("prefGPXVersion"); - ListPreference pShowTrackStatsType = (ListPreference) findPreference("prefShowTrackStatsType"); - ListPreference pShowDirections = (ListPreference) findPreference("prefShowDirections"); - ListPreference pColorTheme = (ListPreference) findPreference("prefColorTheme"); - EditTextPreference pAltitudeCorrection = (EditTextPreference) findPreference("prefAltitudeCorrectionRaw"); - Preference pTracksViewer = (Preference) findPreference("prefTracksViewer"); + ListPreference pUM = findPreference("prefUM"); + ListPreference pUMSpeed = findPreference("prefUMSpeed"); + ListPreference pGPSDistance = findPreference("prefGPSdistance"); + ListPreference pGPSUpdateFrequency = findPreference("prefGPSupdatefrequency"); + ListPreference pKMLAltitudeMode = findPreference("prefKMLAltitudeMode"); + ListPreference pGPXVersion = findPreference("prefGPXVersion"); + ListPreference pShowTrackStatsType = findPreference("prefShowTrackStatsType"); + ListPreference pShowDirections = findPreference("prefShowDirections"); + ListPreference pColorTheme = findPreference("prefColorTheme"); + EditTextPreference pAltitudeCorrection = findPreference("prefAltitudeCorrectionRaw"); + Preference pTracksViewer = findPreference("prefTracksViewer"); // Keep Screen On Flag if (prefs.getBoolean("prefKeepScreenOn", true)) getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); else getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // Track Viewer - - final ArrayList ail = new ArrayList<>(GPSApplication.getInstance().getExternalViewerChecker().getAppInfoList()); + final ArrayList evList = new ArrayList<>(GPSApplication.getInstance().getExternalViewerChecker().getExternalViewersList()); switch (GPSApplication.getInstance().getExternalViewerChecker().size()) { case 0: pTracksViewer.setEnabled(false); // No viewers installed @@ -254,17 +238,17 @@ public boolean onPreferenceClick(Preference preference) { View view = getLayoutInflater().inflate(R.layout.appdialog_list, null); ListView lv = (ListView) view.findViewById(R.id.id_appdialog_list); - final ArrayList aild = new ArrayList<>(); + final ArrayList aild = new ArrayList<>(); // Add "Select every Time" menu item - AppInfo askai = new AppInfo(); + ExternalViewer askai = new ExternalViewer(); askai.label = getString(R.string.pref_track_viewer_select_every_time); askai.icon = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_visibility_24dp, getActivity().getTheme()); aild.add(askai); - aild.addAll(ail); + aild.addAll(evList); - AppDialogList clad = new AppDialogList(getActivity(), aild); + ExternalViewerAdapter clad = new ExternalViewerAdapter(getActivity(), aild); lv.setAdapter(clad); lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -287,24 +271,22 @@ public void onItemClick(AdapterView parent, View view, int position, long id) }); } // ------------ - - if (ail.isEmpty()) + if (evList.isEmpty()) pTracksViewer.setSummary(R.string.pref_track_viewer_not_installed); // no Viewers installed - else if (ail.size() == 1) - pTracksViewer.setSummary(ail.get(0).label + (ail.get(0).fileType.equals(FILETYPE_GPX) ? " (GPX)" : " (KML)")); // 1 Viewer installed + else if (evList.size() == 1) + pTracksViewer.setSummary(evList.get(0).label + (evList.get(0).fileType.equals(FILETYPE_GPX) ? " (GPX)" : " (KML)")); // 1 Viewer installed else { pTracksViewer.setSummary(R.string.pref_track_viewer_select_every_time); // ask every time String pn = prefs.getString("prefTracksViewer", ""); Log.w("myApp", "[#] FragmentSettings.java - prefTracksViewer = " + pn); - for (AppInfo ai : ail) { - if (ai.packageName.equals(pn)) { - //Log.w("myApp", "[#] FragmentSettings.java - Found " + ai.Label); - pTracksViewer.setSummary(ai.label + (ai.fileType.equals(FILETYPE_GPX) ? " (GPX)" : " (KML)")); // Default Viewer available! + for (ExternalViewer ev : evList) { + if (ev.packageName.equals(pn)) { + //Log.w("myApp", "[#] FragmentSettings.java - Found " + ev.Label); + pTracksViewer.setSummary(ev.label + (ev.fileType.equals(FILETYPE_GPX) ? " (GPX)" : " (KML)")); // Default Viewer available! } } } - altcorm = Double.valueOf(prefs.getString("prefAltitudeCorrection", "0")); altcor = prefs.getString("prefUM", "0").equals("0") ? altcorm : altcorm * M_TO_FT; @@ -341,7 +323,9 @@ else if (ail.size() == 1) //pViewTracksWith.setSummary(pViewTracksWith.getEntry()); } - + /** + * Sets the PrefEGM96 to false + */ public void PrefEGM96SetToFalse() { SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor1 = settings.edit(); @@ -351,6 +335,9 @@ public void PrefEGM96SetToFalse() { EGM96.setChecked(false); } + /** + * Sets the PrefEGM96 to true + */ public void PrefEGM96SetToTrue() { SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor1 = settings.edit(); @@ -360,13 +347,17 @@ public void PrefEGM96SetToTrue() { EGM96.setChecked(true); } + // ------------------------------------------------------------- Download of the EGM96 grid file - // ----------------------------------------------------------------.----- EGM96 - Download file - - - // usually, subclasses of AsyncTask are declared inside the activity class. - // that way, you can easily modify the UI thread from here + /** + * The Class that manages the download of the EGM96 grid file. + * The WW15MGH.DAC file is downloaded into the getFilesDir() folder. + * It displays and keeps updated a progress dialog + * that shows the progress of the download + */ private class DownloadTask extends AsyncTask { + // usually, subclasses of AsyncTask are declared inside the activity class. + // that way, you can easily modify the UI thread from here private Context context; //private PowerManager.WakeLock mWakeLock; @@ -441,61 +432,32 @@ protected void onPreExecute() { //mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, // getClass().getName()); //mWakeLock.acquire(); - mProgressDialog.show(); + progressDialog.show(); } @Override protected void onProgressUpdate(Integer... progress) { super.onProgressUpdate(progress); // if we get here, length is known, now set indeterminate to false - mProgressDialog.setIndeterminate(false); - mProgressDialog.setMax(2028); - mProgressDialog.setProgress(progress[0]); + progressDialog.setIndeterminate(false); + progressDialog.setMax(2028); + progressDialog.setProgress(progress[0]); } @Override protected void onPostExecute(String result) { if (getActivity() != null) { //mWakeLock.release(); - mProgressDialog.dismiss(); + progressDialog.dismiss(); if (result != null) Toast.makeText(context, getString(R.string.toast_download_error) + ": " + result, Toast.LENGTH_LONG).show(); else { File sd = new File(getActivity().getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); - File sd_old = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/WW15MGH.DAC"); + File sd_old = new File(GPSApplication.DIRECTORY_TEMP + "/WW15MGH.DAC"); if ((sd.exists() && (sd.length() == 2076480)) || (sd_old.exists() && (sd_old.length() == 2076480))) { - Downloaded = true; + isDownloaded = true; Toast.makeText(context, getString(R.string.toast_download_completed), Toast.LENGTH_SHORT).show(); PrefEGM96SetToTrue(); - - // Ask to switch to Absolute Altitude Mode if not already active. - /* - ListPreference pKMLAltitudeMode = (ListPreference) findPreference("prefKMLAltitudeMode"); - if (!(pKMLAltitudeMode.getValue().equals("0"))) { - AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(getContext(), R.style.StyledDialog)); - builder.setMessage(getResources().getString(R.string.pref_message_switch_to_absolute_altitude_mode)); - builder.setIcon(android.R.drawable.ic_menu_info_details); - builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext()); - SharedPreferences.Editor editor1 = settings.edit(); - editor1.putString("prefKMLAltitudeMode", "0"); - editor1.commit(); - ListPreference pKMLAltitudeMode = (ListPreference) findPreference("prefKMLAltitudeMode"); - pKMLAltitudeMode.setValue("0"); - pKMLAltitudeMode.setSummary(R.string.pref_KML_altitude_mode_absolute); - } - }); - builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.dismiss(); - } - }); - AlertDialog dialog = builder.create(); - dialog.show(); - } - */ - } else { Toast.makeText(context, getString(R.string.toast_download_failed), Toast.LENGTH_SHORT).show(); } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTrack.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTrack.java index cb64e6ba..5bdc03ac 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTrack.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTrack.java @@ -1,6 +1,9 @@ -/** +/* * FragmentTrack - Java Class for Android - * Created by G.Capelli (BasicAirData) on 4/6/2016 + * Created by G.Capelli on 4/6/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,11 +21,17 @@ package eu.basicairdata.graziano.gpslogger; +import android.content.res.Configuration; +import android.os.Build; import android.os.Bundle; import androidx.fragment.app.Fragment; + import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.TableLayout; import android.widget.TextView; @@ -30,32 +39,44 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +/** + * The Fragment that displays the information of the current Track + * on the second tab (Track) of the main Activity (GPSActivity). + */ public class FragmentTrack extends Fragment { private PhysicalDataFormatter phdformatter = new PhysicalDataFormatter(); - - private TextView TVDuration; - private TextView TVTrackName; - private TextView TVTrackID; - private TextView TVDistance; - private TextView TVDistanceUM; - private TextView TVMaxSpeed; - private TextView TVMaxSpeedUM; - private TextView TVAverageSpeed; - private TextView TVAverageSpeedUM; - private TextView TVAltitudeGap; - private TextView TVAltitudeGapUM; - private TextView TVOverallDirection; - private TextView TVTrackStatus; - private TextView TVDirectionUM; - - private TableLayout TLTrack; - private TableLayout TLDuration; - private TableLayout TLSpeedMax; - private TableLayout TLSpeedAvg; - private TableLayout TLDistance; - private TableLayout TLAltitudeGap; - private TableLayout TLOverallDirection; + final GPSApplication gpsApp = GPSApplication.getInstance(); + + private FrameLayout flTrack; + + private TextView tvDuration; + private TextView tvTrackName; + private TextView tvTrackID; + private TextView tvDistance; + private TextView tvDistanceUM; + private TextView tvAnnotations; + private TextView tvTrackpoints; + private TextView tvMaxSpeed; + private TextView tvMaxSpeedUM; + private TextView tvAverageSpeed; + private TextView tvAverageSpeedUM; + private TextView tvAltitudeGap; + private TextView tvAltitudeGapUM; + private TextView tvOverallDirection; + private TextView tvTrackStatus; + private TextView tvDirectionUM; + private TableLayout tlTrack; + private TableLayout tlTrackpoints; + private TableLayout tlAnnotations; + private TableLayout tlDuration; + private TableLayout tlSpeedMax; + private TableLayout tlSpeedAvg; + private TableLayout tlDistance; + private TableLayout tlAltitudeGap; + private TableLayout tlOverallDirection; + + private LinearLayout llTrackpointsAnnotations; private PhysicalData phdDuration; private PhysicalData phdSpeedMax; @@ -64,54 +85,94 @@ public class FragmentTrack extends Fragment { private PhysicalData phdAltitudeGap; private PhysicalData phdOverallDirection; - private String FTrackID = ""; - private String FTrackName = ""; - - final GPSApplication gpsApplication = GPSApplication.getInstance(); + private String fTrackID = ""; + private String fTrackName = ""; + private Track track; + private int prefDirections; + private boolean EGMAltitudeCorrection; + private boolean isValidAltitude; + /** + * The Observer that calculate the new available height when the layout is changed. + * If the height is enough, it set the setSpaceForExtraTilesAvailable flag + * that enable the visualization of the extra tiles: + *
    + *
  • Time and Satellites for FragmentGPSFix
  • + *
  • Trackpoints ane Annotation for FragmentTrack
  • + * *
+ */ + ViewTreeObserver.OnGlobalLayoutListener viewTreeObserverOnGLL = new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + flTrack.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } else { + flTrack.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } + //int width = flTrack.getMeasuredWidth(); + //int height = flTrack.getMeasuredHeight(); + //Log.w("myApp", "[#] FragmentTrack MEASURED: " + width + " x " + height); + int viewHeight = tlDistance.getMeasuredHeight() + (int)(6*getResources().getDisplayMetrics().density); + int layoutHeight = flTrack.getHeight() - (int)(6*getResources().getDisplayMetrics().density); + boolean isTimeAndSatellitesVisible; + if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ + isTimeAndSatellitesVisible = layoutHeight >= 6*viewHeight; + //Log.w("myApp", "[#] FragmentTrack MEASURED: " + layoutHeight + " / " + 6*viewHeight + " -> " + isTimeAndSatellitesVisible); + } else { + isTimeAndSatellitesVisible = layoutHeight >= 3.9*viewHeight; + //Log.w("myApp", "[#] FragmentTrack MEASURED: " + layoutHeight + " / " + 3.9*viewHeight + " -> " + isTimeAndSatellitesVisible); + } + GPSApplication.getInstance().setSpaceForExtraTilesAvailable(isTimeAndSatellitesVisible); + update(); + } + }; public FragmentTrack() { // Required empty public constructor } - @Subscribe (threadMode = ThreadMode.MAIN) - public void onEvent(Short msg) { - if (msg == EventBusMSG.UPDATE_TRACK) { - Update(); - } - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_track, container, false); + // FrameLayouts + flTrack = view.findViewById(R.id.id_fragmenttrackFrameLayout); + // TextViews - TVDuration = view.findViewById(R.id.id_textView_Duration); - TVTrackID = view.findViewById(R.id.id_textView_TrackIDLabel); - TVTrackName = view.findViewById(R.id.id_textView_TrackName); - TVDistance = view.findViewById(R.id.id_textView_Distance); - TVMaxSpeed = view.findViewById(R.id.id_textView_SpeedMax); - TVAverageSpeed = view.findViewById(R.id.id_textView_SpeedAvg); - TVAltitudeGap = view.findViewById(R.id.id_textView_AltitudeGap); - TVOverallDirection = view.findViewById(R.id.id_textView_OverallDirection); - TVTrackStatus = view.findViewById(R.id.id_textView_TrackStatus); - TVDirectionUM = view.findViewById(R.id.id_textView_OverallDirectionUM); - TVDistanceUM = view.findViewById(R.id.id_textView_DistanceUM); - TVMaxSpeedUM = view.findViewById(R.id.id_textView_SpeedMaxUM); - TVAverageSpeedUM = view.findViewById(R.id.id_textView_SpeedAvgUM); - TVAltitudeGapUM = view.findViewById(R.id.id_textView_AltitudeGapUM); + tvDuration = view.findViewById(R.id.id_textView_Duration); + tvTrackID = view.findViewById(R.id.id_textView_TrackIDLabel); + tvTrackName = view.findViewById(R.id.id_textView_TrackName); + tvTrackpoints = view.findViewById(R.id.id_textView_Trackpoints); + tvAnnotations = view.findViewById(R.id.id_textView_Annotations); + tvDistance = view.findViewById(R.id.id_textView_Distance); + tvMaxSpeed = view.findViewById(R.id.id_textView_SpeedMax); + tvAverageSpeed = view.findViewById(R.id.id_textView_SpeedAvg); + tvAltitudeGap = view.findViewById(R.id.id_textView_AltitudeGap); + tvOverallDirection = view.findViewById(R.id.id_textView_OverallDirection); + tvTrackStatus = view.findViewById(R.id.id_textView_TrackStatus); + tvDirectionUM = view.findViewById(R.id.id_textView_OverallDirectionUM); + tvDistanceUM = view.findViewById(R.id.id_textView_DistanceUM); + tvMaxSpeedUM = view.findViewById(R.id.id_textView_SpeedMaxUM); + tvAverageSpeedUM = view.findViewById(R.id.id_textView_SpeedAvgUM); + tvAltitudeGapUM = view.findViewById(R.id.id_textView_AltitudeGapUM); // TableLayouts - TLTrack = view.findViewById(R.id.id_tableLayout_TrackName) ; - TLDuration = view.findViewById(R.id.id_tableLayout_Duration) ; - TLSpeedMax = view.findViewById(R.id.id_tableLayout_SpeedMax) ; - TLDistance = view.findViewById(R.id.id_tableLayout_Distance) ; - TLSpeedAvg = view.findViewById(R.id.id_tableLayout_SpeedAvg) ; - TLAltitudeGap = view.findViewById(R.id.id_tableLayout_AltitudeGap) ; - TLOverallDirection = view.findViewById(R.id.id_tableLayout_OverallDirection) ; - + tlTrack = view.findViewById(R.id.id_tableLayout_TrackName) ; + tlTrackpoints = view.findViewById(R.id.id_TableLayout_Trackpoints) ; + tlAnnotations = view.findViewById(R.id.id_TableLayout_Annotations) ; + tlDuration = view.findViewById(R.id.id_tableLayout_Duration) ; + tlSpeedMax = view.findViewById(R.id.id_tableLayout_SpeedMax) ; + tlDistance = view.findViewById(R.id.id_tableLayout_Distance) ; + tlSpeedAvg = view.findViewById(R.id.id_tableLayout_SpeedAvg) ; + tlAltitudeGap = view.findViewById(R.id.id_tableLayout_AltitudeGap) ; + tlOverallDirection = view.findViewById(R.id.id_tableLayout_OverallDirection) ; + + // LinearLayouts + llTrackpointsAnnotations = view.findViewById(R.id.id_linearLayout_Annotation_Trackpoints); + + tvTrackStatus.setText(getString(R.string.track_empty) + "\n\n" + getString(R.string.track_start_with_button_below)); return view; } @@ -127,31 +188,51 @@ public void onResume() { } EventBus.getDefault().register(this); - Update(); + + ViewTreeObserver vto = flTrack.getViewTreeObserver(); + vto.addOnGlobalLayoutListener(viewTreeObserverOnGLL); + + update(); } @Override public void onPause() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + flTrack.getViewTreeObserver().removeGlobalOnLayoutListener(viewTreeObserverOnGLL); + } else { + flTrack.getViewTreeObserver().removeOnGlobalLayoutListener(viewTreeObserverOnGLL); + } EventBus.getDefault().unregister(this); super.onPause(); } + /** + * The EventBus receiver for Short Messages. + */ + @Subscribe (threadMode = ThreadMode.MAIN) + public void onEvent(Short msg) { + if (msg == EventBusMSG.UPDATE_TRACK) { + update(); + } + } - private Track track; - private int prefDirections; - private boolean EGMAltitudeCorrection; - private boolean isValidAltitude; - - public void Update() { - track = gpsApplication.getCurrentTrack(); - prefDirections = gpsApplication.getPrefShowDirections(); - EGMAltitudeCorrection = gpsApplication.getPrefEGM96AltitudeCorrection(); + /** + * Updates the user interface of the fragment. + * It takes care of visibility and value of each tile, and Track Status widgets. + */ + public void update() { + //Log.w("myApp", "[#] FragmentTrack.java - Update"); + track = gpsApp.getCurrentTrack(); + prefDirections = gpsApp.getPrefShowDirections(); + EGMAltitudeCorrection = gpsApp.getPrefEGM96AltitudeCorrection(); if (isAdded()) { if ((track != null) && (track.getNumberOfLocations() + track.getNumberOfPlacemarks() > 0)) { - FTrackID = getString(R.string.track_id) + " " + String.valueOf(track.getId()); - FTrackName = track.getName(); + fTrackID = (track.getDescription().isEmpty() ? + getString(R.string.track_id) + " " + String.valueOf(track.getId()) : + track.getDescription()); + fTrackName = track.getName(); phdDuration = phdformatter.format(track.getPrefTime(),PhysicalDataFormatter.FORMAT_DURATION); phdSpeedMax = phdformatter.format(track.getSpeedMax(),PhysicalDataFormatter.FORMAT_SPEED); phdSpeedAvg = phdformatter.format(track.getPrefSpeedAverage(),PhysicalDataFormatter.FORMAT_SPEED_AVG); @@ -159,47 +240,54 @@ public void Update() { phdAltitudeGap = phdformatter.format(track.getEstimatedAltitudeGap(EGMAltitudeCorrection),PhysicalDataFormatter.FORMAT_ALTITUDE); phdOverallDirection = phdformatter.format(track.getBearing(),PhysicalDataFormatter.FORMAT_BEARING); - TVTrackID.setText(FTrackID); - TVTrackName.setText(FTrackName); - TVDuration.setText(phdDuration.Value); - TVMaxSpeed.setText(phdSpeedMax.Value); - TVAverageSpeed.setText(phdSpeedAvg.Value); - TVDistance.setText(phdDistance.Value); - TVAltitudeGap.setText(phdAltitudeGap.Value); - TVOverallDirection.setText(phdOverallDirection.Value); - - TVMaxSpeedUM.setText(phdSpeedMax.UM); - TVAverageSpeedUM.setText(phdSpeedAvg.UM); - TVDistanceUM.setText(phdDistance.UM); - TVAltitudeGapUM.setText(phdAltitudeGap.UM); + tvTrackID.setText(fTrackID); + tvTrackName.setText(fTrackName); + tvDuration.setText(phdDuration.value); + tvMaxSpeed.setText(phdSpeedMax.value); + tvAverageSpeed.setText(phdSpeedAvg.value); + tvDistance.setText(phdDistance.value); + tvAltitudeGap.setText(phdAltitudeGap.value); + tvOverallDirection.setText(phdOverallDirection.value); - // Colorize the Altitude Gap textview depending on the altitude filter - isValidAltitude = track.isValidAltitude(); - TVAltitudeGap.setTextColor(isValidAltitude ? getResources().getColor(R.color.textColorPrimary) : getResources().getColor(R.color.textColorSecondary)); - TVAltitudeGapUM.setTextColor(isValidAltitude ? getResources().getColor(R.color.textColorPrimary) : getResources().getColor(R.color.textColorSecondary)); - - TVTrackStatus.setVisibility(View.INVISIBLE); + tvMaxSpeedUM.setText(phdSpeedMax.um); + tvAverageSpeedUM.setText(phdSpeedAvg.um); + tvDistanceUM.setText(phdDistance.um); + tvAltitudeGapUM.setText(phdAltitudeGap.um); - TVDirectionUM.setVisibility(prefDirections == 0 ? View.GONE : View.VISIBLE); + llTrackpointsAnnotations.setVisibility(gpsApp.isSpaceForExtraTilesAvailable() ? View.VISIBLE : View.GONE); - TLTrack.setVisibility(FTrackName.equals("") ? View.INVISIBLE : View.VISIBLE); - TLDuration.setVisibility(phdDuration.Value.equals("") ? View.INVISIBLE : View.VISIBLE); - TLSpeedMax.setVisibility(phdSpeedMax.Value.equals("") ? View.INVISIBLE : View.VISIBLE); - TLSpeedAvg.setVisibility(phdSpeedAvg.Value.equals("") ? View.INVISIBLE : View.VISIBLE); - TLDistance.setVisibility(phdDistance.Value.equals("") ? View.INVISIBLE : View.VISIBLE); - TLOverallDirection.setVisibility(phdOverallDirection.Value.equals("") ? View.INVISIBLE : View.VISIBLE); - TLAltitudeGap.setVisibility(phdAltitudeGap.Value.equals("") ? View.INVISIBLE : View.VISIBLE); + tvAnnotations.setText(String.valueOf(track.getNumberOfPlacemarks())); + tvTrackpoints.setText(String.valueOf(track.getNumberOfLocations())); + // Colorize the Altitude Gap textview depending on the altitude filter + isValidAltitude = track.isValidAltitude(); + tvAltitudeGap.setTextColor(isValidAltitude ? getResources().getColor(R.color.textColorPrimary) : getResources().getColor(R.color.textColorSecondary)); + tvAltitudeGapUM.setTextColor(isValidAltitude ? getResources().getColor(R.color.textColorPrimary) : getResources().getColor(R.color.textColorSecondary)); + + tvTrackStatus.setVisibility(View.INVISIBLE); + tvDirectionUM.setVisibility(prefDirections == 0 ? View.GONE : View.VISIBLE); + + tlTrack.setVisibility(fTrackName.equals("") ? View.INVISIBLE : View.VISIBLE); + tlDuration.setVisibility(phdDuration.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlSpeedMax.setVisibility(phdSpeedMax.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlSpeedAvg.setVisibility(phdSpeedAvg.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlDistance.setVisibility(phdDistance.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlOverallDirection.setVisibility(phdOverallDirection.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlAltitudeGap.setVisibility(phdAltitudeGap.value.equals("") ? View.INVISIBLE : View.VISIBLE); + tlTrackpoints.setVisibility(track.getNumberOfLocations() > 0 ? View.VISIBLE : View.INVISIBLE); + tlAnnotations.setVisibility(track.getNumberOfPlacemarks() + track.getNumberOfLocations() > 0 ? View.VISIBLE : View.INVISIBLE); } else { - TVTrackStatus.setVisibility(View.VISIBLE); - - TLTrack.setVisibility(View.INVISIBLE); - TLDuration.setVisibility(View.INVISIBLE); - TLSpeedMax.setVisibility(View.INVISIBLE); - TLSpeedAvg.setVisibility(View.INVISIBLE); - TLDistance.setVisibility(View.INVISIBLE); - TLOverallDirection.setVisibility(View.INVISIBLE); - TLAltitudeGap.setVisibility(View.INVISIBLE); + tvTrackStatus.setVisibility(View.VISIBLE); + + tlTrack.setVisibility(View.INVISIBLE); + tlDuration.setVisibility(View.INVISIBLE); + tlSpeedMax.setVisibility(View.INVISIBLE); + tlSpeedAvg.setVisibility(View.INVISIBLE); + tlDistance.setVisibility(View.INVISIBLE); + tlOverallDirection.setVisibility(View.INVISIBLE); + tlAltitudeGap.setVisibility(View.INVISIBLE); + tlTrackpoints.setVisibility(View.INVISIBLE); + tlAnnotations.setVisibility(View.INVISIBLE); } } } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTrackPropertiesDialog.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTrackPropertiesDialog.java new file mode 100644 index 00000000..8ff1a7ed --- /dev/null +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTrackPropertiesDialog.java @@ -0,0 +1,199 @@ +/* + * FragmentTrackPropertiesDialog - Java Class for Android + * Created by G.Capelli on 18/4/2021 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package eu.basicairdata.graziano.gpslogger; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.graphics.PorterDuff; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import androidx.fragment.app.DialogFragment; +import androidx.appcompat.app.AlertDialog; + +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.Toast; + +import org.greenrobot.eventbus.EventBus; + +import static eu.basicairdata.graziano.gpslogger.GPSApplication.NOT_AVAILABLE; +import static eu.basicairdata.graziano.gpslogger.GPSApplication.TOAST_VERTICAL_OFFSET; + +/** + * The Dialog that shows the properties of a Track. + * The user can use it to edit the description and the activity type. + * As extra feature of this dialog, The OK Button can finalize the Track. + */ +public class FragmentTrackPropertiesDialog extends DialogFragment { + + private EditText etDescription; + private final ImageView[] tracktypeImageView = new ImageView[7]; + + private int selectedTrackType = NOT_AVAILABLE; // The track type selected by the user + private Track trackToEdit = null; // The track to edit + private int title = 0; // The resource id for the title + private boolean finalizeTrackWithOk = false; // True if the "OK" button finalizes the track and creates a new one + + private static final String KEY_SELTRACKTYPE = "selectedTrackType"; + private static final String KEY_TITLE = "_title"; + private static final String KEY_ISFINALIZATION = "_isFinalization"; + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putInt(KEY_SELTRACKTYPE, selectedTrackType); + outState.putInt(KEY_TITLE, title); + outState.putBoolean(KEY_ISFINALIZATION, finalizeTrackWithOk); + } + + //@SuppressLint("InflateParams") + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder createPlacemarkAlert = new AlertDialog.Builder(getActivity()); + trackToEdit = GPSApplication.getInstance().getTrackToEdit(); + + if (savedInstanceState != null) { + title = savedInstanceState.getInt(KEY_TITLE, 0); + selectedTrackType = savedInstanceState.getInt(KEY_SELTRACKTYPE, NOT_AVAILABLE); + finalizeTrackWithOk = savedInstanceState.getBoolean(KEY_ISFINALIZATION, false); + } else { + selectedTrackType = trackToEdit.getType(); + } + + if (title != 0) createPlacemarkAlert.setTitle(title); + //createPlacemarkAlert.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_stop_24, getActivity().getTheme())); + + LayoutInflater inflater = getActivity().getLayoutInflater(); + final View view = inflater.inflate(R.layout.fragment_track_properties_dialog, null); + + etDescription = view.findViewById(R.id.track_description); + if (!trackToEdit.getDescription().isEmpty()) { + etDescription.setText(trackToEdit.getDescription()); + } + etDescription.setHint(GPSApplication.getInstance().getString(R.string.track_id) + " " + trackToEdit.getId()); + +// DescEditText.postDelayed(new Runnable() +// { +// public void run() +// { +// if (isAdded()) { +// DescEditText.requestFocus(); +// InputMethodManager mgr = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); +// mgr.showSoftInput(DescEditText, InputMethodManager.SHOW_IMPLICIT); +// } +// } +// }, 200); + + tracktypeImageView[Track.TRACK_TYPE_STEADY ] = view.findViewById(R.id.tracktype_steady); + tracktypeImageView[Track.TRACK_TYPE_MOUNTAIN ] = view.findViewById(R.id.tracktype_mountain); + tracktypeImageView[Track.TRACK_TYPE_WALK ] = view.findViewById(R.id.tracktype_walk); + tracktypeImageView[Track.TRACK_TYPE_RUN ] = view.findViewById(R.id.tracktype_run); + tracktypeImageView[Track.TRACK_TYPE_BICYCLE ] = view.findViewById(R.id.tracktype_bicycle); + tracktypeImageView[Track.TRACK_TYPE_CAR ] = view.findViewById(R.id.tracktype_car); + tracktypeImageView[Track.TRACK_TYPE_FLIGHT ] = view.findViewById(R.id.tracktype_flight); + + // Disable all images + for (int i = 0; i< tracktypeImageView.length; i++) { + tracktypeImageView[i].setColorFilter(getResources().getColor(R.color.colorIconDisabledOnDialog), PorterDuff.Mode.SRC_IN); + tracktypeImageView[i].setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + for (int i = 0; i < tracktypeImageView.length; i++) { + if (view == tracktypeImageView[i]) { + tracktypeImageView[i].setColorFilter(getResources().getColor(R.color.textColorRecControlPrimary), PorterDuff.Mode.SRC_IN); + selectedTrackType = i; + } else + tracktypeImageView[i].setColorFilter(getResources().getColor(R.color.colorIconDisabledOnDialog), PorterDuff.Mode.SRC_IN); + } + } + }); + } + // Activate the right image + if (selectedTrackType != NOT_AVAILABLE) + tracktypeImageView[selectedTrackType].setColorFilter(getResources().getColor(R.color.textColorRecControlPrimary), PorterDuff.Mode.SRC_IN); + else if (trackToEdit.getEstimatedTrackType() != Track.TRACK_TYPE_ND) + tracktypeImageView[trackToEdit.getEstimatedTrackType()].setColorFilter(getResources().getColor(R.color.textColorRecControlSecondary), PorterDuff.Mode.SRC_IN); + + createPlacemarkAlert.setView(view) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + if (isAdded()) { + String trackDescription = etDescription.getText().toString(); + trackToEdit.setDescription (trackDescription.trim()); + if (selectedTrackType != NOT_AVAILABLE) trackToEdit.setType(selectedTrackType); // the user selected a track type! + GPSApplication.getInstance().gpsDataBase.updateTrack(trackToEdit); + if (finalizeTrackWithOk) { + // a request to finalize a track + EventBus.getDefault().post(EventBusMSG.NEW_TRACK); + Toast toast = Toast.makeText(GPSApplication.getInstance().getApplicationContext(), R.string.toast_track_saved_into_tracklist, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + } else { + GPSApplication.getInstance().UpdateTrackList(); + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + } + } + } + }) + .setNeutralButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + } + }); + return createPlacemarkAlert.create(); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + } + + /** + * Sets the title of the Dialog. + * + * @param titleResource The Resource String of the title + */ + public void setTitleResource(int titleResource) { + title = titleResource; + } + + /** + * If true, the dialog finalizes the track when the user press the OK Button. + * + * @param finalize true if the dialog should finalize the track + */ + public void setFinalizeTrackWithOk(boolean finalize) { + finalizeTrackWithOk = finalize; + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTracklist.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTracklist.java index 5cdbf011..c0496703 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTracklist.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/FragmentTracklist.java @@ -1,6 +1,9 @@ -/** +/* * FragmentTracklist - Java Class for Android - * Created by G.Capelli (BasicAirData) on 19/6/2016 + * Created by G.Capelli on 19/6/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,13 +30,13 @@ import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; -import android.os.Environment; import android.preference.PreferenceManager; import androidx.core.app.ActivityCompat; import androidx.fragment.app.Fragment; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -56,28 +59,23 @@ import static eu.basicairdata.graziano.gpslogger.GPSApplication.NOT_AVAILABLE; - +/** + * The Fragment that displays and manages the list of the archived Tracks + * on the third tab (Tracklist) of the main Activity (GPSActivity). + */ public class FragmentTracklist extends Fragment { RecyclerView recyclerView; RecyclerView.LayoutManager layoutManager; - private TrackAdapter adapter; private final List data = Collections.synchronizedList(new ArrayList()); - private View view; - private TextView TVTracklistEmpty; - + private TextView tvTracklistEmpty; public FragmentTracklist() { // Required empty public constructor } - private boolean FileExists(String filename) { - File file = new File(filename); - return file.exists (); - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -85,16 +83,14 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, // Inflate the layout for this fragment view = inflater.inflate(R.layout.fragment_tracklist, container, false); - TVTracklistEmpty = view.findViewById(R.id.id_textView_TracklistEmpty); - recyclerView = view.findViewById(R.id.my_recycler_view); - + tvTracklistEmpty = view.findViewById(R.id.id_textView_TracklistEmpty); + recyclerView = view.findViewById(R.id.my_recycler_view); recyclerView.setHasFixedSize(true); layoutManager = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(layoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.getItemAnimator().setChangeDuration(0); adapter = new TrackAdapter(data); - switch (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) { case Configuration.UI_MODE_NIGHT_NO: // Night mode is not active, we're in day time @@ -107,27 +103,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, adapter.isLightTheme = false; break; } - recyclerView.setAdapter(adapter); - return view; } - public boolean CheckStoragePermission() { - if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - Log.w("myApp", "[#] FragmentTracklist.java - WRITE_EXTERNAL_STORAGE = Permission GRANTED"); - return true; // Permission Granted - } else { - Log.w("myApp", "[#] FragmentTracklist.java - WRITE_EXTERNAL_STORAGE = Permission DENIED"); - List listPermissionsNeeded = new ArrayList<>(); - listPermissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); - final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1; - ActivityCompat.requestPermissions(getActivity(), listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]) , REQUEST_ID_MULTIPLE_PERMISSIONS); - return false; - } - } - - @Override public void onResume() { super.onResume(); @@ -140,29 +119,30 @@ public void onResume() { } EventBus.getDefault().register(this); - Update(); + update(); } - @Override public void onPause() { EventBus.getDefault().unregister(this); super.onPause(); } - + /** + * The EventBus receiver for Normal Messages. + */ @Subscribe public void onEvent(final EventBusMSGNormal msg) { int i = 0; boolean found = false; - switch (msg.MSGType) { + switch (msg.eventBusMSG) { case EventBusMSG.TRACKLIST_SELECT: case EventBusMSG.TRACKLIST_DESELECT: synchronized (data) { do { - if (data.get(i).getId() == msg.id) { + if (data.get(i).getId() == msg.trackID) { found = true; - data.get(i).setSelected(msg.MSGType == EventBusMSG.TRACKLIST_SELECT); + data.get(i).setSelected(msg.eventBusMSG == EventBusMSG.TRACKLIST_SELECT); } i++; } while ((i < data.size()) && !found); @@ -176,7 +156,7 @@ public void onEvent(final EventBusMSGNormal msg) { data.get(i).setSelected(GPSApplication.getInstance().getLastClickState()); found = !found; } - if (data.get(i).getId() == msg.id) { + if (data.get(i).getId() == msg.trackID) { data.get(i).setSelected(GPSApplication.getInstance().getLastClickState()); found = !found; } @@ -192,7 +172,9 @@ public void onEvent(final EventBusMSGNormal msg) { } } - + /** + * The EventBus receiver for Short Messages. + */ @Subscribe public void onEvent(Short msg) { if (msg == EventBusMSG.UPDATE_TRACK) { @@ -230,29 +212,43 @@ public void run() { return; } if (msg == EventBusMSG.NOTIFY_TRACKS_DELETED) { - DeleteSomeTracks(); + deleteSomeTracks(); return; } if (msg == EventBusMSG.UPDATE_TRACKLIST) { - Update(); + update(); return; } if (msg == EventBusMSG.ACTION_BULK_SHARE_TRACKS) { - GPSApplication.getInstance().LoadJob(GPSApplication.JOB_TYPE_SHARE); + GPSApplication.getInstance().loadJob(GPSApplication.JOB_TYPE_SHARE); if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - CheckStoragePermission(); // Ask for storage permission - } else GPSApplication.getInstance().ExecuteJob(); - GPSApplication.getInstance().DeselectAllTracks(); + checkStoragePermission(); // Ask for storage permission + } else GPSApplication.getInstance().executeJob(); + GPSApplication.getInstance().deselectAllTracks(); return; } + if (msg == EventBusMSG.ACTION_EDIT_TRACK) { + for (Track T : GPSApplication.getInstance().getTrackList()) { + if (T.isSelected()) { + GPSApplication.getInstance().setTrackToEdit(T); + FragmentManager fm = getActivity().getSupportFragmentManager(); + FragmentTrackPropertiesDialog tpDialog = new FragmentTrackPropertiesDialog(); + tpDialog.setTitleResource(R.string.card_menu_edit); + tpDialog.setFinalizeTrackWithOk(false); + tpDialog.show(fm, ""); + break; + } + } + } + if (msg == EventBusMSG.ACTION_BULK_VIEW_TRACKS) { - final ArrayList ail = new ArrayList<>(GPSApplication.getInstance().getExternalViewerChecker().getAppInfoList()); + final ArrayList evList = new ArrayList<>(GPSApplication.getInstance().getExternalViewerChecker().getExternalViewersList()); - if (!ail.isEmpty()) { - if (ail.size() == 1) { + if (!evList.isEmpty()) { + if (evList.size() == 1) { // 1 Viewer installed, let's use it - GPSApplication.getInstance().setTrackViewer(ail.get(0)); - OpenTrack(); + GPSApplication.getInstance().setTrackViewer(evList.get(0)); + openTrack(); } else { // 2 or more viewers installed @@ -260,10 +256,10 @@ public void run() { String pn = PreferenceManager.getDefaultSharedPreferences(getContext()).getString("prefTracksViewer", ""); boolean foundDefault = false; - for (AppInfo ai : ail) { - if (ai.packageName.equals(pn)) { + for (ExternalViewer ev : evList) { + if (ev.packageName.equals(pn)) { // Default Viewer available! - GPSApplication.getInstance().setTrackViewer(ai); + GPSApplication.getInstance().setTrackViewer(ev); foundDefault = true; } } @@ -274,14 +270,14 @@ public void run() { View view = getLayoutInflater().inflate(R.layout.appdialog_list, null); ListView lv = (ListView) view.findViewById(R.id.id_appdialog_list); - AppDialogList clad = new AppDialogList(getActivity(), ail); + ExternalViewerAdapter clad = new ExternalViewerAdapter(getActivity(), evList); lv.setAdapter(clad); lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - GPSApplication.getInstance().setTrackViewer(ail.get(position)); - OpenTrack(); + GPSApplication.getInstance().setTrackViewer(evList.get(position)); + openTrack(); dialog.dismiss(); } }); @@ -289,18 +285,18 @@ public void onItemClick(AdapterView parent, View view, int position, long id) dialog.show(); } else { // Default Track Viewer found! Let's use it. - OpenTrack(); + openTrack(); } } } return; } if (msg == EventBusMSG.ACTION_BULK_EXPORT_TRACKS) { - GPSApplication.getInstance().LoadJob(GPSApplication.JOB_TYPE_EXPORT); + GPSApplication.getInstance().loadJob(GPSApplication.JOB_TYPE_EXPORT); if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - CheckStoragePermission(); // Ask for storage permission - } else GPSApplication.getInstance().ExecuteJob(); - GPSApplication.getInstance().DeselectAllTracks(); + checkStoragePermission(); // Ask for storage permission + } else GPSApplication.getInstance().executeJob(); + GPSApplication.getInstance().deselectAllTracks(); return; } if (msg == EventBusMSG.ACTION_BULK_DELETE_TRACKS) { @@ -310,9 +306,9 @@ public void onItemClick(AdapterView parent, View view, int position, long id) boolean fileexist = false; if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { for (Track track : selectedTracks) { - fileexist |= FileExists(Environment.getExternalStorageDirectory() + "/GPSLogger/" + track.getName() + ".kml") - || FileExists(Environment.getExternalStorageDirectory() + "/GPSLogger/" + track.getName() + ".gpx") - || FileExists(Environment.getExternalStorageDirectory() + "/GPSLogger/" + track.getName() + ".txt"); + fileexist |= fileExists(GPSApplication.DIRECTORY_EXPORT + "/" + GPSApplication.getInstance().getFileName(track) + ".kml") + || fileExists(GPSApplication.DIRECTORY_EXPORT + "/" + GPSApplication.getInstance().getFileName(track) + ".gpx") + || fileExists(GPSApplication.DIRECTORY_EXPORT + "/" + GPSApplication.getInstance().getFileName(track) + ".txt"); } } if (fileexist) { @@ -323,16 +319,16 @@ public void onItemClick(AdapterView parent, View view, int position, long id) public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); GPSApplication.getInstance().setDeleteAlsoExportedFiles(true); // Delete also exported files - GPSApplication.getInstance().LoadJob(GPSApplication.JOB_TYPE_DELETE); - GPSApplication.getInstance().ExecuteJob(); + GPSApplication.getInstance().loadJob(GPSApplication.JOB_TYPE_DELETE); + GPSApplication.getInstance().executeJob(); } }); builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); GPSApplication.getInstance().setDeleteAlsoExportedFiles(false); // Don't delete exported files - GPSApplication.getInstance().LoadJob(GPSApplication.JOB_TYPE_DELETE); - GPSApplication.getInstance().ExecuteJob(); + GPSApplication.getInstance().loadJob(GPSApplication.JOB_TYPE_DELETE); + GPSApplication.getInstance().executeJob(); } }); builder.setNeutralButton(R.string.cancel, new DialogInterface.OnClickListener() { @@ -350,8 +346,8 @@ public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); GPSApplication.getInstance().setDeleteAlsoExportedFiles(false); // Don't delete exported files - GPSApplication.getInstance().LoadJob(GPSApplication.JOB_TYPE_DELETE); - GPSApplication.getInstance().ExecuteJob(); + GPSApplication.getInstance().loadJob(GPSApplication.JOB_TYPE_DELETE); + GPSApplication.getInstance().executeJob(); } }); builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { @@ -380,7 +376,7 @@ public void onClick(DialogInterface dialog, int id) { for (ExportingTask ET : selectedTracks) { - Track track = GPSApplication.getInstance().GPSDataBase.getTrack(ET.getId()); + Track track = GPSApplication.getInstance().gpsDataBase.getTrack(ET.getId()); if (track == null) return; if (i > 0) { @@ -399,7 +395,7 @@ public void onClick(DialogInterface dialog, int id) { PhysicalData phdAltitudeGap; PhysicalData phdOverallDirection; phdDuration = phdformatter.format(track.getDuration(),PhysicalDataFormatter.FORMAT_DURATION); - phdDurationMoving = phdformatter.format(track.getDuration_Moving(),PhysicalDataFormatter.FORMAT_DURATION); + phdDurationMoving = phdformatter.format(track.getDurationMoving(),PhysicalDataFormatter.FORMAT_DURATION); phdSpeedMax = phdformatter.format(track.getSpeedMax(),PhysicalDataFormatter.FORMAT_SPEED); phdSpeedAvg = phdformatter.format(track.getSpeedAverage(),PhysicalDataFormatter.FORMAT_SPEED_AVG); phdSpeedAvgMoving = phdformatter.format(track.getSpeedAverageMoving(),PhysicalDataFormatter.FORMAT_SPEED_AVG); @@ -408,37 +404,39 @@ public void onClick(DialogInterface dialog, int id) { phdOverallDirection = phdformatter.format(track.getBearing(),PhysicalDataFormatter.FORMAT_BEARING); if (track.getNumberOfLocations() <= 1) { extraText.append(getString(R.string.app_name) + " - " + getString(R.string.tab_track) + " " + track.getName() + + (track.getDescription().isEmpty() ? "\n" + track.getDescription() + "\n" : "") + "\n" + track.getNumberOfLocations() + " " + getString(R.string.trackpoints) - + "\n" + track.getNumberOfPlacemarks() + " " + getString(R.string.placemarks)); + + "\n" + track.getNumberOfPlacemarks() + " " + getString(R.string.annotations)); } else { extraText.append(getString(R.string.app_name) + " - " + getString(R.string.tab_track) + " " + track.getName() + + (!track.getDescription().isEmpty() ? "\n" + track.getDescription() : "") + "\n" + track.getNumberOfLocations() + " " + getString(R.string.trackpoints) - + "\n" + track.getNumberOfPlacemarks() + " " + getString(R.string.placemarks) + + "\n" + track.getNumberOfPlacemarks() + " " + getString(R.string.annotations) + "\n" - + "\n" + getString(R.string.distance) + " = " + phdDistance.Value + " " + phdDistance.UM - + "\n" + getString(R.string.duration) + " = " + phdDuration.Value + " | " + phdDurationMoving.Value - + "\n" + getString(R.string.altitude_gap) + " = " + phdAltitudeGap.Value + " " + phdAltitudeGap.UM - + "\n" + getString(R.string.max_speed) + " = " + phdSpeedMax.Value + " " + phdSpeedMax.UM - + "\n" + getString(R.string.average_speed) + " = " + phdSpeedAvg.Value + " | " + phdSpeedAvgMoving.Value + " " + phdSpeedAvg.UM - + "\n" + getString(R.string.overall_direction) + " = " + phdOverallDirection.Value + " " + phdOverallDirection.UM + + "\n" + getString(R.string.distance) + " = " + phdDistance.value + " " + phdDistance.um + + "\n" + getString(R.string.duration) + " = " + phdDuration.value + " | " + phdDurationMoving.value + + "\n" + getString(R.string.altitude_gap) + " = " + phdAltitudeGap.value + " " + phdAltitudeGap.um + + "\n" + getString(R.string.max_speed) + " = " + phdSpeedMax.value + " " + phdSpeedMax.um + + "\n" + getString(R.string.average_speed) + " = " + phdSpeedAvg.value + " | " + phdSpeedAvgMoving.value + " " + phdSpeedAvg.um + + "\n" + getString(R.string.overall_direction) + " = " + phdOverallDirection.value + " " + phdOverallDirection.um + "\n" + "\n" + getString(R.string.pref_track_stats) + ": " + getString(R.string.pref_track_stats_totaltime) + " | " + getString(R.string.pref_track_stats_movingtime)); } - String fname = track.getName() + ".kml"; - file = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/", fname); + String fname = GPSApplication.getInstance().getFileName(track) + ".kml"; + file = new File(GPSApplication.DIRECTORY_TEMP + "/", fname); if (file.exists () && GPSApplication.getInstance().getPrefExportKML()) { Uri uri = FileProvider.getUriForFile(GPSApplication.getInstance(), "eu.basicairdata.graziano.gpslogger.fileprovider", file); files.add(uri); } - fname = track.getName() + ".gpx"; - file = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/", fname); + fname = GPSApplication.getInstance().getFileName(track) + ".gpx"; + file = new File(GPSApplication.DIRECTORY_TEMP + "/", fname); if (file.exists () && GPSApplication.getInstance().getPrefExportGPX()) { Uri uri = FileProvider.getUriForFile(GPSApplication.getInstance(), "eu.basicairdata.graziano.gpslogger.fileprovider", file); files.add(uri); } - fname = track.getName() + ".txt"; - file = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/", fname); + fname = GPSApplication.getInstance().getFileName(track) + ".txt"; + file = new File(GPSApplication.DIRECTORY_TEMP + "/", fname); if (file.exists () && GPSApplication.getInstance().getPrefExportTXT()) { Uri uri = FileProvider.getUriForFile(GPSApplication.getInstance(), "eu.basicairdata.graziano.gpslogger.fileprovider", file); files.add(uri); @@ -473,17 +471,46 @@ public void onClick(DialogInterface dialog, int id) { } } + /** + * Checks the permission to access the External Storage. + */ + public boolean checkStoragePermission() { + if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + Log.w("myApp", "[#] FragmentTracklist.java - WRITE_EXTERNAL_STORAGE = Permission GRANTED"); + return true; // Permission Granted + } else { + Log.w("myApp", "[#] FragmentTracklist.java - WRITE_EXTERNAL_STORAGE = Permission DENIED"); + List listPermissionsNeeded = new ArrayList<>(); + listPermissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); + final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1; + ActivityCompat.requestPermissions(getActivity(), listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]) , REQUEST_ID_MULTIPLE_PERMISSIONS); + return false; + } + } - public void OpenTrack() { - GPSApplication.getInstance().LoadJob(GPSApplication.JOB_TYPE_VIEW); - if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - CheckStoragePermission(); // Ask for storage permission - } else GPSApplication.getInstance().ExecuteJob(); - GPSApplication.getInstance().DeselectAllTracks(); + /** + * @return true if a specified file exists + */ + private boolean fileExists(String filename) { + File file = new File(filename); + return file.exists (); } + /** + * Opens a Track with an external viewer using the GPSApplication Job executor. + */ + public void openTrack() { + GPSApplication.getInstance().loadJob(GPSApplication.JOB_TYPE_VIEW); + if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + checkStoragePermission(); // Ask for storage permission + } else GPSApplication.getInstance().executeJob(); + GPSApplication.getInstance().deselectAllTracks(); + } - public void Update() { + /** + * Updates the user interface of the fragment. + */ + public void update() { if (isAdded()) { Log.w("myApp", "[#] FragmentTracklist.java - Updating Tracklist"); final List TI = GPSApplication.getInstance().getTrackList(); @@ -493,21 +520,21 @@ public void Update() { if (!TI.isEmpty()) { data.addAll(TI); if (data.get(0).getId() == GPSApplication.getInstance().getCurrentTrack().getId()) { - GPSApplication.getInstance().setisCurrentTrackVisible(true); + GPSApplication.getInstance().setCurrentTrackVisible(true); //Log.w("myApp", "[#] FragmentTracklist.java - current track, VISIBLE into the tracklist (" // + GPSApplication.getInstance().getCurrentTrack().getId() + ")"); } else { - GPSApplication.getInstance().setisCurrentTrackVisible(false); + GPSApplication.getInstance().setCurrentTrackVisible(false); //Log.w("myApp", "[#] FragmentTracklist.java - current track empty, NOT VISIBLE into the tracklist"); } } else { - GPSApplication.getInstance().setisCurrentTrackVisible(false); + GPSApplication.getInstance().setCurrentTrackVisible(false); } try { getActivity().runOnUiThread(new Runnable() { @Override public void run() { - TVTracklistEmpty.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE); + tvTracklistEmpty.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE); adapter.notifyDataSetChanged(); } }); @@ -518,8 +545,11 @@ public void run() { } } - - public void DeleteSomeTracks() { + /** + * Deletes some tracks from the CardView Adapter via notification, + * in order to show a graceful animation of the deletion. + */ + public void deleteSomeTracks() { try { getActivity().runOnUiThread(new Runnable() { @Override @@ -532,13 +562,13 @@ public void run() { adapter.notifyItemRemoved(i); } } - TVTracklistEmpty.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE); + tvTracklistEmpty.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE); } } }); } catch (NullPointerException e) { //Log.w("myApp", "[#] FragmentTracklist.java - Unable to manage UI"); - Update(); + update(); } } } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSActivity.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSActivity.java index d52c1868..c2393a2d 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSActivity.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSActivity.java @@ -1,6 +1,9 @@ -/** +/* * GPSActivity - Java Class for Android - * Created by G.Capelli (BasicAirData) on 5/5/2016 + * Created by G.Capelli on 5/5/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,13 +17,11 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * */ package eu.basicairdata.graziano.gpslogger; import android.Manifest; -import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -28,7 +29,6 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.provider.Settings; import androidx.annotation.NonNull; import com.google.android.material.bottomsheet.BottomSheetBehavior; @@ -45,6 +45,7 @@ import androidx.appcompat.view.ActionMode; import androidx.appcompat.widget.Toolbar; import android.util.Log; +import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -60,32 +61,39 @@ import java.util.List; import java.util.Map; +import static eu.basicairdata.graziano.gpslogger.GPSApplication.TOAST_VERTICAL_OFFSET; + +/** + * The main Activity. + * Here you can view the status of GPS, of the current Track and the list + * of the recorded tracks. + *

+ * The tabbed interface contains the 3 following Fragments: + *

    + *
  • Page 1 = FragmentGPSFix: it shows the status of the GPS and the FIX
  • + *
  • Page 2 = FragmentTrack: it shows the statistics of the current Track
  • + *
  • Page 3 = FragmentTracklist: it lists the archive of the recorded tracks
  • + *
+ * The Activity is driven by a bottom bar (FragmentRecordingControls), + * that is visible on the first 2 pages, and can host an Action-mode Toolbar + * on top of the third page (ToolbarActionMode). + */ public class GPSActivity extends AppCompatActivity { private static final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1; - private final GPSApplication GPSApp = GPSApplication.getInstance(); - + private final GPSApplication gpsApp = GPSApplication.getInstance(); private Toolbar toolbar; private TabLayout tabLayout; private ViewPager viewPager; private ActionMode actionMode; private View bottomSheet; - private MenuItem menutrackfinished = null; - private int activeTab = 1; - final Context context = this; - - private boolean show_toast_grant_storage_permission = false; - - private BottomSheetBehavior mBottomSheetBehavior; - - Toast ToastClickAgain; - + private BottomSheetBehavior bottomSheetBehavior; + private boolean showToastGrantStoragePermission = false; @Override protected void onCreate(Bundle savedInstanceState) { Log.w("myApp", "[#] " + this + " - onCreate()"); - setTheme(R.style.MyMaterialTheme); super.onCreate(savedInstanceState); @@ -108,38 +116,30 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onTabSelected(TabLayout.Tab tab) { super.onTabSelected(tab); - activeTab = tab.getPosition(); - GPSApp.setGPSActivity_activeTab(activeTab); + gpsApp.setGPSActivityActiveTab(tab.getPosition()); updateBottomSheetPosition(); - ActivateActionModeIfNeeded(); + activateActionModeIfNeeded(); } }); - bottomSheet = findViewById( R.id.id_bottomsheet ); - - mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet); - mBottomSheetBehavior.setHideable (false); - - ToastClickAgain = Toast.makeText(this, getString(R.string.toast_track_finished_click_again), Toast.LENGTH_SHORT); + bottomSheet = findViewById(R.id.id_bottomsheet); + bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet); + bottomSheetBehavior.setHideable(false); } - @Override public void onStart() { Log.w("myApp", "[#] " + this + " - onStart()"); super.onStart(); - activeTab = tabLayout.getSelectedTabPosition(); - GPSApp.setGPSActivity_activeTab(activeTab); + gpsApp.setGPSActivityActiveTab(tabLayout.getSelectedTabPosition()); } - @Override public void onStop() { Log.w("myApp", "[#] " + this + " - onStop()"); super.onStop(); } - @Override public void onResume() { Log.w("myApp", "[#] " + this + " - onResume()"); @@ -153,22 +153,21 @@ public void onResume() { } EventBus.getDefault().register(this); - LoadPreferences(); + loadPreferences(); EventBus.getDefault().post(EventBusMSG.APP_RESUME); - if (menutrackfinished != null) menutrackfinished.setVisible(!GPSApp.getCurrentTrack().getName().equals("")); // Check for Location runtime Permissions (for Android 23+) - if (!GPSApp.isLocationPermissionChecked()) { - CheckLocationPermission(); - GPSApp.setLocationPermissionChecked(true); + if (!gpsApp.isLocationPermissionChecked()) { + checkLocationPermission(); + gpsApp.setLocationPermissionChecked(true); } - ActivateActionModeIfNeeded(); + activateActionModeIfNeeded(); - if (GPSApp.FlagExists(GPSApplication.FLAG_RECORDING) && !GPSApp.getRecording()) { + if (gpsApp.preferenceFlagExists(GPSApplication.FLAG_RECORDING) && !gpsApp.isRecording()) { // The app is crashed in background Log.w("myApp", "[#] GPSActivity.java - THE APP HAS BEEN KILLED IN BACKGROUND DURING A RECORDING !!!"); - GPSApp.FlagRemove(GPSApplication.FLAG_RECORDING); + gpsApp.clearPreferenceFlag_NoBackup(GPSApplication.FLAG_RECORDING); AlertDialog.Builder builder = new AlertDialog.Builder(this); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -195,23 +194,23 @@ public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); } }); - AlertDialog dialog = builder.create(); dialog.show(); } - - if (GPSApp.isJustStarted() && (GPSApp.getCurrentTrack().getNumberOfLocations() + GPSApp.getCurrentTrack().getNumberOfPlacemarks() > 0)) { - Toast.makeText(this.context, getString(R.string.toast_active_track_not_empty), Toast.LENGTH_LONG).show(); - GPSApp.setJustStarted(false); - } else GPSApp.setJustStarted(false); - - if (show_toast_grant_storage_permission) { - Toast.makeText(this.context, getString(R.string.please_grant_storage_permission), Toast.LENGTH_LONG).show(); - show_toast_grant_storage_permission = false; + if (gpsApp.isJustStarted() && (gpsApp.getCurrentTrack().getNumberOfLocations() + gpsApp.getCurrentTrack().getNumberOfPlacemarks() > 0)) { + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.toast_active_track_not_empty, Toast.LENGTH_LONG); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + } + gpsApp.setJustStarted(false); + if (showToastGrantStoragePermission) { + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.please_grant_storage_permission, Toast.LENGTH_LONG); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + showToastGrantStoragePermission = false; } } - @Override public void onPause() { Log.w("myApp", "[#] " + this + " - onPause()"); @@ -220,67 +219,54 @@ public void onPause() { super.onPause(); } - @Override public void onBackPressed() { ShutdownApp(); } - @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); updateBottomSheetPosition(); } - @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_menu, menu); return true; } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - menutrackfinished = menu.findItem(R.id.action_track_finished); - menutrackfinished.setVisible(!GPSApp.getCurrentTrack().getName().equals("")); - return true; - } - - @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); - if (id == R.id.action_settings) { - GPSApp.setHandlerTimer(60000); + gpsApp.setHandlerTime(60000); Intent intent = new Intent(this, SettingsActivity.class); startActivity(intent); return true; } - if (id == R.id.action_track_finished) { - if (GPSApp.getNewTrackFlag()) { - // This is the second click - GPSApp.setNewTrackFlag(false); - GPSApp.setRecording(false); - EventBus.getDefault().post(EventBusMSG.NEW_TRACK); - ToastClickAgain.cancel(); - Toast.makeText(this, getString(R.string.toast_track_saved_into_tracklist), Toast.LENGTH_SHORT).show(); - } else { - // This is the first click - GPSApp.setNewTrackFlag(true); // Start the timer - ToastClickAgain.show(); - } - return true; - } if (id == R.id.action_about) { - // Show About Dialog + // Shows About Dialog FragmentManager fm = getSupportFragmentManager(); FragmentAboutDialog aboutDialog = new FragmentAboutDialog(); aboutDialog.show(fm, ""); return true; } + if (id == R.id.action_online_help) { + if (isBrowserInstalled()) { + // Opens the default browser and shows the Getting Started Guide page + String url = "https://www.basicairdata.eu/projects/android/android-gps-logger/getting-started-guide-for-gps-logger/"; + Intent i = new Intent(Intent.ACTION_VIEW); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + i.setData(Uri.parse(url)); + startActivity(i); + } else { + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.toast_no_browser_installed, Toast.LENGTH_LONG); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); + } + return true; + } if (id == R.id.action_shutdown) { ShutdownApp(); return true; @@ -288,99 +274,113 @@ public boolean onOptionsItemSelected(MenuItem item) { return super.onOptionsItemSelected(item); } - - private void updateBottomSheetPosition() { - activeTab = tabLayout.getSelectedTabPosition(); - if (activeTab != 2) { - mBottomSheetBehavior.setPeekHeight(1); - mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); - //Log.w("myApp", "[#] GPSActivity.java - mBottomSheetBehavior.setPeekHeight(" + bottomSheet.getHeight() +");"); - mBottomSheetBehavior.setPeekHeight(bottomSheet.getHeight()); - } else { - mBottomSheetBehavior.setPeekHeight(1); - mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED) ; - } - } - - - private void setupViewPager(ViewPager viewPager) { - ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); - adapter.addFragment(new FragmentGPSFix(), getString(R.string.tab_gpsfix)); - adapter.addFragment(new FragmentTrack(), getString(R.string.tab_track)); - adapter.addFragment(new FragmentTracklist(), getString(R.string.tab_tracklist)); - viewPager.setAdapter(adapter); - } - - class ViewPagerAdapter extends FragmentPagerAdapter { - private final List mFragmentList = new ArrayList<>(); - private final List mFragmentTitleList = new ArrayList<>(); - - public ViewPagerAdapter(FragmentManager manager) { - super(manager); - } - - @NonNull - @Override - public Fragment getItem(int position) { - return mFragmentList.get(position); - } - - @Override - public int getCount() { - return mFragmentList.size(); - } - - public void addFragment(Fragment fragment, String title) { - mFragmentList.add(fragment); - mFragmentTitleList.add(title); - } - - @Override - public CharSequence getPageTitle(int position) { - return mFragmentTitleList.get(position); + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + switch (requestCode) { + case REQUEST_ID_MULTIPLE_PERMISSIONS: { + Map perms = new HashMap<>(); + if (grantResults.length > 0) { + // Fill with actual results from user + for (int i = 0; i < permissions.length; i++) perms.put(permissions[i], grantResults[i]); + // Check for permissions + if (perms.containsKey(Manifest.permission.ACCESS_FINE_LOCATION)) { + if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + Log.w("myApp", "[#] GPSActivity.java - ACCESS_FINE_LOCATION = PERMISSION_GRANTED; setGPSLocationUpdates!"); + gpsApp.setGPSLocationUpdates(false); + gpsApp.setGPSLocationUpdates(true); + gpsApp.updateGPSLocationFrequency(); + } else { + Log.w("myApp", "[#] GPSActivity.java - ACCESS_FINE_LOCATION = PERMISSION_DENIED"); + } + } + if (perms.containsKey(Manifest.permission.INTERNET)) { + if (perms.get(Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED) { + Log.w("myApp", "[#] GPSActivity.java - INTERNET = PERMISSION_GRANTED"); + } else { + Log.w("myApp", "[#] GPSActivity.java - INTERNET = PERMISSION_DENIED"); + } + } + if (perms.containsKey(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + if (perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + Log.w("myApp", "[#] GPSActivity.java - WRITE_EXTERNAL_STORAGE = PERMISSION_GRANTED"); + // ---------------------------------------------------- Create the Directories if not exist + File sd = new File(GPSApplication.DIRECTORY_EXPORT); + if (!sd.exists()) { + sd.mkdir(); + } + sd = new File(GPSApplication.DIRECTORY_TEMP); + if (!sd.exists()) { + sd.mkdir(); + } + sd = new File(getApplicationContext().getFilesDir() + "/Thumbnails"); + if (!sd.exists()) { + sd.mkdir(); + } + EGM96 egm96 = EGM96.getInstance(); + if (egm96 != null) { + if (!egm96.isEGMGridLoaded()) { + //Log.w("myApp", "[#] GPSApplication.java - Loading EGM Grid..."); + egm96.LoadGridFromFile(GPSApplication.DIRECTORY_TEMP + "/WW15MGH.DAC", getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); + } + } + if (gpsApp.getJobsPending() > 0) gpsApp.executeJob(); + } else { + Log.w("myApp", "[#] GPSActivity.java - WRITE_EXTERNAL_STORAGE = PERMISSION_DENIED"); + if (gpsApp.getJobsPending() > 0) { + // Shows toast "Unable to write the file" + showToastGrantStoragePermission = true; + EventBus.getDefault().post(EventBusMSG.TOAST_STORAGE_PERMISSION_REQUIRED); + gpsApp.setJobsPending(0); + } + } + } + } + break; + } + default: { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } } } + /** + * The EventBus receiver for Normal Messages. + */ @Subscribe public void onEvent(EventBusMSGNormal msg) { - switch (msg.MSGType) { + switch (msg.eventBusMSG) { case EventBusMSG.TRACKLIST_SELECT: case EventBusMSG.TRACKLIST_DESELECT: - ActivateActionModeIfNeeded(); + activateActionModeIfNeeded(); } } + /** + * The EventBus receiver for Short Messages. + */ @Subscribe public void onEvent(Short msg) { switch (msg) { case EventBusMSG.REQUEST_ADD_PLACEMARK: - // Show Placemark Dialog + // Shows the Placemark Dialog FragmentManager fm = getSupportFragmentManager(); FragmentPlacemarkDialog placemarkDialog = new FragmentPlacemarkDialog(); placemarkDialog.show(fm, ""); break; case EventBusMSG.UPDATE_TRACKLIST: case EventBusMSG.NOTIFY_TRACKS_DELETED: - ActivateActionModeIfNeeded(); + activateActionModeIfNeeded(); break; case EventBusMSG.APPLY_SETTINGS: - LoadPreferences(); - break; - - case EventBusMSG.UPDATE_TRACK: - runOnUiThread(new Runnable() { - @Override - public void run() { - if (menutrackfinished != null) - menutrackfinished.setVisible(!GPSApp.getCurrentTrack().getName().equals("")); - } - }); + loadPreferences(); break; case EventBusMSG.TOAST_TRACK_EXPORTED: runOnUiThread(new Runnable() { @Override public void run() { - Toast.makeText(context, getString(R.string.toast_track_exported), Toast.LENGTH_LONG).show(); + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.toast_track_exported, Toast.LENGTH_LONG); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); } }); break; @@ -388,7 +388,9 @@ public void run() { runOnUiThread(new Runnable() { @Override public void run() { - Toast.makeText(context, getString(R.string.please_grant_storage_permission), Toast.LENGTH_LONG).show(); + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.please_grant_storage_permission, Toast.LENGTH_LONG); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); } }); break; @@ -396,38 +398,118 @@ public void run() { runOnUiThread(new Runnable() { @Override public void run() { - Toast.makeText(context, getString(R.string.export_unable_to_write_file), Toast.LENGTH_LONG).show(); + Toast toast = Toast.makeText(gpsApp.getApplicationContext(), R.string.export_unable_to_write_file, Toast.LENGTH_LONG); + toast.setGravity(Gravity.BOTTOM, 0, TOAST_VERTICAL_OFFSET); + toast.show(); } }); } } - private void LoadPreferences() { + /** + * @return true if a browser is installed on the device. + */ + private Boolean isBrowserInstalled() { + String url = "https://www.basicairdata.eu/projects/android/android-gps-logger/getting-started-guide-for-gps-logger/"; + Uri webAddress = Uri.parse(url); + Intent intentWeb = new Intent(Intent.ACTION_VIEW, webAddress); + intentWeb.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return (intentWeb.resolveActivity(getPackageManager()) != null); + } + + /** + * Expands/Collapses the bottom bar, basing on the active tab. + */ + private void updateBottomSheetPosition() { + gpsApp.setGPSActivityActiveTab(tabLayout.getSelectedTabPosition()); + if (gpsApp.getGPSActivityActiveTab() != 2) { + bottomSheetBehavior.setPeekHeight(1); + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); + //Log.w("myApp", "[#] GPSActivity.java - mBottomSheetBehavior.setPeekHeight(" + bottomSheet.getHeight() +");"); + bottomSheetBehavior.setPeekHeight(bottomSheet.getHeight()); + } else { + bottomSheetBehavior.setPeekHeight(1); + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED) ; + } + } + + /** + * Adds the 3 Fragments to the ViewPager Adapter. + */ + private void setupViewPager(ViewPager viewPager) { + ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); + adapter.addFragment(new FragmentGPSFix(), getString(R.string.tab_gpsfix)); + adapter.addFragment(new FragmentTrack(), getString(R.string.tab_track)); + adapter.addFragment(new FragmentTracklist(), getString(R.string.tab_tracklist)); + viewPager.setAdapter(adapter); + } + + /** + * The ViewPager Adapter Class. + */ + static class ViewPagerAdapter extends FragmentPagerAdapter { + private final List fragmentsList = new ArrayList<>(); + private final List fragmentsTitleList = new ArrayList<>(); + + public ViewPagerAdapter(FragmentManager manager) { + super(manager); + } + + @NonNull + @Override + public Fragment getItem(int position) { + return fragmentsList.get(position); + } + + @Override + public int getCount() { + return fragmentsList.size(); + } + + public void addFragment(Fragment fragment, String title) { + fragmentsList.add(fragment); + fragmentsTitleList.add(title); + } + + @Override + public CharSequence getPageTitle(int position) { + return fragmentsTitleList.get(position); + } + } + + /** + * Loads the preferences related to this Activity: the prefKeepScreenOn Flag. + */ + private void loadPreferences() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); if (preferences.getBoolean("prefKeepScreenOn", true)) getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); else getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } - private void ShutdownApp() - { - if ((GPSApp.getCurrentTrack().getNumberOfLocations() > 0) - || (GPSApp.getCurrentTrack().getNumberOfPlacemarks() > 0) - || (GPSApp.getRecording()) - || (GPSApp.getPlacemarkRequest())) { + /** + * Performs all the operation in order to safely shutdown the Application: + * it check if a Track is active and, if needed, shows a confirmation + * dialog where asks to finalize the track. + */ + private void ShutdownApp() { + if ((gpsApp.getCurrentTrack().getNumberOfLocations() > 0) + || (gpsApp.getCurrentTrack().getNumberOfPlacemarks() > 0) + || (gpsApp.isRecording()) + || (gpsApp.isPlacemarkRequested())) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(getResources().getString(R.string.message_exit_finalizing)); builder.setIcon(android.R.drawable.ic_menu_info_details); builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - GPSApp.setRecording(false); - GPSApp.setPlacemarkRequest(false); + gpsApp.setRecording(false); + gpsApp.setPlacemarkRequested(false); EventBus.getDefault().post(EventBusMSG.NEW_TRACK); - GPSApp.StopAndUnbindGPSService(); - GPSApp.setLocationPermissionChecked(false); + gpsApp.stopAndUnbindGPSService(); + gpsApp.setLocationPermissionChecked(false); dialog.dismiss(); - GPSApp.setJustStarted(true); + gpsApp.setJustStarted(true); finish(); } }); @@ -438,36 +520,40 @@ public void onClick(DialogInterface dialog, int id) { }); builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - GPSApp.setRecording(false); - GPSApp.setPlacemarkRequest(false); - GPSApp.StopAndUnbindGPSService(); - GPSApp.setLocationPermissionChecked(false); + gpsApp.setRecording(false); + gpsApp.setPlacemarkRequested(false); + gpsApp.stopAndUnbindGPSService(); + gpsApp.setLocationPermissionChecked(false); dialog.dismiss(); - GPSApp.setJustStarted(true); + gpsApp.setJustStarted(true); finish(); } }); AlertDialog dialog = builder.create(); dialog.show(); } else { - GPSApp.setRecording(false); - GPSApp.setPlacemarkRequest(false); - GPSApp.StopAndUnbindGPSService(); - GPSApp.setLocationPermissionChecked(false); + gpsApp.setRecording(false); + gpsApp.setPlacemarkRequested(false); + gpsApp.stopAndUnbindGPSService(); + gpsApp.setLocationPermissionChecked(false); finish(); } } - private void ActivateActionModeIfNeeded() { + /** + * Activates the Action Mode upper Toolbar if needed. + * The Toolbar will be shown if the Tab 3 is active and one or more Tracks + * are selected into the Tracklist. + */ + private void activateActionModeIfNeeded() { runOnUiThread(new Runnable() { @Override public void run() { - if ((GPSApp.getNumberOfSelectedTracks() > 0) && (activeTab == 2)) { - if (actionMode == null) - actionMode = (startSupportActionMode(new ToolbarActionMode())); - actionMode.setTitle(GPSApp.getNumberOfSelectedTracks() > 1 ? String.valueOf(GPSApp.getNumberOfSelectedTracks()) : ""); + if ((gpsApp.getNumberOfSelectedTracks() > 0) && (gpsApp.getGPSActivityActiveTab() == 2)) { + if (actionMode == null) actionMode = (startSupportActionMode(new ToolbarActionMode())); + if (actionMode != null) actionMode.setTitle(gpsApp.getNumberOfSelectedTracks() > 1 ? String.valueOf(gpsApp.getNumberOfSelectedTracks()) : ""); } else if (actionMode != null) { actionMode.finish(); actionMode = null; @@ -476,8 +562,11 @@ public void run() { }); } - - public void CheckLocationPermission() { + /** + * Checks that the Location permission is granted. + * If not, requests it using the standard ActivityCompat.requestPermissions method. + */ + public void checkLocationPermission() { Log.w("myApp", "[#] GPSActivity.java - Check Location Permission..."); if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { Log.w("myApp", "[#] GPSActivity.java - Location Permission granted"); @@ -485,7 +574,7 @@ public void CheckLocationPermission() { } else { Log.w("myApp", "[#] GPSActivity.java - Location Permission denied"); boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION); - if (showRationale || !GPSApp.isLocationPermissionChecked()) { + if (showRationale || !gpsApp.isLocationPermissionChecked()) { Log.w("myApp", "[#] GPSActivity.java - Location Permission denied, need new check"); List listPermissionsNeeded = new ArrayList<>(); listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION); @@ -493,79 +582,4 @@ public void CheckLocationPermission() { } } } - - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - switch (requestCode) { - case REQUEST_ID_MULTIPLE_PERMISSIONS: { - Map perms = new HashMap<>(); - - if (grantResults.length > 0) { - // Fill with actual results from user - for (int i = 0; i < permissions.length; i++) perms.put(permissions[i], grantResults[i]); - // Check for permissions - if (perms.containsKey(Manifest.permission.ACCESS_FINE_LOCATION)) { - if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - Log.w("myApp", "[#] GPSActivity.java - ACCESS_FINE_LOCATION = PERMISSION_GRANTED; setGPSLocationUpdates!"); - GPSApp.setGPSLocationUpdates(false); - GPSApp.setGPSLocationUpdates(true); - GPSApp.updateGPSLocationFrequency(); - } else { - Log.w("myApp", "[#] GPSActivity.java - ACCESS_FINE_LOCATION = PERMISSION_DENIED"); - } - } - - if (perms.containsKey(Manifest.permission.INTERNET)) { - if (perms.get(Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED) { - Log.w("myApp", "[#] GPSActivity.java - INTERNET = PERMISSION_GRANTED"); - } else { - Log.w("myApp", "[#] GPSActivity.java - INTERNET = PERMISSION_DENIED"); - } - } - - if (perms.containsKey(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - if (perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - Log.w("myApp", "[#] GPSActivity.java - WRITE_EXTERNAL_STORAGE = PERMISSION_GRANTED"); - // ---------------------------------------------------- Create the Directories if not exist - File sd = new File(Environment.getExternalStorageDirectory() + "/GPSLogger"); - if (!sd.exists()) { - sd.mkdir(); - } - sd = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); - if (!sd.exists()) { - sd.mkdir(); - } - sd = new File(getApplicationContext().getFilesDir() + "/Thumbnails"); - if (!sd.exists()) { - sd.mkdir(); - } - EGM96 egm96 = EGM96.getInstance(); - if (egm96 != null) { - if (!egm96.isEGMGridLoaded()) { - //Log.w("myApp", "[#] GPSApplication.java - Loading EGM Grid..."); - egm96.LoadGridFromFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/WW15MGH.DAC", getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); - } - } - - if (GPSApp.getJobsPending() > 0) GPSApp.ExecuteJob(); - - } else { - Log.w("myApp", "[#] GPSActivity.java - WRITE_EXTERNAL_STORAGE = PERMISSION_DENIED"); - if (GPSApp.getJobsPending() > 0) { - // Shows toast "Unable to write the file" - show_toast_grant_storage_permission = true; - EventBus.getDefault().post(EventBusMSG.TOAST_STORAGE_PERMISSION_REQUIRED); - GPSApp.setJobsPending(0); - } - } - } - } - break; - } - default: { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - } - } - } -} +} \ No newline at end of file diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSApplication.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSApplication.java index 00dbaca3..e97801c4 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSApplication.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSApplication.java @@ -1,6 +1,9 @@ -/** +/* * GPSApplication - Java Class for Android - * Created by G.Capelli (BasicAirData) on 20/5/2016 + * Created by G.Capelli on 20/5/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -66,7 +69,9 @@ import org.greenrobot.eventbus.Subscribe; import java.io.File; +import java.io.FileFilter; import java.io.FileOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -84,20 +89,22 @@ public class GPSApplication extends Application implements LocationListener { //private static final int UM_IMPERIAL_FPS = 8; //private static final int UM_IMPERIAL_MPH = 9; - private static final int STABILIZERVALUE = 3000; // The application discards fixes for 3000 ms (minimum) - private static final int DEFAULTHANDLERTIMER = 5000; // The timer for turning off GPS on exit - private static final int GPSUNAVAILABLEHANDLERTIMER = 7000; // The "GPS temporary unavailable" timer - private int StabilizingSamples = 3; + private static final int STABILIZER_TIME = 3000; // The application discards fixes for 3000 ms (minimum) + private static final int DEFAULT_SWITCHOFF_HANDLER_TIME = 5000; // Default time for turning off GPS on exit + private static final int GPS_UNAVAILABLE_HANDLER_TIME = 7000; // The "GPS temporary unavailable" time + + private static final int MAX_ACTIVE_EXPORTER_THREADS = 3; // The maximum number of Exporter threads to run simultaneously + private static final int EXPORTING_STATUS_CHECK_INTERVAL = 16; // The app updates the progress of exportation every 16 milliseconds - public static final int GPS_DISABLED = 0; - public static final int GPS_OUTOFSERVICE = 1; - public static final int GPS_TEMPORARYUNAVAILABLE = 2; - public static final int GPS_SEARCHING = 3; - public static final int GPS_STABILIZING = 4; - public static final int GPS_OK = 5; + public static final int GPS_DISABLED = 0; + public static final int GPS_OUTOFSERVICE = 1; + public static final int GPS_TEMPORARYUNAVAILABLE = 2; + public static final int GPS_SEARCHING = 3; + public static final int GPS_STABILIZING = 4; + public static final int GPS_OK = 5; public static final int APP_ORIGIN_NOT_SPECIFIED = 0; - public static final int APP_ORIGIN_GOOGLE_PLAY_STORE = 1; // The app is installed via the Google Play Store + public static final int APP_ORIGIN_GOOGLE_PLAY_STORE = 1; // The app is installed via the Google Play Store public static final int JOB_TYPE_NONE = 0; // No operation public static final int JOB_TYPE_EXPORT = 1; // Bulk Exportation @@ -105,240 +112,254 @@ public class GPSApplication extends Application implements LocationListener { public static final int JOB_TYPE_SHARE = 3; // Bulk Share public static final int JOB_TYPE_DELETE = 4; // Bulk Delete - public static final String FLAG_RECORDING = "flagRecording"; // The persistent Flag is set when the app is recording, in order to detect Background Crashes - public static final String FILETYPE_KML = ".kml"; - public static final String FILETYPE_GPX = ".gpx"; + private static final String TASK_SHUTDOWN = "TASK_SHUTDOWN"; // The AsyncTodo Type to Shut down the DB connection + private static final String TASK_NEWTRACK = "TASK_NEWTRACK"; // The AsyncTodo Type to create a new track into DB + private static final String TASK_ADDLOCATION = "TASK_ADDLOCATION"; // The AsyncTodo Type to create a new track into DB + private static final String TASK_ADDPLACEMARK = "TASK_ADDPLACEMARK"; // The AsyncTodo Type to create a new placemark into DB + private static final String TASK_UPDATEFIX = "TASK_UPDATEFIX"; // The AsyncTodo Type to update the current FIX + private static final String TASK_DELETETRACKS = "TASK_DELETETRACKS"; // The AsyncTodo Type to delete some tracks + + public static final String FLAG_RECORDING = "flagRecording"; // The persistent Flag is set when the app is recording, in order to detect Background Crashes + public static final String FILETYPE_KML = ".kml"; + public static final String FILETYPE_GPX = ".gpx"; private static final float[] NEGATIVE = { - -1.0f, 0, 0, 0, 248, // red - 0, -1.0f, 0, 0, 248, // green - 0, 0, -1.0f, 0, 248, // blue - 0, 0, 0, 1.00f, 0 // alpha + -1.0f, 0, 0, 0, 248, // red + 0, -1.0f, 0, 0, 248, // green + 0, 0, -1.0f, 0, 248, // blue + 0, 0, 0, 1.00f, 0 // alpha }; - public static final ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(NEGATIVE); + public static final ColorMatrixColorFilter colorMatrixColorFilter + = new ColorMatrixColorFilter(NEGATIVE); // The color filter for Track thumbnails + + public static int TOAST_VERTICAL_OFFSET ; // The Y offset, in dp, for Toasts + public static String DIRECTORY_TEMP; // The directory to store temporary tracks. Currently /GPSLogger/AppData + public static String DIRECTORY_EXPORT; // The directory where the app exports tracks. Currently /GPSLogger + public static String DIRECTORY_FILESDIR_TRACKS; // The directory FilesDir/Tracks + public static String FILE_EMPTY_GPX; + public static String FILE_EMPTY_KML; // Preferences Variables - // private boolean prefKeepScreenOn = true; // DONE in GPSActivity - private boolean prefShowDecimalCoordinates = false; - // private int prefViewTracksWith = 0; - private int prefUM = UM_METRIC_KMH; - private float prefGPSdistance = 0f; - private long prefGPSupdatefrequency = 1000L; - private boolean prefEGM96AltitudeCorrection = false; - private double prefAltitudeCorrection = 0d; - private boolean prefExportKML = true; - private boolean prefExportGPX = true; - private int prefGPXVersion = 100; // the version of the GPX schema - private boolean prefExportTXT = false; - private int prefKMLAltitudeMode = 0; - private int prefShowTrackStatsType = 0; - private int prefShowDirections = 0; - private boolean prefGPSWeekRolloverCorrected= false; - private boolean prefShowLocalTime = true; - - private boolean LocationPermissionChecked = false; // If the flag is false the GPSActivity will check for Location Permission - private boolean isFirstRun = false; // True if it is the first run of the app (the DB is empty) - private boolean isJustStarted = true; // True if the application has just been started - private boolean isMockProvider = false; // True if the location is from mock provider - - private boolean isBackgroundActivityRestricted = false; // True if the App is Background Restricted - - private LocationExtended PrevFix = null; - private boolean isPrevFixRecorded = false; - - private MyGPSStatus myGPSStatusListener; - - private LocationExtended PrevRecordedFix = null; - - private boolean MustUpdatePrefs = true; // True if preferences needs to be updated - - private boolean isCurrentTrackVisible = false; - private boolean isContextMenuShareVisible = false; // True if "Share with ..." menu is visible - private boolean isContextMenuViewVisible = false; // True if "View in *" menu is visible - private Drawable ViewInAppIcon = null; - private String ViewInApp = ""; // The string of default app name for "View" - // "" in case of selector - - private AppInfo TrackViewer = new AppInfo(); - - // Singleton instance + private boolean prefShowDecimalCoordinates; // If true the coordinates are shows in decimal notation + private int prefUM = UM_METRIC_KMH; // The units of measurement to use for visualization + private float prefGPSdistance = 0f; // The distance filter value + private long prefGPSupdatefrequency = 1000L; // The GPS Update frequency in milliseconds + private boolean prefEGM96AltitudeCorrection; // True if the EGM96 altitude correction is active + private double prefAltitudeCorrection = 0d; // The manual offset for the altitude correction, in meters + private boolean prefExportKML = true; // If true the KML file are exported on Share/Export + private boolean prefExportGPX = true; // If true the GPX file are exported on Share/Export + private int prefGPXVersion = 100; // The version of the GPX schema + private boolean prefExportTXT; // If true the TXT file are exported on Share/Export + private int prefKMLAltitudeMode = 0; // The altitude mode for KML files: 1="clampToGround"; 0="absolute" + private int prefShowTrackStatsType = 0; // What shown stats are based on: 0="Total time"; 1="Time in movement" + private int prefShowDirections = 0; // Visualization of headings: 0="NSWE"; 1="Degrees" + private boolean prefGPSWeekRolloverCorrected; // A flag for Week Rollover correction + private boolean prefShowLocalTime = true; // I true the app shows GPS Time instead of local time + + private boolean mustUpdatePrefs = true; // True if preferences needs to be updated + + private boolean isLocationPermissionChecked; // If the flag is false the GPSActivity will check for Location Permission + private boolean isFirstRun; // True if it is the first run of the app (the DB is empty) + private boolean isJustStarted = true; // True if the application has just been started + private boolean isMockProvider; // True if the location is from mock provider + private boolean isScreenOn = true; // True if the screen of the device is ON + private boolean isBackgroundActivityRestricted; // True if the App is Background Restricted + + private LocationExtended prevFix = null; // The previous fix + private LocationExtended prevRecordedFix = null; // The previous recorded fix + private boolean isPrevFixRecorded; // true if the previous fix has been recorded + private boolean isFirstFixFound; // True if at less one fix has been obtained + + private MyGPSStatus gpsStatusListener; // The listener for the GPS Status changes events + + private boolean isCurrentTrackVisible; // If true the current track is visible in Tracklist + private boolean isContextMenuShareVisible; // True if "Share with ..." menu is visible + private boolean isContextMenuViewVisible; // True if "View in *" menu is visible + private Drawable viewInAppIcon = null; // The icon of the default app used as viewer + private String viewInApp = ""; // The string of default app name for "View"; "" in case of selector + + private boolean isSpaceForExtraTilesAvailable = true; // True if there is space to show Time and Satellites in GPS Fix Tab; + + // Variables for multiple selection on Tracklist + private long lastClickId = NOT_AVAILABLE; // The last item clicked on Tracklist + private boolean lastClickState; // The state of the last item clicked on Tracklist + + private ExternalViewer trackViewer = new ExternalViewer(); // The class that makes and manage the list of external track viewers + private final Satellites satellites = new Satellites(); // The class that contains all the information about satellites + DatabaseHandler gpsDataBase; // The handler for the GPSLogger Database of Tracks + + private String placemarkDescription = ""; // The description of the Placemark (annotation) set by PlacemarkDialog + private boolean isPlacemarkRequested; // True if the user requested to add a placemark (Annotation) + private boolean isRecording; // True if the recording is active + private boolean isBottomBarLocked; // True if the bottom bar is locked + private boolean isGPSLocationUpdatesActive; // True if the Location Manager is active (is requesting FIXes) + private int gpsStatus = GPS_SEARCHING; // The status of the GPS: GPS_DISABLED, GPS_OUTOFSERVICE, + // GPS_TEMPORARYUNAVAILABLE, GPS_SEARCHING, GPS_STABILIZING; + private int appOrigin = APP_ORIGIN_NOT_SPECIFIED; // Which package manager is used to install this app (for Rate button visualization): + // APP_ORIGIN_NOT_SPECIFIED, APP_ORIGIN_GOOGLE_PLAY_STORE + private LocationManager locationManager = null; // GPS LocationManager + private int numberOfSatellitesTotal = 0; // The total Number of Satellites + private int numberOfSatellitesUsedInFix = 0; // The Number of Satellites used in Fix + + private int gpsActivityActiveTab = 1; // The active tab on GPSActivity + private int jobProgress = 0; + private int jobsPending = 0; // The number of jobs to be done + public int jobType = JOB_TYPE_NONE; // The type of job that is pending + private boolean deleteAlsoExportedFiles; // When true, the deletion of some tracks will delete also the exported files of the tracks + + private int numberOfStabilizationSamples = 3; + private int stabilizer = numberOfStabilizationSamples; // The number of stabilization FIXes before the first valid Location + private int handlerTime = DEFAULT_SWITCHOFF_HANDLER_TIME; // The time for the GPS update requests deactivation + + private LocationExtended currentLocationExtended = null; // The current Location + private LocationExtended currentPlacemark = null; // The location used to add the Placemark (Annotation) + private Track currentTrack = null; // The current track. Used for adding Trackpoints and Annotations + private Track trackToEdit = null; // The Track that the user selected to edit with the "Track Properties" Dialog + + private final List arrayListTracks + = Collections.synchronizedList(new ArrayList()); // The list of Tracks + + private final List exportingTaskList + = new ArrayList<>(); // The list of Exporting Tasks + + private AsyncPrepareActionmodeToolbar asyncPrepareActionmodeToolbar; // Prepares the Action Mode menu asynchronously + private ExternalViewerChecker externalViewerChecker; // The manager of the External Viewers + BroadcastReceiver broadcastReceiver = new ActionsBroadcastReceiver(); // The BroadcastReceiver for SHUTDOWN and SCREEN_ON/OFF events + + Thumbnailer thumbnailer; // It creates the Thumbnails of the Tracks asynchronously + Exporter exporter; // It exports the Tracks + private final AsyncUpdateThreadClass asyncUpdateThread = new AsyncUpdateThreadClass(); + + // ---------------------------------------------------------------------- Singleton instance + private static GPSApplication singleton; public static GPSApplication getInstance(){ return singleton; } - private Satellites satellites; + // ---------------------------------------------------------------------- Handlers and Runnables - private boolean isScreenOn = true; - private long lastClickId = NOT_AVAILABLE; - private boolean lastClickState = false; - - DatabaseHandler GPSDataBase; - private String PlacemarkDescription = ""; - private boolean Recording = false; - private boolean PlacemarkRequest = false; - private boolean isGPSLocationUpdatesActive = false; - private int GPSStatus = GPS_SEARCHING; - - private int AppOrigin = APP_ORIGIN_NOT_SPECIFIED; // Which package manager is used to install this app - - private boolean NewTrackFlag = false; // The variable that handle the double-click on "Track Finished" - final Handler newtrackhandler = new Handler(); - Runnable newtrackr = new Runnable() { + // The Handler that prevents a double click of the Stop button of the bottom bar + private boolean isStopButtonFlag; // True if the Stop button has been clicked + final Handler stopButtonHandler = new Handler(); + Runnable stopButtonRunnable = new Runnable() { @Override public void run() { - NewTrackFlag = false; + isStopButtonFlag = false; + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); } }; - private LocationManager mlocManager = null; // GPS LocationManager - private int numberOfSatellitesTotal = 0; // The total Number of Satellites - private int numberOfSatellitesUsedInFix = 0; // The Number of Satellites used in Fix - - private int GPSActivity_activeTab = 0; // The active tab on GPSActivity - private int JobProgress = 0; - private int JobsPending = 0; // The number of jobs to be done - public int JobType = JOB_TYPE_NONE; // The type of job that is pending - private boolean DeleteAlsoExportedFiles = false; // When true, the deletion of some tracks will delete also the exported files of the tracks - - private int _Stabilizer = StabilizingSamples; - private int HandlerTimer = DEFAULTHANDLERTIMER; - - private LocationExtended _currentLocationExtended = null; - private LocationExtended _currentPlacemark = null; - private Track _currentTrack = null; - private final List _ArrayListTracks = Collections.synchronizedList(new ArrayList()); - - Thumbnailer Th; - Exporter Ex; - private final AsyncUpdateThreadClass asyncUpdateThread = new AsyncUpdateThreadClass(); - - // The handler that switches off the location updates after a time delay: - final Handler handler = new Handler(); - Runnable r = new Runnable() { - + // The Handler that switches off the location updates after a time delay: + final Handler disableLocationUpdatesHandler = new Handler(); + Runnable disableLocationUpdatesRunnable = new Runnable() { @Override public void run() { setGPSLocationUpdates(false); } }; - final Handler gpsunavailablehandler = new Handler(); - Runnable unavailr = new Runnable() { - + // The Handler that sets the GPS Status to GPS_TEMPORARYUNAVAILABLE + final Handler gpsUnavailableHandler = new Handler(); + Runnable gpsUnavailableRunnable = new Runnable() { @Override public void run() { - if ((GPSStatus == GPS_OK) || (GPSStatus == GPS_STABILIZING)) { - GPSStatus = GPS_TEMPORARYUNAVAILABLE; + if ((gpsStatus == GPS_OK) || (gpsStatus == GPS_STABILIZING)) { + gpsStatus = GPS_TEMPORARYUNAVAILABLE; EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); } } }; - private static final int MAX_ACTIVE_EXPORTER_THREADS = 3; // The maximum number of Exporter threads to run simultaneously - - private List ExportingTaskList = new ArrayList<>(); - - private AsyncPrepareTracklistContextMenu asyncPrepareTracklistContextMenu; - private ExternalViewerChecker externalViewerChecker; // The manager of the External Viewers - - BroadcastReceiver sReceiver = new ActionsBroadcastReceiver(); // The BroadcastReceiver for SHUTDOWN event - - - // The handler that checks the progress of an exportation: - private static final int ExportingStatusCheckInterval = 16; // The app updates the progress of exportation every 16 milliseconds - final Handler ExportingStatusCheckHandler = new Handler(); - - Runnable ExportingStatusChecker = new Runnable() { + // The Handler that checks the progress of an exportation + final Handler exportingStatusCheckHandler = new Handler(); + Runnable exportingStatusCheckRunnable = new Runnable() { @Override public void run() { - long Total = 0; - long Progress = 0; - int Exporters_Total = ExportingTaskList.size(); // The total amount of exportation into the current job - int Exporters_Pending = 0; - int Exporters_Running = 0; // The amount of exportation in progress - int Exporters_Success = 0; // The amount of exportation finished with success - int Exporters_Failed = 0; // The amount of exportation failed - - - // Check Progress - for (ExportingTask ET : ExportingTaskList) { - Total += ET.getNumberOfPoints_Total(); - Progress += ET.getNumberOfPoints_Processed(); - if (ET.getStatus() == ExportingTask.STATUS_PENDING) Exporters_Pending++; - if (ET.getStatus() == ExportingTask.STATUS_RUNNING) Exporters_Running++; - if (ET.getStatus() == ExportingTask.STATUS_ENDED_SUCCESS) Exporters_Success++; - if (ET.getStatus() == ExportingTask.STATUS_ENDED_FAILED) Exporters_Failed++; + long total = 0; + long progress = 0; + int exportersTotal = exportingTaskList.size(); // The total amount of exportation into the current job + int exportersPending = 0; + int exportersRunning = 0; // The amount of exportation in progress + int exportersSuccess = 0; // The amount of exportation finished with success + int exportersFailed = 0; // The amount of exportation failed + + // Check progress + for (ExportingTask et : exportingTaskList) { + total += et.getNumberOfPoints_Total(); + progress += et.getNumberOfPoints_Processed(); + if (et.getStatus() == ExportingTask.STATUS_PENDING) exportersPending++; + if (et.getStatus() == ExportingTask.STATUS_RUNNING) exportersRunning++; + if (et.getStatus() == ExportingTask.STATUS_ENDED_SUCCESS) exportersSuccess++; + if (et.getStatus() == ExportingTask.STATUS_ENDED_FAILED) exportersFailed++; } // Update job progress - if (Total != 0) { - if (JobProgress != (int) Math.round(1000L * Progress / Total)) { // The ProgressBar on FragmentJobProgress has android:max="1000" - JobProgress = (int) Math.round(1000L * Progress / Total); + if (total != 0) { + if (jobProgress != (int) Math.round(1000L * progress / total)) { // The ProgressBar on FragmentJobProgress has android:max="1000" + jobProgress = (int) Math.round(1000L * progress / total); EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); } } else { - if (JobProgress != 0) { - JobProgress = 0; + if (jobProgress != 0) { + jobProgress = 0; EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); } } - //Log.w("myApp", "[#] GPSApplication.java - ExportingStatusChecker running: " + 100*Progress/Total + "% - P " - // + Exporters_Pending + " - R " + Exporters_Running + " - S " + Exporters_Success + " - F " + Exporters_Failed); + //Log.w("myApp", "[#] GPSApplication.java - ExportingStatusChecker running: " + 100*progress/total + "% - P " + // + exportersPending + " - R " + exportersRunning + " - S " + exportersSuccess + " - F " + exportersFailed); // Exportation Failed - if (Exporters_Failed != 0) { + if (exportersFailed != 0) { EventBus.getDefault().post(EventBusMSG.TOAST_UNABLE_TO_WRITE_THE_FILE); - JobProgress = 0; - JobsPending = 0; + jobProgress = 0; + jobsPending = 0; EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); return; } // Exportation Finished - if (Exporters_Success == Exporters_Total) { - if (JobType == JOB_TYPE_VIEW) { - if (!ExportingTaskList.isEmpty()) ViewTrack(ExportingTaskList.get(0)); - } else if (JobType == JOB_TYPE_SHARE) { + if (exportersSuccess == exportersTotal) { + if (jobType == JOB_TYPE_VIEW) { + if (!exportingTaskList.isEmpty()) viewTrack(exportingTaskList.get(0)); + } else if (jobType == JOB_TYPE_SHARE) { EventBus.getDefault().post(EventBusMSG.INTENT_SEND); } else { EventBus.getDefault().post(EventBusMSG.TOAST_TRACK_EXPORTED); } - JobProgress = 0; - JobsPending = 0; + jobProgress = 0; + jobsPending = 0; EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); return; } // If needed, run another Exportation Thread - if ((Exporters_Running < MAX_ACTIVE_EXPORTER_THREADS) && (Exporters_Pending > 0)) { - for (ExportingTask ET : ExportingTaskList) { - if (ET.getStatus() == ExportingTask.STATUS_PENDING) { - //Log.w("myApp", "[#] GPSApplication.java - Run the export thread nr." + Exporters_Running + ": " + ET.getId()); - ET.setStatus(ExportingTask.STATUS_RUNNING); - ExecuteExportingTask(ET); + if ((exportersRunning < MAX_ACTIVE_EXPORTER_THREADS) && (exportersPending > 0)) { + for (ExportingTask et : exportingTaskList) { + if (et.getStatus() == ExportingTask.STATUS_PENDING) { + //Log.w("myApp", "[#] GPSApplication.java - Run the export thread nr." + exportersRunning + ": " + et.getId()); + et.setStatus(ExportingTask.STATUS_RUNNING); + executeExportingTask(et); break; } } } - ExportingStatusCheckHandler.postDelayed(ExportingStatusChecker, ExportingStatusCheckInterval); + exportingStatusCheckHandler.postDelayed(exportingStatusCheckRunnable, EXPORTING_STATUS_CHECK_INTERVAL); } }; - void startExportingStatusChecker() { - ExportingStatusChecker.run(); - } - -// void stopExportingStatusChecker() { -// ExportingStatusCheckHandler.removeCallbacks(ExportingStatusChecker); -// } - + // ---------------------------------------------------------------------- GPSStatus - // ------------------------------------------------------------------------------------ GPSStatus + /** + * The Class that manages the GPS Status, using the appropriate methods + * depending on the Android Version. + * - For VERSION_CODES > N it uses the new GnssStatus.Callback; + * - For older Android it uses the legacy GpsStatus.Listener; + */ private class MyGPSStatus { private GpsStatus.Listener gpsStatusListener; private GnssStatus.Callback mGnssStatusListener; @@ -366,34 +387,39 @@ public void onGpsStatusChanged(int event) { } } + /** + * Enables the GPS Status listener + */ public void enable() { if (ContextCompat.checkSelfPermission(GPSApplication.getInstance(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) mlocManager.registerGnssStatusCallback(mGnssStatusListener); - else mlocManager.addGpsStatusListener(gpsStatusListener); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) locationManager.registerGnssStatusCallback(mGnssStatusListener); + else locationManager.addGpsStatusListener(gpsStatusListener); } } + /** + * Disables the GPS Status listener + */ public void disable() { if (ContextCompat.checkSelfPermission(GPSApplication.getInstance(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) mlocManager.unregisterGnssStatusCallback(mGnssStatusListener); - else mlocManager.removeGpsStatusListener(gpsStatusListener); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) locationManager.unregisterGnssStatusCallback(mGnssStatusListener); + else locationManager.removeGpsStatusListener(gpsStatusListener); } } } + // ---------------------------------------------------------------------- Foreground Service - // ------------------------------------------------------------------------------------ Service - Intent GPSServiceIntent; - GPSService GPSLoggerService; - boolean isGPSServiceBound = false; - - private ServiceConnection GPSServiceConnection = new ServiceConnection() { + Intent gpsServiceIntent; // The intent for GPSService + GPSService gpsService; // The Foreground Service that keeps the app alive in Background + boolean isGPSServiceBound = false; // True if the GPSService is bound + private final ServiceConnection gpsServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { GPSService.LocalBinder binder = (GPSService.LocalBinder) service; - GPSLoggerService = binder.getServiceInstance(); //Get instance of your service! + gpsService = binder.getServiceInstance(); //Get instance of your service! Log.w("myApp", "[#] GPSApplication.java - GPSSERVICE CONNECTED - onServiceConnected event"); isGPSServiceBound = true; } @@ -405,19 +431,21 @@ public void onServiceDisconnected(ComponentName arg0) { } }; - private void StartAndBindGPSService() { - GPSServiceIntent = new Intent(GPSApplication.this, GPSService.class); + /** + * Starts and Binds to the Foreground Service GPSService + */ + private void startAndBindGPSService() { + gpsServiceIntent = new Intent(GPSApplication.this, GPSService.class); //Start the service - startService(GPSServiceIntent); + startService(gpsServiceIntent); //Bind to the service if (Build.VERSION.SDK_INT >= 14) - bindService(GPSServiceIntent, GPSServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT); + bindService(gpsServiceIntent, gpsServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT); else - bindService(GPSServiceIntent, GPSServiceConnection, Context.BIND_AUTO_CREATE); + bindService(gpsServiceIntent, gpsServiceConnection, Context.BIND_AUTO_CREATE); Log.w("myApp", "[#] GPSApplication.java - StartAndBindGPSService"); } - /* private void UnbindGPSService() { //UNUSED try { unbindService(GPSServiceConnection); //Unbind to the service @@ -427,35 +455,38 @@ private void StartAndBindGPSService() { } } */ - public void StopAndUnbindGPSService() { + /** + * Stops and Unbinds to the Foreground Service GPSService + */ + public void stopAndUnbindGPSService() { try { - unbindService(GPSServiceConnection); //Unbind to the service + unbindService(gpsServiceConnection); //Unbind to the service Log.w("myApp", "[#] GPSApplication.java - Service unbound"); } catch (Exception e) { Log.w("myApp", "[#] GPSApplication.java - Unable to unbind the GPSService"); } try { - stopService(GPSServiceIntent); //Stop the service + stopService(gpsServiceIntent); //Stop the service Log.w("myApp", "[#] GPSApplication.java - Service stopped"); } catch (Exception e) { Log.w("myApp", "[#] GPSApplication.java - Unable to stop GPSService"); } } + // ---------------------------------------------------------------------- Getters and Setters - // ------------------------------------------------------------------------ Getters and Setters - public boolean getNewTrackFlag() { - return NewTrackFlag; + public boolean isStopButtonFlag() { + return isStopButtonFlag; } - public void setNewTrackFlag(boolean newTrackFlag) { - if (newTrackFlag) { - NewTrackFlag = true; - newtrackhandler.removeCallbacks(newtrackr); // Cancel the previous newtrackr handler - newtrackhandler.postDelayed(newtrackr, 1500); // starts the new handler + public void setStopButtonFlag(boolean stopFlag, long millis) { + if (stopFlag) { + this.isStopButtonFlag = true; + stopButtonHandler.removeCallbacks(stopButtonRunnable); // Cancel the previous handler + stopButtonHandler.postDelayed(stopButtonRunnable, millis); // starts the new handler } else { - NewTrackFlag = false; - newtrackhandler.removeCallbacks(newtrackr); // Cancel the previous newtrackr handler + this.isStopButtonFlag = false; + stopButtonHandler.removeCallbacks(stopButtonRunnable); // Cancel the previous handler } } @@ -468,19 +499,19 @@ public boolean isContextMenuViewVisible() { } public String getViewInApp() { - return ViewInApp; + return viewInApp; } public Drawable getViewInAppIcon() { - return ViewInAppIcon; + return viewInAppIcon; } public boolean isLocationPermissionChecked() { - return LocationPermissionChecked; + return isLocationPermissionChecked; } public void setLocationPermissionChecked(boolean locationPermissionChecked) { - LocationPermissionChecked = locationPermissionChecked; + isLocationPermissionChecked = locationPermissionChecked; } public long getLastClickId() { @@ -499,16 +530,16 @@ public void setLastClickState(boolean lastClickState) { this.lastClickState = lastClickState; } - public void setHandlerTimer(int handlerTimer) { - HandlerTimer = handlerTimer; + public void setHandlerTime(int handlerTime) { + this.handlerTime = handlerTime; } - public int getHandlerTimer() { - return HandlerTimer; + public int getHandlerTime() { + return handlerTime; } public int getGPSStatus() { - return GPSStatus; + return gpsStatus; } public int getPrefKMLAltitudeMode() { @@ -560,15 +591,15 @@ public boolean getPrefShowLocalTime() { } public LocationExtended getCurrentLocationExtended() { - return _currentLocationExtended; + return currentLocationExtended; } public void setPlacemarkDescription(String Description) { - this.PlacemarkDescription = Description; + this.placemarkDescription = Description; } public Track getCurrentTrack() { - return _currentTrack; + return currentTrack; } public int getNumberOfSatellitesTotal() { @@ -579,30 +610,39 @@ public int getNumberOfSatellitesUsedInFix() { return numberOfSatellitesUsedInFix; } - public boolean getRecording() { - return Recording; + public boolean isRecording() { + return isRecording; } public void setRecording(boolean recordingState) { - PrevRecordedFix = null; - Recording = recordingState; - if (Recording) FlagAdd(FLAG_RECORDING); - else FlagRemove(FLAG_RECORDING); + prevRecordedFix = null; + isRecording = recordingState; + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + if (isRecording) addPreferenceFlag_NoBackup(FLAG_RECORDING); + else clearPreferenceFlag_NoBackup(FLAG_RECORDING); } - public boolean getPlacemarkRequest() { return PlacemarkRequest; } + public boolean isPlacemarkRequested() { return isPlacemarkRequested; } + + public void setPlacemarkRequested(boolean placemarkRequested) { this.isPlacemarkRequested = placemarkRequested; } - public void setPlacemarkRequest(boolean placemarkRequest) { PlacemarkRequest = placemarkRequest; } + public boolean isBottomBarLocked() { + return isBottomBarLocked; + } + + public void setBottomBarLocked(boolean locked) { + isBottomBarLocked = locked; + } public List getTrackList() { - return _ArrayListTracks; + return arrayListTracks; } public boolean isCurrentTrackVisible() { return isCurrentTrackVisible; } - public void setisCurrentTrackVisible(boolean currentTrackVisible) { + public void setCurrentTrackVisible(boolean currentTrackVisible) { isCurrentTrackVisible = currentTrackVisible; } @@ -611,35 +651,35 @@ public boolean isBackgroundActivityRestricted() { } public int getAppOrigin() { - return AppOrigin; + return appOrigin; } public int getJobProgress() { - return JobProgress; + return jobProgress; } public int getJobsPending() { - return JobsPending; + return jobsPending; } public void setJobsPending(int jobsPending) { - JobsPending = jobsPending; + this.jobsPending = jobsPending; } - public int getGPSActivity_activeTab() { - return GPSActivity_activeTab; + public int getGPSActivityActiveTab() { + return gpsActivityActiveTab; } - public void setGPSActivity_activeTab(int GPSActivity_activeTab) { - this.GPSActivity_activeTab = GPSActivity_activeTab; + public void setGPSActivityActiveTab(int gpsActivityActiveTab) { + this.gpsActivityActiveTab = gpsActivityActiveTab; } public List getExportingTaskList() { - return ExportingTaskList; + return exportingTaskList; } public void setDeleteAlsoExportedFiles(boolean deleteAlsoExportedFiles) { - DeleteAlsoExportedFiles = deleteAlsoExportedFiles; + this.deleteAlsoExportedFiles = deleteAlsoExportedFiles; } public boolean isJustStarted() { @@ -654,13 +694,58 @@ public ExternalViewerChecker getExternalViewerChecker() { return externalViewerChecker; } - public void setTrackViewer(AppInfo trackViewer) { - TrackViewer = trackViewer; + public void setTrackViewer(ExternalViewer trackViewer) { + this.trackViewer = trackViewer; + } + + public Track getTrackToEdit() { + return trackToEdit; + } + + public void setTrackToEdit(Track trackToEdit) { + this.trackToEdit = trackToEdit; } - // ------------------------------------------------------------------------ Utility + public boolean isFirstFixFound() { + return isFirstFixFound; + } + + public boolean isSpaceForExtraTilesAvailable() { + return isSpaceForExtraTilesAvailable; + } + + public void setSpaceForExtraTilesAvailable(boolean spaceForExtraTilesAvailable) { + isSpaceForExtraTilesAvailable = spaceForExtraTilesAvailable; + } + + // ---------------------------------------------------------------------- Utilities + +// /** +// * Converts dp unit to equivalent pixels, depending on device density. +// * +// * @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels +// * @return A float value to represent px equivalent to dp depending on device density +// */ +// public float convertDpToPx(float dp) { +// return dp * getResources().getDisplayMetrics().density; +// } +// +// /** +// * Converts device specific pixels to density independent pixels. +// * +// * @param px A value in px (pixels) unit. Which we need to convert into dp +// * @return A float value to represent dp equivalent to px value +// */ +// public float convertPxToDp(Context context, float px) { +// return px / getResources().getDisplayMetrics().density; +// } - private void DeleteFile(String filename) { + /** + * Deletes the file with the given filename. + * + * @param filename The name of the file, including the full path + */ + private void fileDelete(String filename) { File file = new File(filename); boolean deleted; if (file.exists ()) { @@ -671,60 +756,128 @@ private void DeleteFile(String filename) { else Log.w("myApp", "[#] GPSApplication.java - DeleteFile: " + filename + " doesn't exists"); } + /** + * Finds all the files in a folder that have the name starting with a specified string. + * + * @param path The folder to search into + * @param nameStart The starting characters + */ + public File[] fileFind(String path, final String nameStart) { + File _path = new File(path); + try { + return _path.listFiles(new FileFilter() { + @Override + public boolean accept(File file) { + String name = file.getName(); //.toLowerCase(); + return name.startsWith(nameStart); + } + }); + } catch (Exception e) { + return null; + } + } - /* NOT USED, Commented out - private boolean FileExists(String filename) { - File file = new File(filename); - return file.exists (); - } */ - + /** + * Convert the input string into a valid string for a file name + * + * @param str The desired file name, without path and extension + * @return a valid string for a filename, basing on the input string + */ + private String stringToDescFileName(String str) { + if ((str == null) || str.isEmpty()) return ""; + // Remove the \ / : * ? " < > | + // Remove heading and trailing spaces + String sName = str.substring(0, Math.min(128, str.length())) + .replace("\\","_") + .replace("/","_") + .replace(":","_") + .replace(".","_") + .replace("*","_") + .replace("?","_") + .replace("\"","_") + .replace("<","_") + .replace(">","_") + .replace("|","_") + .trim(); + if (sName.isEmpty()) return ""; + else return sName; + } + + /** + * Extracts the filename starting from a Track, basing on name and description. + * + * @param track The desired Track + * @return a valid string for a filename (without .extension) + */ + public String getFileName(Track track) { + if (track.getDescription().isEmpty()) + return track.getName(); + else + // Adds the separator and returns the filename = name - description (without .extension) + return track.getName() + " - " + stringToDescFileName(track.getDescription()); + } - // Flags are Boolean SharedPreferences that are excluded by automatic Backups + // ---------------------------------------------------------------------- Preferences Excluded from Backup + // These are Boolean SharedPreferences that are excluded by automatic Backups - public void FlagAdd (String flag) { + /** + * Adds a boolean preference (excluded from backup). + * This kind of Preference is used to store certain Flags, like the recording state. + * + * @param flag The name of the flag + */ + public void addPreferenceFlag_NoBackup(String flag) { SharedPreferences preferences_nobackup = getSharedPreferences("prefs_nobackup",Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences_nobackup.edit(); editor.putBoolean(flag, true); editor.commit(); } - - public void FlagRemove (String flag) { + /** + * Removes a boolean preference (excluded from backup). + * This kind of Preference is used to store certain Flags, like the recording state. + * + * @param flag The name of the flag + */ + public void clearPreferenceFlag_NoBackup(String flag) { SharedPreferences preferences_nobackup = getSharedPreferences("prefs_nobackup", Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences_nobackup.edit(); editor.remove(flag); editor.commit(); } - - public boolean FlagExists (String flag) { + /** + * Checks if a boolean preference (excluded from backup) exists. + * This kind of Preference is used to store certain Flags, like the recording state. + * + * @param flag The name of the flag + */ + public boolean preferenceFlagExists(String flag) { SharedPreferences preferences_nobackup = getSharedPreferences("prefs_nobackup",Context.MODE_PRIVATE); return preferences_nobackup.getBoolean(flag, false); } - - // -------------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------- Class @Overrides @Override public void onCreate() { - + // Sets the night mode, basing on App Preference AppCompatDelegate.setDefaultNightMode(Integer.valueOf(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("prefColorTheme", "2"))); + // Enables the Vector Drawable from Resource support AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); super.onCreate(); singleton = this; - // work around the android.os.FileUriExposedException + // Workaround for the android.os.FileUriExposedException StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build()); - final String CHANNEL_ID = "GPSLoggerServiceChannel"; - - // Create notification channel for Android >= O + // Creates the notification channel for Android >= O if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( - CHANNEL_ID, + "GPSLoggerServiceChannel", getString(R.string.app_name), NotificationManager.IMPORTANCE_LOW ); @@ -750,132 +903,303 @@ public void onCreate() { //if (file.exists ()) file.delete(); // ----------------------- + // Starts and registers EventBus //EventBus eventBus = EventBus.builder().addIndex(new EventBusIndex()).build(); EventBus.builder().addIndex(new EventBusIndex()).installDefaultEventBus(); EventBus.getDefault().register(this); - satellites = new Satellites(); // Satellites + TOAST_VERTICAL_OFFSET = (int)(75 * getResources().getDisplayMetrics().density); - mlocManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); // Location Manager + DIRECTORY_TEMP = Environment.getExternalStorageDirectory() + "/GPSLogger/Temp"; + DIRECTORY_EXPORT = Environment.getExternalStorageDirectory() + "/GPSLogger"; + DIRECTORY_FILESDIR_TRACKS = getApplicationContext().getFilesDir() + "/URI"; + FILE_EMPTY_GPX = DIRECTORY_FILESDIR_TRACKS + "/empty.gpx"; + FILE_EMPTY_KML = DIRECTORY_FILESDIR_TRACKS + "/empty.kml"; - myGPSStatusListener = new MyGPSStatus(); // GPS Satellites + locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); // Location Manager + gpsStatusListener = new MyGPSStatus(); // GPS Satellites - File sd = new File(Environment.getExternalStorageDirectory() + "/GPSLogger"); // Create the Directories if not exist + // Creates EXPORTING folder + File sd = new File(DIRECTORY_EXPORT); // Create the Directories if not exist if (!sd.exists()) { sd.mkdir(); Log.w("myApp", "[#] GPSApplication.java - Folder created: " + sd.getAbsolutePath()); } - sd = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); + // Creates TEMP folder into EXPORTING folder + sd = new File(DIRECTORY_TEMP); + if (!sd.exists()) { + File oldTemp = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); + if (oldTemp.exists()) { // The old "AppData" folder exists + boolean success = oldTemp.renameTo(sd); // Try to rename it to "Temp" + if (!success) { + sd.mkdir(); // In case of failure it creates "Temp" leaving also "AppData" + Log.w("myApp", "[#] GPSApplication.java - Folder created: " + sd.getAbsolutePath()); + } else Log.w("myApp", "[#] GPSApplication.java - Folder AppData renamed to: " + sd.getAbsolutePath()); + } + else { + sd.mkdir(); + Log.w("myApp", "[#] GPSApplication.java - Folder created: " + sd.getAbsolutePath()); + } + } + // Creates THUMBNAILS folder into private FilesDir + sd = new File(getApplicationContext().getFilesDir() + "/Thumbnails"); if (!sd.exists()) { sd.mkdir(); Log.w("myApp", "[#] GPSApplication.java - Folder created: " + sd.getAbsolutePath()); } - - sd = new File(getApplicationContext().getFilesDir() + "/Thumbnails"); + // Creates TRACKS folder into private FilesDir + sd = new File(DIRECTORY_FILESDIR_TRACKS); if (!sd.exists()) { sd.mkdir(); Log.w("myApp", "[#] GPSApplication.java - Folder created: " + sd.getAbsolutePath()); } + // Creates the empty GPX + sd = new File(FILE_EMPTY_GPX); + if (!sd.exists()) { + try { + sd.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + Log.w("myApp", "[#] GPSApplication.java - Unable to create " + sd.getAbsolutePath()); + } + } + // Creates the empty KML + sd = new File(FILE_EMPTY_KML); + if (!sd.exists()) { + try { + sd.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + Log.w("myApp", "[#] GPSApplication.java - Unable to create " + sd.getAbsolutePath()); + } + } - EGM96 egm96 = EGM96.getInstance(); // Load EGM Grid + // Loads the EGM Grid + EGM96 egm96 = EGM96.getInstance(); if (egm96 != null) { if (!egm96.isEGMGridLoaded()) { - egm96.LoadGridFromFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/WW15MGH.DAC", getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); + egm96.LoadGridFromFile(DIRECTORY_TEMP + "/WW15MGH.DAC", getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); } } - try { // Determine the app installation source + // Determine the app installation source + try { String installer; installer = getApplicationContext().getPackageManager().getInstallerPackageName(getApplicationContext().getPackageName()); if (installer.equals("com.android.vending") || installer.equals("com.google.android.feedback")) - AppOrigin = APP_ORIGIN_GOOGLE_PLAY_STORE; // App installed from Google Play Store - else AppOrigin = APP_ORIGIN_NOT_SPECIFIED; // Otherwise + appOrigin = APP_ORIGIN_GOOGLE_PLAY_STORE; // App installed from Google Play Store + else appOrigin = APP_ORIGIN_NOT_SPECIFIED; // Otherwise } catch (Exception e) { Log.w("myApp", "[#] GPSApplication.java - Exception trying to determine the package installer"); - AppOrigin = APP_ORIGIN_NOT_SPECIFIED; + appOrigin = APP_ORIGIN_NOT_SPECIFIED; } - GPSDataBase = new DatabaseHandler(this); // Initialize the Database + // Initialize the connection with the Database + gpsDataBase = new DatabaseHandler(this); // Prepare the current track - if (GPSDataBase.getLastTrackID() == 0) { - GPSDataBase.addTrack(new Track()); // Creation of the first track if the DB is empty + if (gpsDataBase.getLastTrackID() == 0) { + gpsDataBase.addTrack(new Track()); // Creation of the first track if the DB is empty isFirstRun = true; } - _currentTrack = GPSDataBase.getLastTrack(); // Get the last track - - asyncPrepareTracklistContextMenu = new AsyncPrepareTracklistContextMenu(); + currentTrack = gpsDataBase.getLastTrack(); // Get the last track + // Init Async operations + asyncPrepareActionmodeToolbar = new AsyncPrepareActionmodeToolbar(); externalViewerChecker = new ExternalViewerChecker(getApplicationContext()); - LoadPreferences(); // Load Settings - - // ---------------------------------------------------------------------------------------- + // Load Settings + LoadPreferences(); + // Starts the Thread that manages the queue of the operation on the Database asyncUpdateThread.start(); - // Get max available VM memory, exceeding this amount will throw an - // OutOfMemory exception. Stored in kilobytes as LruCache takes an - // int in its constructor. - //Log.w("myApp", "[#] GPSApplication.java - Max available VM memory = " + (int) (Runtime.getRuntime().maxMemory() / 1024) + " kbytes"); - + // Registers the Broadcast Receiver for ACTION_SHUTDOWN, ACTION_SCREEN_OFF, and ACTION_SCREEN_ON IntentFilter filter = new IntentFilter(Intent.ACTION_SHUTDOWN); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); - registerReceiver(sReceiver, filter); + registerReceiver(broadcastReceiver, filter); } - @Override public void onTerminate() { Log.w("myApp", "[#] GPSApplication.java - onTerminate"); EventBus.getDefault().unregister(this); - StopAndUnbindGPSService(); - unregisterReceiver(sReceiver); + stopAndUnbindGPSService(); + unregisterReceiver(broadcastReceiver); super.onTerminate(); } + // --------------------------------------------------------------------------- LocationListener + + @Override + public void onLocationChanged(Location loc) { + //if ((loc != null) && (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) { + if (loc != null) { // Location data is valid + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { // For API >= 18 + if ((prevFix == null) || (loc.isFromMockProvider()!=isMockProvider)) { // Reset the number of satellites when the provider changes between GPS and MOCK + if (loc.isFromMockProvider()!=isMockProvider) { + numberOfSatellitesTotal = NOT_AVAILABLE; + numberOfSatellitesUsedInFix = NOT_AVAILABLE; + } + isMockProvider = loc.isFromMockProvider(); + if (isMockProvider) Log.w("myApp", "[#] GPSApplication.java - Provider Type = MOCK PROVIDER"); + else Log.w("myApp", "[#] GPSApplication.java - Provider Type = GPS PROVIDER"); + } + } + + //Log.w("myApp", "[#] GPSApplication.java - onLocationChanged: provider=" + loc.getProvider()); + if (loc.hasSpeed() && (loc.getSpeed() == 0)) loc.removeBearing(); // Removes bearing if the speed is zero + // --------- Workaround for old GPS that are affected to Week Rollover + //loc.setTime(loc.getTime() - 619315200000L); // Commented out, it simulate the old GPS hardware Timestamp + if (loc.getTime() <= 1388534400000L) // if the Location Time is <= 01/01/2014 00:00:00.000 + loc.setTime(loc.getTime() + 619315200000L); // Timestamp incremented by 1024×7×24×60×60×1000 = 619315200000 ms + // This value must be doubled every 1024 weeks !!! + LocationExtended eloc = new LocationExtended(loc); + eloc.setNumberOfSatellites(getNumberOfSatellitesTotal()); + eloc.setNumberOfSatellitesUsedInFix(getNumberOfSatellitesUsedInFix()); + boolean forceRecord = false; + + gpsUnavailableHandler.removeCallbacks(gpsUnavailableRunnable); // Cancel the previous unavail countdown handler + gpsUnavailableHandler.postDelayed(gpsUnavailableRunnable, GPS_UNAVAILABLE_HANDLER_TIME); // starts the unavailability timeout (in 7 sec.) + + if (gpsStatus != GPS_OK) { + if (gpsStatus != GPS_STABILIZING) { + gpsStatus = GPS_STABILIZING; + stabilizer = numberOfStabilizationSamples; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + } + else stabilizer--; + if (stabilizer == 0) gpsStatus = GPS_OK; + prevFix = eloc; + prevRecordedFix = eloc; + isPrevFixRecorded = true; + } + + // Save fix in case this is a STOP or a START (the speed is "old>0 and new=0" or "old=0 and new>0") + if ((prevFix != null) && (prevFix.getLocation().hasSpeed()) && (eloc.getLocation().hasSpeed()) && (gpsStatus == GPS_OK) && (isRecording) + && (((eloc.getLocation().getSpeed() == 0) && (prevFix.getLocation().getSpeed() != 0)) || ((eloc.getLocation().getSpeed() != 0) && (prevFix.getLocation().getSpeed() == 0)))) { + if (!isPrevFixRecorded) { // Record the old sample if not already recorded + AsyncTODO ast = new AsyncTODO(); + ast.taskType = TASK_ADDLOCATION; + ast.location = prevFix; + asyncTODOQueue.add(ast); + prevRecordedFix = prevFix; + isPrevFixRecorded = true; + } + forceRecord = true; // + Force to record the new + } + + if (gpsStatus == GPS_OK) { + AsyncTODO ast = new AsyncTODO(); + if ((isRecording) && ((prefGPSdistance == 0) || (prevRecordedFix == null) || (forceRecord) || (loc.distanceTo(prevRecordedFix.getLocation()) >= prefGPSdistance))) { + prevRecordedFix = eloc; + ast.taskType = TASK_ADDLOCATION; + ast.location = eloc; + asyncTODOQueue.add(ast); + isPrevFixRecorded = true; + } else { + ast.taskType = TASK_UPDATEFIX; + ast.location = eloc; + asyncTODOQueue.add(ast); + isPrevFixRecorded = false; + } + if (isPlacemarkRequested) { + currentPlacemark = new LocationExtended(loc); + currentPlacemark.setNumberOfSatellites(getNumberOfSatellitesTotal()); + currentPlacemark.setNumberOfSatellitesUsedInFix(getNumberOfSatellitesUsedInFix()); + isPlacemarkRequested = false; + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + EventBus.getDefault().post(EventBusMSG.REQUEST_ADD_PLACEMARK); + } + prevFix = eloc; + isFirstFixFound = true; + } + } + } + + @Override + public void onProviderDisabled(String provider) { + gpsStatus = GPS_DISABLED; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + } + + @Override + public void onProviderEnabled(String provider) { + gpsStatus = GPS_SEARCHING; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + // This is called when the GPS status changes + switch (status) { + case LocationProvider.OUT_OF_SERVICE: + //Log.w("myApp", "[#] GPSApplication.java - GPS Out of Service"); + gpsUnavailableHandler.removeCallbacks(gpsUnavailableRunnable); // Cancel the previous unavail countdown handler + gpsStatus = GPS_OUTOFSERVICE; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + //Toast.makeText( getApplicationContext(), "GPS Out of Service", Toast.LENGTH_SHORT).show(); + break; + case LocationProvider.TEMPORARILY_UNAVAILABLE: + //Log.w("myApp", "[#] GPSApplication.java - GPS Temporarily Unavailable"); + gpsUnavailableHandler.removeCallbacks(gpsUnavailableRunnable); // Cancel the previous unavail countdown handler + gpsStatus = GPS_TEMPORARYUNAVAILABLE; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + //Toast.makeText( getApplicationContext(), "GPS Temporarily Unavailable", Toast.LENGTH_SHORT).show(); + break; + case LocationProvider.AVAILABLE: + gpsUnavailableHandler.removeCallbacks(gpsUnavailableRunnable); // Cancel the previous unavail countdown handler + //Log.w("myApp", "[#] GPSApplication.java - GPS Available: " + _NumberOfSatellites + " satellites"); + break; + } + } + // --------------------------------------------------------------------------- + + /** + * The EventBus receiver for Short Messages. + */ @Subscribe public void onEvent(Short msg) { if (msg == EventBusMSG.NEW_TRACK) { AsyncTODO ast = new AsyncTODO(); - ast.TaskType = "TASK_NEWTRACK"; + ast.taskType = TASK_NEWTRACK; ast.location = null; - AsyncTODOQueue.add(ast); + asyncTODOQueue.add(ast); return; } if (msg == EventBusMSG.ADD_PLACEMARK) { AsyncTODO ast = new AsyncTODO(); - ast.TaskType = "TASK_ADDPLACEMARK"; - ast.location = _currentPlacemark; - _currentPlacemark.setDescription(PlacemarkDescription); - AsyncTODOQueue.add(ast); + ast.taskType = TASK_ADDPLACEMARK; + ast.location = currentPlacemark; + currentPlacemark.setDescription(placemarkDescription); + asyncTODOQueue.add(ast); return; } if (msg == EventBusMSG.APP_PAUSE) { - handler.postDelayed(r, getHandlerTimer()); // Starts the switch-off handler (delayed by HandlerTimer) - if ((_currentTrack.getNumberOfLocations() == 0) && (_currentTrack.getNumberOfPlacemarks() == 0) - && (!Recording) && (!PlacemarkRequest)) StopAndUnbindGPSService(); + disableLocationUpdatesHandler.postDelayed(disableLocationUpdatesRunnable, getHandlerTime()); // Starts the switch-off handler (delayed by HandlerTimer) + if ((currentTrack.getNumberOfLocations() == 0) && (currentTrack.getNumberOfPlacemarks() == 0) + && (!isRecording) && (!isPlacemarkRequested)) stopAndUnbindGPSService(); System.gc(); // Clear mem from released objects with Garbage Collector return; } if (msg == EventBusMSG.APP_RESUME) { isScreenOn = true; //Log.w("myApp", "[#] GPSApplication.java - Received EventBusMSG.APP_RESUME"); - if (!asyncPrepareTracklistContextMenu.isAlive()) { - asyncPrepareTracklistContextMenu = new AsyncPrepareTracklistContextMenu(); - asyncPrepareTracklistContextMenu.start(); - } else Log.w("myApp", "[#] GPSApplication.java - asyncPrepareTracklistContextMenu already alive"); + if (!asyncPrepareActionmodeToolbar.isAlive()) { + asyncPrepareActionmodeToolbar = new AsyncPrepareActionmodeToolbar(); + asyncPrepareActionmodeToolbar.start(); + } else Log.w("myApp", "[#] GPSApplication.java - asyncPrepareActionmodeToolbar already alive"); - handler.removeCallbacks(r); // Cancel the switch-off handler - setHandlerTimer(DEFAULTHANDLERTIMER); + disableLocationUpdatesHandler.removeCallbacks(disableLocationUpdatesRunnable); // Cancel the switch-off handler + setHandlerTime(DEFAULT_SWITCHOFF_HANDLER_TIME); setGPSLocationUpdates(true); - if (MustUpdatePrefs) { - MustUpdatePrefs = false; + if (mustUpdatePrefs) { + mustUpdatePrefs = false; LoadPreferences(); } - StartAndBindGPSService(); + startAndBindGPSService(); // Check if the App is Background Restricted if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { @@ -889,45 +1213,55 @@ public void onEvent(Short msg) { } else { isBackgroundActivityRestricted = false; } - return; } if (msg == EventBusMSG.UPDATE_SETTINGS) { - MustUpdatePrefs = true; + mustUpdatePrefs = true; return; } } - + /** + * Gently shuts off the thread that manages the Database, waiting the end + * of the transaction queue. + * This method is called by the ActionBroadcastReceiver when it + * receives a Intent.ACTION_SHUTDOWN. + */ public void onShutdown() { - if (AsyncTODOQueue != null) { - GPSStatus = GPS_SEARCHING; - - Log.w("myApp", "[#] GPSApplication.java - onShutdown()"); - AsyncTODO ast = new AsyncTODO(); - ast.TaskType = "TASK_SHUTDOWN"; - ast.location = null; - AsyncTODOQueue.add(ast); - - if (asyncUpdateThread.isAlive()) { - try { - Log.w("myApp", "[#] GPSApplication.java - onShutdown(): asyncUpdateThread isAlive. join..."); - asyncUpdateThread.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - Log.w("myApp", "[#] GPSApplication.java - onShutdown() InterruptedException: " + e); - } + gpsStatus = GPS_SEARCHING; + Log.w("myApp", "[#] GPSApplication.java - onShutdown()"); + AsyncTODO ast = new AsyncTODO(); + ast.taskType = TASK_SHUTDOWN; + ast.location = null; + asyncTODOQueue.add(ast); + if (asyncUpdateThread.isAlive()) { + try { + Log.w("myApp", "[#] GPSApplication.java - onShutdown(): asyncUpdateThread isAlive. join..."); + asyncUpdateThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + Log.w("myApp", "[#] GPSApplication.java - onShutdown() InterruptedException: " + e); } } } - + /** + * This method is called by the ActionBroadcastReceiver when it + * receives a Intent.ACTION_SCREEN_OFF. + *

+ * By setting isScreenOn = false, it disables the EventBus messages at every FIX, + * in order to save battery power. + */ public void onScreenOff() { isScreenOn = false; Log.w("myApp", "[#] GPSApplication.java - SCREEN_OFF"); } - + /** + * This method is called by the ActionBroadcastReceiver when it + * receives a Intent.ACTION_SCREEN_ON. + * By setting isScreenOn = true, it enables the normal flow of EventBus messages. + */ public void onScreenOn() { Log.w("myApp", "[#] GPSApplication.java - SCREEN_ON"); isScreenOn = true; @@ -935,46 +1269,57 @@ public void onScreenOn() { EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); } - + /** + * Enables / Disables the GPS Location Updates + * + * @param state Tne state of GPS Location Updates: true = enabled; false = disabled. + */ public void setGPSLocationUpdates (boolean state) { // Request permissions = https://developer.android.com/training/permissions/requesting.html - if (!state && !getRecording() && isGPSLocationUpdatesActive + if (!state && !isRecording() && isGPSLocationUpdatesActive && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { - GPSStatus = GPS_SEARCHING; - myGPSStatusListener.disable(); - mlocManager.removeUpdates(this); + gpsStatus = GPS_SEARCHING; + gpsStatusListener.disable(); + locationManager.removeUpdates(this); isGPSLocationUpdatesActive = false; //Log.w("myApp", "[#] GPSApplication.java - setGPSLocationUpdates = false"); } if (state && !isGPSLocationUpdatesActive && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { - myGPSStatusListener.enable(); - mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, prefGPSupdatefrequency, 0, this); // Requires Location update + gpsStatusListener.enable(); + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, prefGPSupdatefrequency, 0, this); // Requires Location update isGPSLocationUpdatesActive = true; //Log.w("myApp", "[#] GPSApplication.java - setGPSLocationUpdates = true"); - if (prefGPSupdatefrequency >= 1000) StabilizingSamples = (int) Math.ceil(STABILIZERVALUE / prefGPSupdatefrequency); - else StabilizingSamples = (int) Math.ceil(STABILIZERVALUE / 1000); + if (prefGPSupdatefrequency >= 1000) numberOfStabilizationSamples = (int) Math.ceil(STABILIZER_TIME / prefGPSupdatefrequency); + else numberOfStabilizationSamples = (int) Math.ceil(STABILIZER_TIME / 1000); } } + /** + * Updates the GPS Location update frequency, basing on the value of prefGPSupdatefrequency. + * Set prefGPSupdatefrequency to a new value before calling this in order to change + * frequency. + */ public void updateGPSLocationFrequency () { if (isGPSLocationUpdatesActive && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { //Log.w("myApp", "[#] GPSApplication.java - updateGPSLocationFrequency"); - myGPSStatusListener.disable(); - mlocManager.removeUpdates(this); - if (prefGPSupdatefrequency >= 1000) StabilizingSamples = (int) Math.ceil(STABILIZERVALUE / prefGPSupdatefrequency); - else StabilizingSamples = (int) Math.ceil(STABILIZERVALUE / 1000); - myGPSStatusListener.enable(); - mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, prefGPSupdatefrequency, 0, this); + gpsStatusListener.disable(); + locationManager.removeUpdates(this); + if (prefGPSupdatefrequency >= 1000) numberOfStabilizationSamples = (int) Math.ceil(STABILIZER_TIME / prefGPSupdatefrequency); + else numberOfStabilizationSamples = (int) Math.ceil(STABILIZER_TIME / 1000); + gpsStatusListener.enable(); + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, prefGPSupdatefrequency, 0, this); } } - + /** + * Updates the GPS Status for legacy Androids. + */ public void updateGPSStatus() { try { - if ((mlocManager != null) && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { - satellites.updateStatus(mlocManager.getGpsStatus(null)); + if ((locationManager != null) && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { + satellites.updateStatus(locationManager.getGpsStatus(null)); numberOfSatellitesTotal = satellites.getNumSatsTotal(); numberOfSatellitesUsedInFix = satellites.getNumSatsUsedInFix(); } else { @@ -986,14 +1331,20 @@ public void updateGPSStatus() { numberOfSatellitesUsedInFix = NOT_AVAILABLE; //Log.w("myApp", "[#] GPSApplication.java - updateSats: Caught NullPointerException: " + e); } + if (gpsStatus != GPS_OK) { + if (isScreenOn) EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + //Log.w("myApp", "[#] GPSApplication.java - updateSats: Used=" + numberOfSatellitesUsedInFix + " Total=" + numberOfSatellitesTotal); + } //Log.w("myApp", "[#] GPSApplication.java - updateSats: Total=" + _NumberOfSatellites + " Used=" + _NumberOfSatellitesUsedInFix); } - + /** + * Updates the GPS Status for new Androids (Build.VERSION_CODES >= N). + */ @RequiresApi(api = Build.VERSION_CODES.N) public void updateGNSSStatus(android.location.GnssStatus status) { try { - if ((mlocManager != null) && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { + if ((locationManager != null) && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { satellites.updateStatus(status); numberOfSatellitesTotal = satellites.getNumSatsTotal(); numberOfSatellitesUsedInFix = satellites.getNumSatsUsedInFix(); @@ -1006,46 +1357,101 @@ public void updateGNSSStatus(android.location.GnssStatus status) { numberOfSatellitesUsedInFix = NOT_AVAILABLE; //Log.w("myApp", "[#] GPSApplication.java - updateSats: Caught NullPointerException: " + e); } + if (gpsStatus != GPS_OK) { + if (isScreenOn) EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + //Log.w("myApp", "[#] GPSApplication.java - updateSats: Used=" + numberOfSatellitesUsedInFix + " Total=" + numberOfSatellitesTotal); + } //Log.w("myApp", "[#] GPSApplication.java - updateSats: Total=" + _NumberOfSatellites + " Used=" + _NumberOfSatellitesUsedInFix); } - - private void ViewTrack(ExportingTask exportingTask) { + /** + * View a Track exported by a specific ExportingTask, using the set trackViewer. + * The trackViewer should be set prior to call this. + * + * @param exportingTask the ExportingTask that exported the Track + */ + private void viewTrack(ExportingTask exportingTask) { File file; Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setPackage(TrackViewer.packageName); - Log.w("myApp", "[#] GPSApplication.java - ViewTrack with " + TrackViewer.packageName); + intent.setPackage(trackViewer.packageName); + Log.w("myApp", "[#] GPSApplication.java - ViewTrack with " + trackViewer.packageName); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (!TrackViewer.fileType.isEmpty()) { - file = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/", exportingTask.getName() + TrackViewer.fileType); - if (TrackViewer.requiresFileProvider) { + if (!trackViewer.fileType.isEmpty()) { + file = new File(DIRECTORY_TEMP + "/", exportingTask.getName() + trackViewer.fileType); + if (trackViewer.requiresFileProvider) { Uri uri = FileProvider.getUriForFile(GPSApplication.getInstance(), "eu.basicairdata.graziano.gpslogger.fileprovider", file); - getApplicationContext().grantUriPermission(TrackViewer.packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setDataAndType(uri, TrackViewer.mimeType); + getApplicationContext().grantUriPermission(trackViewer.packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setDataAndType(uri, trackViewer.mimeType); } else { - intent.setDataAndType(Uri.fromFile(file), TrackViewer.mimeType); + intent.setDataAndType(Uri.fromFile(file), trackViewer.mimeType); } - try { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); startActivity(intent); } catch (ActivityNotFoundException e) { Log.w("myApp", "[#] GPSApplication.java - ViewTrack: Unable to view the track: " + e); - if (!asyncPrepareTracklistContextMenu.isAlive()) { - asyncPrepareTracklistContextMenu = new AsyncPrepareTracklistContextMenu(); - asyncPrepareTracklistContextMenu.start(); - } else Log.w("myApp", "[#] GPSApplication.java - asyncPrepareTracklistContextMenu already alive"); + if (!asyncPrepareActionmodeToolbar.isAlive()) { + asyncPrepareActionmodeToolbar = new AsyncPrepareActionmodeToolbar(); + asyncPrepareActionmodeToolbar.start(); + } else Log.w("myApp", "[#] GPSApplication.java - asyncPrepareActionmodeToolbar already alive"); } } } + /** + * Updates the Tracklist (re-)reading it from the Database. + */ + public void UpdateTrackList() { + long ID = gpsDataBase.getLastTrackID(); + + if (ID > 0) { + synchronized(arrayListTracks) { + // Save Selections + ArrayList SelectedT = new ArrayList<>(); + for (Track T : arrayListTracks) { + if (T.isSelected()) SelectedT.add(T.getId()); + } + // Update the List + arrayListTracks.clear(); + arrayListTracks.addAll(gpsDataBase.getTracksList(0, ID - 1)); + if ((ID > 1) && (gpsDataBase.getTrack(ID - 1) != null)) { + String fname = (ID - 1) + ".png"; + File file = new File(getApplicationContext().getFilesDir() + "/Thumbnails/", fname); + if (!file.exists()) thumbnailer = new Thumbnailer(ID - 1); + } + if (currentTrack.getNumberOfLocations() + currentTrack.getNumberOfPlacemarks() > 0) { + Log.w("myApp", "[#] GPSApplication.java - Update Tracklist: current track (" + currentTrack.getId() + ") visible into the tracklist"); + arrayListTracks.add(0, currentTrack); + } else + Log.w("myApp", "[#] GPSApplication.java - Update Tracklist: current track not visible into the tracklist"); + + // Restore the selection state + for (Track T : arrayListTracks) { + for (Long SelT : SelectedT) { + if (SelT == T.getId()) { + T.setSelected(true); + break; + } + } + } + } + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACKLIST); + //Log.w("myApp", "[#] GPSApplication.java - Update Tracklist: Added " + _ArrayListTracks.size() + " tracks"); + } + } + + /** + * Extracts and returns the list of the selected tracks on the Tracklist. + * + * @return the ArrayList of the selected tracks. + */ public ArrayList getSelectedTracks() { - ArrayList selTracks = new ArrayList(); - synchronized(_ArrayListTracks) { - for (Track T : _ArrayListTracks) { + ArrayList selTracks = new ArrayList<>(); + synchronized(arrayListTracks) { + for (Track T : arrayListTracks) { if (T.isSelected()) { selTracks.add(T); } @@ -1054,21 +1460,27 @@ public ArrayList getSelectedTracks() { return (selTracks); } - + /** + * Returns the number of selected tracks on the Tracklist. + * + * @return the number of selected tracks. + */ public int getNumberOfSelectedTracks() { int nsel = 0; - synchronized(_ArrayListTracks) { - for (Track T : _ArrayListTracks) { + synchronized(arrayListTracks) { + for (Track T : arrayListTracks) { if (T.isSelected()) nsel++; } } return nsel; } - - public void DeselectAllTracks() { - synchronized(_ArrayListTracks) { - for (Track T : _ArrayListTracks) { + /** + * Deselects all the tracks on the Tracklist. + */ + public void deselectAllTracks() { + synchronized(arrayListTracks) { + for (Track T : arrayListTracks) { if (T.isSelected()) { T.setSelected(false); EventBus.getDefault().post(new EventBusMSGNormal(EventBusMSG.TRACKLIST_DESELECT, T.getId())); @@ -1078,64 +1490,99 @@ public void DeselectAllTracks() { EventBus.getDefault().post(EventBusMSG.REFRESH_TRACKLIST); } - - public void LoadJob (int jobType) { - ExportingTaskList.clear(); - synchronized(_ArrayListTracks) { - for (Track T : _ArrayListTracks) { - if (T.isSelected()) { - ExportingTask ET = new ExportingTask(); - ET.setId(T.getId()); - ET.setName(T.getName()); - ET.setNumberOfPoints_Total(T.getNumberOfLocations() + T.getNumberOfPlacemarks()); - ET.setNumberOfPoints_Processed(0); - ExportingTaskList.add(ET); - } - } - } - JobsPending = ExportingTaskList.size(); - JobType = jobType; + /** + * Starts the ExportingStatusChecker. + * The ExportingStatusChecker regularly updates the status of the exportation, + * updating the progressbar at the bottom of the tracklist. + * The ExportingStatusChecker will end as soon as the last ExportingTask of + * the ExportingTaskList is done. + */ + void startExportingStatusChecker() { + exportingStatusCheckRunnable.run(); } - - public void ExecuteExportingTask (ExportingTask exportingTask) { - switch (JobType) { - case JOB_TYPE_NONE: - case JOB_TYPE_DELETE: - break; + /** + * Executes the specified ExportingTask. + * + * @param exportingTask The ExportingTask to execute + */ + public void executeExportingTask(ExportingTask exportingTask) { + switch (jobType) { case JOB_TYPE_EXPORT: - Ex = new Exporter(exportingTask, prefExportKML, prefExportGPX, prefExportTXT, Environment.getExternalStorageDirectory() + "/GPSLogger"); - Ex.start(); + exporter = new Exporter(exportingTask, prefExportKML, prefExportGPX, prefExportTXT, DIRECTORY_EXPORT); + exporter.start(); break; case JOB_TYPE_VIEW: - if (TrackViewer.fileType.equals(FILETYPE_GPX)) Ex = new Exporter(exportingTask, false, true, false, Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); - if (TrackViewer.fileType.equals(FILETYPE_KML)) Ex = new Exporter(exportingTask, true, false, false, Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); - Ex.start(); + if (trackViewer.fileType.equals(FILETYPE_GPX)) exporter = new Exporter(exportingTask, false, true, false, DIRECTORY_TEMP); + if (trackViewer.fileType.equals(FILETYPE_KML)) exporter = new Exporter(exportingTask, true, false, false, DIRECTORY_TEMP); + exporter.start(); break; case JOB_TYPE_SHARE: - Ex = new Exporter(exportingTask, prefExportKML, prefExportGPX, prefExportTXT, Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); - Ex.start(); + exporter = new Exporter(exportingTask, prefExportKML, prefExportGPX, prefExportTXT, DIRECTORY_TEMP); + exporter.start(); break; + case JOB_TYPE_NONE: + case JOB_TYPE_DELETE: default: break; } } - - public void ExecuteJob () { - if (!ExportingTaskList.isEmpty()) { - switch (JobType) { + /** + * Loads a Job. + * A Job is an operation to do with a set of Tracks. + * It is described by a type (jobType) and by a list of ExportingTask. + * Loading a Job means populate the ExportingTaskList and set a jobType. + *

+ * For example, if you want to export a set of Tracks, you should set as selected + * some tracks of the arrayListTracks (the tracks on the TrackList), + * call this method with jobType = JOB_TYPE_EXPORT, + * and then call executeJob. + * + * @param jobType The Job Type (JOB_TYPE_DELETE, JOB_TYPE_EXPORT, JOB_TYPE_VIEW...) + */ + public void loadJob(int jobType) { + exportingTaskList.clear(); + synchronized(arrayListTracks) { + for (Track t : arrayListTracks) { + if (t.isSelected()) { + ExportingTask et = new ExportingTask(); + et.setId(t.getId()); + et.setName(getFileName(t)); + et.setNumberOfPoints_Total(t.getNumberOfLocations() + t.getNumberOfPlacemarks()); + et.setNumberOfPoints_Processed(0); + exportingTaskList.add(et); + } + } + } + jobsPending = exportingTaskList.size(); + this.jobType = jobType; + } + + /** + * Executes a Job. + * A Job is an operation to do with a set of Tracks. + * It is described by a type (jobType) and by a list of ExportingTask. + *

+ * For example, if you want to export a set of Tracks, you should set as selected + * some tracks of the arrayListTracks (the tracks on the TrackList), + * call this method with jobType = JOB_TYPE_EXPORT, + * and then call executeJob. + */ + public void executeJob() { + if (!exportingTaskList.isEmpty()) { + switch (jobType) { case JOB_TYPE_NONE: break; case JOB_TYPE_DELETE: - String S = "TASK_DELETE_TRACKS"; - for (ExportingTask ET : ExportingTaskList) { - S = S + " " + ET.getId(); + String s = TASK_DELETETRACKS; + for (ExportingTask et : exportingTaskList) { + s = s + " " + et.getId(); } AsyncTODO ast = new AsyncTODO(); - ast.TaskType = S; + ast.taskType = s; ast.location = null; - AsyncTODOQueue.add(ast); + asyncTODOQueue.add(ast); break; case JOB_TYPE_EXPORT: case JOB_TYPE_VIEW: @@ -1147,20 +1594,28 @@ public void ExecuteJob () { } } else { Log.w("myApp", "[#] GPSApplication.java - Empty Job, nothing processed"); - JobProgress = 0; - JobsPending = 0; + jobProgress = 0; + jobsPending = 0; } } - + /** + * Gets a Bitmap starting from a Drawable. + * It is user to extract the icon of the viewers, in order to use them into Action Mode + * Toolbar of the Tracklist instead of the generic eye icon when a default viewer is set. + * Starting from Android VERSION_CODES.O, the apps could have an Adaptive Icon: this method + * obtains a bitmap usable on the toolbar. + * + * @param drawable The icon Drawable + */ @RequiresApi(api = Build.VERSION_CODES.O) - public Bitmap getBitmap(Drawable d) { - if (d instanceof BitmapDrawable) { + public Bitmap getBitmap(Drawable drawable) { + if (drawable instanceof BitmapDrawable) { Log.w("myApp", "[#] GPSApplication.java - getBitmap: instanceof BitmapDrawable"); - return ((BitmapDrawable) d).getBitmap(); - } else if ((Build.VERSION.SDK_INT >= 26) && (d instanceof AdaptiveIconDrawable)) { + return ((BitmapDrawable) drawable).getBitmap(); + } else if ((Build.VERSION.SDK_INT >= 26) && (drawable instanceof AdaptiveIconDrawable)) { Log.w("myApp", "[#] GPSApplication.java - getBitmap: instanceof AdaptiveIconDrawable"); - AdaptiveIconDrawable icon = ((AdaptiveIconDrawable) d); + AdaptiveIconDrawable icon = ((AdaptiveIconDrawable) drawable); int w = icon.getIntrinsicWidth(); int h = icon.getIntrinsicHeight(); Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); @@ -1172,234 +1627,19 @@ public Bitmap getBitmap(Drawable d) { float density = getApplicationContext().getResources().getDisplayMetrics().density; int defaultWidth = (int) (24 * density); int defaultHeight = (int) (24 * density); - Log.w("myApp", "[#] GPSApplication.java - getBitmap: !(Build.VERSION.SDK_INT >= 26) && (d instanceof AdaptiveIconDrawable)"); + Log.w("myApp", "[#] GPSApplication.java - getBitmap: !(Build.VERSION.SDK_INT >= 26) && (drawable instanceof AdaptiveIconDrawable)"); return Bitmap.createBitmap(defaultWidth, defaultHeight, Bitmap.Config.ARGB_8888); } + // ---------------------------------------------------------------------- Preferences - private class AsyncPrepareTracklistContextMenu extends Thread { - - public AsyncPrepareTracklistContextMenu() { - } - - public void run() { - isContextMenuShareVisible = false; - isContextMenuViewVisible = false; - ViewInApp = ""; - ViewInAppIcon = null; - - final PackageManager pm = getPackageManager(); - - // ----- menu share - - Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setType("text/xml"); - if ((intent.resolveActivity(pm) != null)) isContextMenuShareVisible = true; // Verify the intent will resolve to at least one activity - - // ----- menu view - - externalViewerChecker.makeAppInfoList(); - String pn = android.preference.PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("prefTracksViewer", ""); - if (!externalViewerChecker.isEmpty()) { - isContextMenuViewVisible = true; - for (AppInfo ai : externalViewerChecker.getAppInfoList()) { - if ((ai.packageName.equals(pn)) || (externalViewerChecker.size() == 1)) { - ViewInApp = ai.label + (ai.fileType.equals(FILETYPE_GPX) ? " (GPX)" : " (KML)"); - - // Set View Icon - Bitmap bitmap; - if (Build.VERSION.SDK_INT >= 26) { - bitmap = getBitmap(ai.icon); - } else { - bitmap = ((BitmapDrawable) ai.icon).getBitmap(); - } - ViewInAppIcon = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(bitmap, - (int) (24 * getResources().getDisplayMetrics().density), - (int) (24 * getResources().getDisplayMetrics().density), true)); - } - } - } - else isContextMenuViewVisible = false; - - Log.w("myApp", "[#] GPSApplication.java - Tracklist ContextMenu prepared"); - EventBus.getDefault().post(EventBusMSG.UPDATE_ACTIONBAR); - } - } - - - // --------------------------------------------------------------------------- LocationListener - @Override - public void onLocationChanged(Location loc) { - //if ((loc != null) && (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) { - if (loc != null) { // Location data is valid - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { // For API >= 18 - if ((PrevFix == null) || (loc.isFromMockProvider()!=isMockProvider)) { // Reset the number of satellites when the provider changes between GPS and MOCK - isMockProvider = loc.isFromMockProvider(); - numberOfSatellitesTotal = NOT_AVAILABLE; - numberOfSatellitesUsedInFix = NOT_AVAILABLE; - if (isMockProvider) Log.w("myApp", "[#] GPSApplication.java - Provider Type = MOCK PROVIDER"); - else Log.w("myApp", "[#] GPSApplication.java - Provider Type = GPS PROVIDER"); - } - } - - //Log.w("myApp", "[#] GPSApplication.java - onLocationChanged: provider=" + loc.getProvider()); - if (loc.hasSpeed() && (loc.getSpeed() == 0)) loc.removeBearing(); // Removes bearing if the speed is zero - // --------- Workaround for old GPS that are affected to Week Rollover - //loc.setTime(loc.getTime() - 619315200000L); // Commented out, it simulate the old GPS hardware Timestamp - if (loc.getTime() <= 1388534400000L) // if the Location Time is <= 01/01/2014 00:00:00.000 - loc.setTime(loc.getTime() + 619315200000L); // Timestamp incremented by 1024×7×24×60×60×1000 = 619315200000 ms - // This value must be doubled every 1024 weeks !!! - LocationExtended eloc = new LocationExtended(loc); - eloc.setNumberOfSatellites(getNumberOfSatellitesTotal()); - eloc.setNumberOfSatellitesUsedInFix(getNumberOfSatellitesUsedInFix()); - boolean ForceRecord = false; - - gpsunavailablehandler.removeCallbacks(unavailr); // Cancel the previous unavail countdown handler - gpsunavailablehandler.postDelayed(unavailr, GPSUNAVAILABLEHANDLERTIMER); // starts the unavailability timeout (in 7 sec.) - - if (GPSStatus != GPS_OK) { - if (GPSStatus != GPS_STABILIZING) { - GPSStatus = GPS_STABILIZING; - _Stabilizer = StabilizingSamples; - EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); - } - else _Stabilizer--; - if (_Stabilizer == 0) GPSStatus = GPS_OK; - PrevFix = eloc; - PrevRecordedFix = eloc; - isPrevFixRecorded = true; - } - - // Save fix in case this is a STOP or a START (the speed is "old>0 and new=0" or "old=0 and new>0") - if ((PrevFix != null) && (PrevFix.getLocation().hasSpeed()) && (eloc.getLocation().hasSpeed()) && (GPSStatus == GPS_OK) && (Recording) - && (((eloc.getLocation().getSpeed() == 0) && (PrevFix.getLocation().getSpeed() != 0)) || ((eloc.getLocation().getSpeed() != 0) && (PrevFix.getLocation().getSpeed() == 0)))) { - if (!isPrevFixRecorded) { // Record the old sample if not already recorded - AsyncTODO ast = new AsyncTODO(); - ast.TaskType = "TASK_ADDLOCATION"; - ast.location = PrevFix; - AsyncTODOQueue.add(ast); - PrevRecordedFix = PrevFix; - isPrevFixRecorded = true; - } - - ForceRecord = true; // + Force to record the new - } - - if (GPSStatus == GPS_OK) { - AsyncTODO ast = new AsyncTODO(); - if ((Recording) && ((prefGPSdistance == 0) || (PrevRecordedFix == null) || (ForceRecord) || (loc.distanceTo(PrevRecordedFix.getLocation()) >= prefGPSdistance))) { - PrevRecordedFix = eloc; - ast.TaskType = "TASK_ADDLOCATION"; - ast.location = eloc; - AsyncTODOQueue.add(ast); - isPrevFixRecorded = true; - } else { - ast.TaskType = "TASK_UPDATEFIX"; - ast.location = eloc; - AsyncTODOQueue.add(ast); - isPrevFixRecorded = false; - } - - if (PlacemarkRequest) { - _currentPlacemark = new LocationExtended(loc); - _currentPlacemark.setNumberOfSatellites(getNumberOfSatellitesTotal()); - _currentPlacemark.setNumberOfSatellitesUsedInFix(getNumberOfSatellitesUsedInFix()); - PlacemarkRequest = false; - EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); - EventBus.getDefault().post(EventBusMSG.REQUEST_ADD_PLACEMARK); - } - PrevFix = eloc; - } - } - } - - @Override - public void onProviderDisabled(String provider) { - GPSStatus = GPS_DISABLED; - EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); - } - - @Override - public void onProviderEnabled(String provider) { - GPSStatus = GPS_SEARCHING; - EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); - } - - - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - // This is called when the GPS status changes - switch (status) { - case LocationProvider.OUT_OF_SERVICE: - //Log.w("myApp", "[#] GPSApplication.java - GPS Out of Service"); - gpsunavailablehandler.removeCallbacks(unavailr); // Cancel the previous unavail countdown handler - GPSStatus = GPS_OUTOFSERVICE; - EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); - //Toast.makeText( getApplicationContext(), "GPS Out of Service", Toast.LENGTH_SHORT).show(); - break; - case LocationProvider.TEMPORARILY_UNAVAILABLE: - //Log.w("myApp", "[#] GPSApplication.java - GPS Temporarily Unavailable"); - gpsunavailablehandler.removeCallbacks(unavailr); // Cancel the previous unavail countdown handler - GPSStatus = GPS_TEMPORARYUNAVAILABLE; - EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); - //Toast.makeText( getApplicationContext(), "GPS Temporarily Unavailable", Toast.LENGTH_SHORT).show(); - break; - case LocationProvider.AVAILABLE: - gpsunavailablehandler.removeCallbacks(unavailr); // Cancel the previous unavail countdown handler - //Log.w("myApp", "[#] GPSApplication.java - GPS Available: " + _NumberOfSatellites + " satellites"); - break; - } - } - - - public void UpdateTrackList() { - long ID = GPSDataBase.getLastTrackID(); - - if (ID > 0) { - synchronized(_ArrayListTracks) { - // Save Selections - ArrayList SelectedT = new ArrayList<>(); - for (Track T : _ArrayListTracks) { - if (T.isSelected()) SelectedT.add(T.getId()); - } - - // Update the List - _ArrayListTracks.clear(); - _ArrayListTracks.addAll(GPSDataBase.getTracksList(0, ID - 1)); - if ((ID > 1) && (GPSDataBase.getTrack(ID - 1) != null)) { - String fname = (ID - 1) + ".png"; - File file = new File(getApplicationContext().getFilesDir() + "/Thumbnails/", fname); - if (!file.exists()) Th = new Thumbnailer(ID - 1); - } - if (_currentTrack.getNumberOfLocations() + _currentTrack.getNumberOfPlacemarks() > 0) { - Log.w("myApp", "[#] GPSApplication.java - Update Tracklist: current track (" + _currentTrack.getId() + ") visible into the tracklist"); - _ArrayListTracks.add(0, _currentTrack); - } else - Log.w("myApp", "[#] GPSApplication.java - Update Tracklist: current track not visible into the tracklist"); - - // Restore the selection state - for (Track T : _ArrayListTracks) { - for (Long SelT : SelectedT) { - if (SelT == T.getId()) { - T.setSelected(true); - break; - } - } - } - } - EventBus.getDefault().post(EventBusMSG.UPDATE_TRACKLIST); - //Log.w("myApp", "[#] GPSApplication.java - Update Tracklist: Added " + _ArrayListTracks.size() + " tracks"); - } - } - - -// PREFERENCES LOADER ------------------------------------------------------------------------------ - + /** + * (re-)Loads the Preferences and Launch signals in order to updates the UI. + */ private void LoadPreferences() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - // ---------Conversion from the previous versions of GPS Logger preferences + // Conversion from the previous versions of GPS Logger preferences if (preferences.contains("prefShowImperialUnits")) { // The old boolean setting for imperial units in v.1.1.5 Log.w("myApp", "[#] GPSApplication.java - Old setting prefShowImperialUnits present. Converting to new preference PrefUM."); boolean imperialUM = preferences.getBoolean("prefShowImperialUnits", false); @@ -1409,16 +1649,13 @@ private void LoadPreferences() { editor.commit(); } - // ---------Remove the prefIsStoragePermissionChecked in preferences if present - + // Remove the prefIsStoragePermissionChecked in preferences if present if (preferences.contains("prefIsStoragePermissionChecked")) { SharedPreferences.Editor editor = preferences.edit(); editor.remove("prefIsStoragePermissionChecked"); editor.commit(); } - // ----------------------------------------------------------------------- - //prefKeepScreenOn = preferences.getBoolean("prefKeepScreenOn", true); prefGPSWeekRolloverCorrected = preferences.getBoolean("prefGPSWeekRolloverCorrected", false); prefShowDecimalCoordinates = preferences.getBoolean("prefShowDecimalCoordinates", false); @@ -1428,7 +1665,7 @@ private void LoadPreferences() { prefGPSdistance = Float.valueOf(preferences.getString("prefGPSdistance", "0")); prefEGM96AltitudeCorrection = preferences.getBoolean("prefEGM96AltitudeCorrection", false); prefAltitudeCorrection = Double.valueOf(preferences.getString("prefAltitudeCorrection", "0")); - Log.w("myApp", "[#] GPSApplication.java - Manual Correction set to " + prefAltitudeCorrection + " m"); + Log.w("myApp", "[#] GPSApplication.java - Manual Correction set to " + prefAltitudeCorrection + " m"); prefExportKML = preferences.getBoolean("prefExportKML", true); prefExportGPX = preferences.getBoolean("prefExportGPX", true); prefExportTXT = preferences.getBoolean("prefExportTXT", false); @@ -1440,10 +1677,10 @@ private void LoadPreferences() { long oldGPSupdatefrequency = prefGPSupdatefrequency; prefGPSupdatefrequency = Long.valueOf(preferences.getString("prefGPSupdatefrequency", "1000")); - // ---------------------------------------------- Update the GPS Update Frequency if needed + // Update the GPS Update Frequency if needed if (oldGPSupdatefrequency != prefGPSupdatefrequency) updateGPSLocationFrequency(); - // ---------------------------------------------------------------- If no Exportation formats are enabled, enable the GPX one + // If no Exportation formats are enabled, enable the GPX one if (!prefExportKML && !prefExportGPX && !prefExportTXT) { SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("prefExportGPX", true); @@ -1451,32 +1688,95 @@ private void LoadPreferences() { prefExportGPX = true; } - // ---------------------------------------------------------------- Load EGM Grid if needed + // Load EGM Grid if needed EGM96 egm96 = EGM96.getInstance(); if (egm96 != null) { if (!egm96.isEGMGridLoaded()) { - egm96.LoadGridFromFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/WW15MGH.DAC", getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); + egm96.LoadGridFromFile(DIRECTORY_TEMP + "/WW15MGH.DAC", getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); } } - // ------------------------------------------------------------------- Request of UI Update + // Request of UI Update EventBus.getDefault().post(EventBusMSG.APPLY_SETTINGS); EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); EventBus.getDefault().post(EventBusMSG.UPDATE_TRACKLIST); } + // ---------------------------------------------------------------------- Threads + + /** + * The Thread that prepares the Action Mode Toolbar of the Tracklist asynchronously. + * It evaluates the visibility of the Share button, the visibility + * and the icon of the View button, basing on installed apps on the System. + */ + private class AsyncPrepareActionmodeToolbar extends Thread { + + public AsyncPrepareActionmodeToolbar() { + } + + public void run() { + isContextMenuShareVisible = false; + isContextMenuViewVisible = false; + viewInApp = ""; + viewInAppIcon = null; + + final PackageManager pm = getPackageManager(); + + // ----- menu share + + Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setType("text/xml"); + if ((intent.resolveActivity(pm) != null)) isContextMenuShareVisible = true; // Verify the intent will resolve to at least one activity -// THE THREAD THAT DOES ASYNCHRONOUS OPERATIONS --------------------------------------------------- + // ----- menu view + externalViewerChecker.makeExternalViewersList(); + String pn = android.preference.PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("prefTracksViewer", ""); + if (!externalViewerChecker.isEmpty()) { + isContextMenuViewVisible = true; + for (ExternalViewer ev : externalViewerChecker.getExternalViewersList()) { + if ((ev.packageName.equals(pn)) || (externalViewerChecker.size() == 1)) { + viewInApp = ev.label + (ev.fileType.equals(FILETYPE_GPX) ? " (GPX)" : " (KML)"); - class AsyncTODO { - String TaskType; + // Set View Icon + Bitmap bitmap; + if (Build.VERSION.SDK_INT >= 26) { + bitmap = getBitmap(ev.icon); + } else { + bitmap = ((BitmapDrawable) ev.icon).getBitmap(); + } + viewInAppIcon = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(bitmap, + (int) (24 * getResources().getDisplayMetrics().density), + (int) (24 * getResources().getDisplayMetrics().density), true)); + } + } + } + else isContextMenuViewVisible = false; + + Log.w("myApp", "[#] GPSApplication.java - Tracklist ContextMenu prepared"); + EventBus.getDefault().post(EventBusMSG.UPDATE_ACTIONBAR); + } + } + + /** + * The Class defines a Database transaction to be enqueued + */ + private static class AsyncTODO { + String taskType; LocationExtended location; } - private final BlockingQueue AsyncTODOQueue = new LinkedBlockingQueue<>(); + private final BlockingQueue asyncTODOQueue + = new LinkedBlockingQueue<>(); // The FIFO for asynchronous DB operations + /** + * The Thread that manages and executes the Database operations asynchronously. + * It takes one by one the elements of the asyncTODOQueue and executes them + * in FIFO order. + * When the asyncTODOQueue list is empty, the thread blocks waiting the next item. + */ private class AsyncUpdateThreadClass extends Thread { Track track; @@ -1486,18 +1786,17 @@ public AsyncUpdateThreadClass() {} public void run() { - track = _currentTrack; + track = currentTrack; EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); UpdateTrackList(); // ---------------------------------------------------------------------------------------- - // Apply the GPS Week Rollover Correction, for data already stored into the DB - // ---------------------------------------------------------------------------------------- - + // If needed, applies the GPS Week Rollover Correction for data already stored into the DB if (!prefGPSWeekRolloverCorrected) { if (!isFirstRun) { + // Applies the GPS Week Rollover Correction, for data already stored into the DB Log.w("myApp", "[#] GPSApplication.java - CORRECTING DATA FOR GPS WEEK ROLLOVER"); - GPSDataBase.CorrectGPSWeekRollover(); + gpsDataBase.CorrectGPSWeekRollover(); Log.w("myApp", "[#] GPSApplication.java - DATA FOR GPS WEEK ROLLOVER CORRECTED"); UpdateTrackList(); Log.w("myApp", "[#] GPSApplication.java - TRACKLIST UPDATED WITH THE CORRECTED NAMES"); @@ -1514,20 +1813,20 @@ public void run() { while (!shutdown) { AsyncTODO asyncTODO; try { - asyncTODO = AsyncTODOQueue.take(); + asyncTODO = asyncTODOQueue.take(); } catch (InterruptedException e) { Log.w("myApp", "[!] Buffer not available: " + e.getMessage()); break; } // Task: Safely Shutdown - if (asyncTODO.TaskType.equals("TASK_SHUTDOWN")) { + if (asyncTODO.taskType.equals(TASK_SHUTDOWN)) { shutdown = true; Log.w("myApp", "[#] GPSApplication.java - AsyncUpdateThreadClass: SHUTDOWN EVENT."); } // Task: Create new track (if needed) - if (asyncTODO.TaskType.equals("TASK_NEWTRACK")) { + if (asyncTODO.taskType.equals(TASK_NEWTRACK)) { if ((track.getNumberOfLocations() != 0) || (track.getNumberOfPlacemarks() != 0)) { // ---- Delete 2 thumbs files forward - in case of user deleted DB in App manager (pngs could be already presents for the new IDS) String fname = (track.getId() + 1) +".png"; @@ -1538,107 +1837,111 @@ public void run() { if (file.exists ()) file.delete (); track = new Track(); // ---- - track.setId(GPSDataBase.addTrack(track)); + track.setId(gpsDataBase.addTrack(track)); Log.w("myApp", "[#] GPSApplication.java - TASK_NEWTRACK: " + track.getId()); - _currentTrack = track; + currentTrack = track; UpdateTrackList(); } else Log.w("myApp", "[#] GPSApplication.java - TASK_NEWTRACK: Track " + track.getId() + " already empty (New track not created)"); - _currentTrack = track; + currentTrack = track; EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); } // Task: Add location to current track - if (asyncTODO.TaskType.equals("TASK_ADDLOCATION")) { + if (asyncTODO.taskType.equals(TASK_ADDLOCATION)) { locationExtended = new LocationExtended(asyncTODO.location.getLocation()); locationExtended.setNumberOfSatellites(asyncTODO.location.getNumberOfSatellites()); locationExtended.setNumberOfSatellitesUsedInFix(asyncTODO.location.getNumberOfSatellitesUsedInFix()); - _currentLocationExtended = locationExtended; + currentLocationExtended = locationExtended; if (isScreenOn) EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); track.add(locationExtended); - GPSDataBase.addLocationToTrack(locationExtended, track); + gpsDataBase.addLocationToTrack(locationExtended, track); //Log.w("myApp", "[#] GPSApplication.java - TASK_ADDLOCATION: Added new Location in " + track.getId()); - _currentTrack = track; + currentTrack = track; if (isScreenOn) EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); - if (_currentTrack.getNumberOfLocations() + _currentTrack.getNumberOfPlacemarks() == 1) UpdateTrackList(); + if (currentTrack.getNumberOfLocations() + currentTrack.getNumberOfPlacemarks() == 1) UpdateTrackList(); } // Task: Add a placemark to current track - if (asyncTODO.TaskType.equals("TASK_ADDPLACEMARK")) { + if (asyncTODO.taskType.equals(TASK_ADDPLACEMARK)) { locationExtended = new LocationExtended(asyncTODO.location.getLocation()); locationExtended.setDescription(asyncTODO.location.getDescription()); locationExtended.setNumberOfSatellites(asyncTODO.location.getNumberOfSatellites()); locationExtended.setNumberOfSatellitesUsedInFix(asyncTODO.location.getNumberOfSatellitesUsedInFix()); track.addPlacemark(locationExtended); - GPSDataBase.addPlacemarkToTrack(locationExtended, track); - _currentTrack = track; + gpsDataBase.addPlacemarkToTrack(locationExtended, track); + currentTrack = track; EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); - if (_currentTrack.getNumberOfLocations() + _currentTrack.getNumberOfPlacemarks() == 1) UpdateTrackList(); + if (currentTrack.getNumberOfLocations() + currentTrack.getNumberOfPlacemarks() == 1) UpdateTrackList(); } // Task: Update current Fix - if (asyncTODO.TaskType.equals("TASK_UPDATEFIX")) { - _currentLocationExtended = new LocationExtended(asyncTODO.location.getLocation()); - _currentLocationExtended.setNumberOfSatellites(asyncTODO.location.getNumberOfSatellites()); - _currentLocationExtended.setNumberOfSatellitesUsedInFix(asyncTODO.location.getNumberOfSatellitesUsedInFix()); + if (asyncTODO.taskType.equals(TASK_UPDATEFIX)) { + currentLocationExtended = new LocationExtended(asyncTODO.location.getLocation()); + currentLocationExtended.setNumberOfSatellites(asyncTODO.location.getNumberOfSatellites()); + currentLocationExtended.setNumberOfSatellitesUsedInFix(asyncTODO.location.getNumberOfSatellitesUsedInFix()); if (isScreenOn) EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); } // Task: Delete some tracks - if (asyncTODO.TaskType.startsWith("TASK_DELETE_TRACKS")) { + if (asyncTODO.taskType.startsWith(TASK_DELETETRACKS)) { - String STokens = asyncTODO.TaskType.substring(19); + String sTokens = asyncTODO.taskType.substring(asyncTODO.taskType.indexOf(" ") + 1); + Log.w("myApp", "[#] GPSApplication.java - DELETING (" + sTokens + ")"); List tokens = new ArrayList<>(); - StringTokenizer tokenizer = new StringTokenizer(STokens, " "); + StringTokenizer tokenizer = new StringTokenizer(sTokens, " "); while (tokenizer.hasMoreElements()) { tokens.add(tokenizer.nextToken()); } if (!tokens.isEmpty()) { - JobProgress = 0; - int TracksToBeDeleted = tokens.size(); - int TracksDeleted = 0; + jobProgress = 0; + int tracksToBeDeleted = tokens.size(); + int tracksDeleted = 0; for (String s : tokens) { Track track = null; // The track found in the _ArrayListTracks int i = Integer.valueOf(s); - if (i != _currentTrack.getId()) { // Prevent the deletion of the current track - synchronized (_ArrayListTracks) { - for (Track T : _ArrayListTracks) { - if (T.getId() == i) { - track = T; - GPSDataBase.DeleteTrack(i); + if (i != currentTrack.getId()) { // Prevent the deletion of the current track + synchronized (arrayListTracks) { + for (Track t : arrayListTracks) { + if (t.getId() == i) { + track = t; + gpsDataBase.DeleteTrack(i); Log.w("myApp", "[#] GPSApplication.java - TASK_DELETE_TRACKS: Track " + i + " deleted."); - _ArrayListTracks.remove(T); + arrayListTracks.remove(t); break; } } } if (track != null) { // Delete track files - DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/" + track.getName() + ".txt"); - DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/" + track.getName() + FILETYPE_KML); - DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/" + track.getName() + FILETYPE_GPX); - DeleteFile(getApplicationContext().getFilesDir() + "/Thumbnails/" + track.getId() + ".png"); - if (DeleteAlsoExportedFiles) { - // Delete exported files - DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/" + track.getName() + ".txt"); - DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/" + track.getName() + FILETYPE_KML); - DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/" + track.getName() + FILETYPE_GPX); + for (File f : fileFind(DIRECTORY_TEMP, track.getName())) { + Log.w("myApp", "[#] GPSApplication.java - Deleting: " + f.getAbsolutePath()); + fileDelete(f.getAbsolutePath()); + } + // Delete thumbnail + fileDelete(getApplicationContext().getFilesDir() + "/Thumbnails/" + track.getId() + ".png"); + // Delete exported files + if (deleteAlsoExportedFiles) { + for (File f : fileFind(DIRECTORY_EXPORT, track.getName())) { + Log.w("myApp", "[#] GPSApplication.java - Deleting: " + f.getAbsolutePath()); + fileDelete(f.getAbsolutePath()); + } } - TracksDeleted++; - JobProgress = (int) Math.round(1000L * TracksDeleted / TracksToBeDeleted); + tracksDeleted++; + jobProgress = (int) Math.round(1000L * tracksDeleted / tracksToBeDeleted); EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); - if (JobsPending > 0) JobsPending--; + if (jobsPending > 0) jobsPending--; } } else { Log.w("myApp", "[#] GPSApplication.java - TASK_DELETE_TRACKS: Unable to delete the current track!"); - TracksDeleted++; - JobProgress = (int) Math.round(1000L * TracksDeleted / TracksToBeDeleted); + tracksDeleted++; + jobProgress = (int) Math.round(1000L * tracksDeleted / tracksToBeDeleted); EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); - if (JobsPending > 0) JobsPending--; + if (jobsPending > 0) jobsPending--; } } } - JobProgress = 0; + jobProgress = 0; EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); EventBus.getDefault().post(EventBusMSG.NOTIFY_TRACKS_DELETED); } @@ -1646,44 +1949,44 @@ public void run() { } } - - - - -// THE THREAD THAT GENERATES A TRACK THUMBNAIL ----------------------------------------------------- - + /** + * The Thread that generates the Thumbnail of the Track with the given id. + */ public class Thumbnailer { - long Id; - long NumberOfLocations; - - private Paint drawPaint = new Paint(); - private Paint BGPaint = new Paint(); - private Paint EndDotdrawPaint = new Paint(); - private Paint EndDotBGPaint = new Paint(); - private int Size = (int)(getResources().getDimension(R.dimen.thumbSize)); - - private int Margin = (int) Math.ceil(getResources().getDimension(R.dimen.thumbLineWidth) * 3); - private int Size_Minus_Margins = Size - 2 * Margin; - - private double MinLatitude; - private double MinLongitude; - - double Distance_Proportion; - double DrawScale; - double Lat_Offset; - double Lon_Offset; - - private AsyncThumbnailThreadClass asyncThumbnailThreadClass = new AsyncThumbnailThreadClass(); - - public Thumbnailer(long ID) { - - Track track = GPSDataBase.getTrack(ID); + long id; + long numberOfLocations; + + private final Paint drawPaint = new Paint(); + private final Paint bgPaint = new Paint(); + private final Paint endDotdrawPaint = new Paint(); + private final Paint endDotBGPaint = new Paint(); + private final int size = (int)(getResources().getDimension(R.dimen.thumbSize)); + private final int margin = (int) Math.ceil(getResources().getDimension(R.dimen.thumbLineWidth) * 3); + private final int sizeMinusMargins = size - 2 * margin; + + private double minLatitude; + private double minLongitude; + + double distanceProportion; + double drawScale; + double latOffset; + double lonOffset; + + /** + * Generates the Thumbnail of the Track with the given id into FilesDir/Thumbnails/. + * The id will be used also to name the output png file. + * + * @param id The id of the Track + */ + public Thumbnailer(long id) { + + Track track = gpsDataBase.getTrack(id); //Log.w("myApp", "[#] GPSApplication.java - Bitmap Size = " + Size); if ((track.getNumberOfLocations() > 2) && (track.getDistance() >= 15) && (track.getValidMap() != 0)) { - Id = track.getId(); - NumberOfLocations = track.getNumberOfLocations(); + this.id = track.getId(); + numberOfLocations = track.getNumberOfLocations(); // Setup Paints drawPaint.setColor(getResources().getColor(R.color.colorThumbnailLineColor)); @@ -1694,42 +1997,43 @@ public Thumbnailer(long ID) { drawPaint.setStrokeJoin(Paint.Join.ROUND); drawPaint.setStrokeCap(Paint.Cap.ROUND); - BGPaint.setColor(Color.BLACK); - BGPaint.setAntiAlias(true); - BGPaint.setStrokeWidth(getResources().getDimension(R.dimen.thumbLineWidth) * 3); + bgPaint.setColor(Color.BLACK); + bgPaint.setAntiAlias(true); + bgPaint.setStrokeWidth(getResources().getDimension(R.dimen.thumbLineWidth) * 3); //BGPaint.setStrokeWidth(6); - BGPaint.setStyle(Paint.Style.STROKE); - BGPaint.setStrokeJoin(Paint.Join.ROUND); - BGPaint.setStrokeCap(Paint.Cap.ROUND); - - EndDotdrawPaint.setColor(getResources().getColor(R.color.colorThumbnailLineColor)); - EndDotdrawPaint.setAntiAlias(true); - EndDotdrawPaint.setStrokeWidth(getResources().getDimension(R.dimen.thumbLineWidth) * 2.5f); - EndDotdrawPaint.setStyle(Paint.Style.STROKE); - EndDotdrawPaint.setStrokeJoin(Paint.Join.ROUND); - EndDotdrawPaint.setStrokeCap(Paint.Cap.ROUND); - - EndDotBGPaint.setColor(Color.BLACK); - EndDotBGPaint.setAntiAlias(true); - EndDotBGPaint.setStrokeWidth(getResources().getDimension(R.dimen.thumbLineWidth) * 4.5f); - EndDotBGPaint.setStyle(Paint.Style.STROKE); - EndDotBGPaint.setStrokeJoin(Paint.Join.ROUND); - EndDotBGPaint.setStrokeCap(Paint.Cap.ROUND); + bgPaint.setStyle(Paint.Style.STROKE); + bgPaint.setStrokeJoin(Paint.Join.ROUND); + bgPaint.setStrokeCap(Paint.Cap.ROUND); + + endDotdrawPaint.setColor(getResources().getColor(R.color.colorThumbnailLineColor)); + endDotdrawPaint.setAntiAlias(true); + endDotdrawPaint.setStrokeWidth(getResources().getDimension(R.dimen.thumbLineWidth) * 2.5f); + endDotdrawPaint.setStyle(Paint.Style.STROKE); + endDotdrawPaint.setStrokeJoin(Paint.Join.ROUND); + endDotdrawPaint.setStrokeCap(Paint.Cap.ROUND); + + endDotBGPaint.setColor(Color.BLACK); + endDotBGPaint.setAntiAlias(true); + endDotBGPaint.setStrokeWidth(getResources().getDimension(R.dimen.thumbLineWidth) * 4.5f); + endDotBGPaint.setStyle(Paint.Style.STROKE); + endDotBGPaint.setStrokeJoin(Paint.Join.ROUND); + endDotBGPaint.setStrokeCap(Paint.Cap.ROUND); // Calculate the drawing scale - double Mid_Latitude = (track.getMax_Latitude() + track.getMin_Latitude()) / 2; - double Angle_From_Equator = Math.abs(Mid_Latitude); + double midLatitude = (track.getLatitudeMax() + track.getLatitudeMin()) / 2; + double angleFromEquator = Math.abs(midLatitude); - Distance_Proportion = Math.cos(Math.toRadians(Angle_From_Equator)); + distanceProportion = Math.cos(Math.toRadians(angleFromEquator)); //Log.w("myApp", "[#] GPSApplication.java - Distance_Proportion = " + Distance_Proportion); - DrawScale = Math.max(track.getMax_Latitude() - track.getMin_Latitude(), Distance_Proportion * (track.getMax_Longitude() - track.getMin_Longitude())); - Lat_Offset = Size_Minus_Margins * (1 - (track.getMax_Latitude() - track.getMin_Latitude()) / DrawScale) / 2; - Lon_Offset = Size_Minus_Margins * (1 - (Distance_Proportion * (track.getMax_Longitude() - track.getMin_Longitude()) / DrawScale)) / 2; + drawScale = Math.max(track.getLatitudeMax() - track.getLatitudeMin(), distanceProportion * (track.getLongitudeMax() - track.getLongitudeMin())); + latOffset = sizeMinusMargins * (1 - (track.getLatitudeMax() - track.getLatitudeMin()) / drawScale) / 2; + lonOffset = sizeMinusMargins * (1 - (distanceProportion * (track.getLongitudeMax() - track.getLongitudeMin()) / drawScale)) / 2; - MinLatitude = track.getMin_Latitude(); - MinLongitude = track.getMin_Longitude(); + minLatitude = track.getLatitudeMin(); + minLongitude = track.getLongitudeMin(); + final AsyncThumbnailThreadClass asyncThumbnailThreadClass = new AsyncThumbnailThreadClass(); asyncThumbnailThreadClass.start(); } } @@ -1741,52 +2045,50 @@ public AsyncThumbnailThreadClass() {} public void run() { Thread.currentThread().setPriority(Thread.MIN_PRIORITY); - String fname = Id + ".png"; + String fname = id + ".png"; File file = new File(getApplicationContext().getFilesDir() + "/Thumbnails/", fname); if (file.exists()) file.delete(); - if (DrawScale > 0) { - int GroupOfLocations = 200; + if (drawScale > 0) { + int groupOfLocations = 200; Path path = new Path(); List latlngList = new ArrayList<>(); //Log.w("myApp", "[#] GPSApplication.java - Thumbnailer Thread started"); - for (int i = 0; i < NumberOfLocations; i += GroupOfLocations) { - latlngList.addAll(GPSDataBase.getLatLngList(Id, i, i + GroupOfLocations - 1)); + for (int i = 0; i < numberOfLocations; i += groupOfLocations) { + latlngList.addAll(gpsDataBase.getLatLngList(id, i, i + groupOfLocations - 1)); } //Log.w("myApp", "[#] GPSApplication.java - Added " + latlngList.size() + " items to Path"); if (!latlngList.isEmpty()) { - Bitmap ThumbBitmap = Bitmap.createBitmap(Size, Size, Bitmap.Config.ARGB_8888); - Canvas ThumbCanvas = new Canvas(ThumbBitmap); + Bitmap thumbBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + Canvas thumbCanvas = new Canvas(thumbBitmap); for (int i = 0; i < latlngList.size(); i++) { if (i == 0) - path.moveTo((float) (Lon_Offset + Margin + Size_Minus_Margins * ((latlngList.get(i).Longitude - MinLongitude) * Distance_Proportion / DrawScale)), - (float) (-Lat_Offset + Size - (Margin + Size_Minus_Margins * ((latlngList.get(i).Latitude - MinLatitude) / DrawScale)))); + path.moveTo((float) (lonOffset + margin + sizeMinusMargins * ((latlngList.get(i).longitude - minLongitude) * distanceProportion / drawScale)), + (float) (-latOffset + size - (margin + sizeMinusMargins * ((latlngList.get(i).latitude - minLatitude) / drawScale)))); else - path.lineTo((float) (Lon_Offset + Margin + Size_Minus_Margins * ((latlngList.get(i).Longitude - MinLongitude) * Distance_Proportion / DrawScale)), - (float) (-Lat_Offset + Size - (Margin + Size_Minus_Margins * ((latlngList.get(i).Latitude - MinLatitude) / DrawScale)))); + path.lineTo((float) (lonOffset + margin + sizeMinusMargins * ((latlngList.get(i).longitude - minLongitude) * distanceProportion / drawScale)), + (float) (-latOffset + size - (margin + sizeMinusMargins * ((latlngList.get(i).latitude - minLatitude) / drawScale)))); } - ThumbCanvas.drawPath(path, BGPaint); - ThumbCanvas.drawPoint((float) (Lon_Offset + Margin + Size_Minus_Margins * ((latlngList.get(latlngList.size()-1).Longitude - MinLongitude) * Distance_Proportion / DrawScale)), - (float) (-Lat_Offset + Size - (Margin + Size_Minus_Margins * ((latlngList.get(latlngList.size()-1).Latitude - MinLatitude) / DrawScale))), EndDotBGPaint); - ThumbCanvas.drawPath(path, drawPaint); - ThumbCanvas.drawPoint((float) (Lon_Offset + Margin + Size_Minus_Margins * ((latlngList.get(latlngList.size()-1).Longitude - MinLongitude) * Distance_Proportion / DrawScale)), - (float) (-Lat_Offset + Size - (Margin + Size_Minus_Margins * ((latlngList.get(latlngList.size()-1).Latitude - MinLatitude) / DrawScale))), EndDotdrawPaint); + thumbCanvas.drawPath(path, bgPaint); + thumbCanvas.drawPoint((float) (lonOffset + margin + sizeMinusMargins * ((latlngList.get(latlngList.size()-1).longitude - minLongitude) * distanceProportion / drawScale)), + (float) (-latOffset + size - (margin + sizeMinusMargins * ((latlngList.get(latlngList.size()-1).latitude - minLatitude) / drawScale))), endDotBGPaint); + thumbCanvas.drawPath(path, drawPaint); + thumbCanvas.drawPoint((float) (lonOffset + margin + sizeMinusMargins * ((latlngList.get(latlngList.size()-1).longitude - minLongitude) * distanceProportion / drawScale)), + (float) (-latOffset + size - (margin + sizeMinusMargins * ((latlngList.get(latlngList.size()-1).latitude - minLatitude) / drawScale))), endDotdrawPaint); try { FileOutputStream out = new FileOutputStream(file); //Log.w("myApp", "[#] GPSApplication.java - FileOutputStream out = new FileOutputStream(file)"); - //boolean res = ThumbBitmap.compress(Bitmap.CompressFormat.PNG, 60, out); - ThumbBitmap.compress(Bitmap.CompressFormat.PNG, 60, out); - //Log.w("myApp", "[#] GPSApplication.java - ThumbBitmap.compress(Bitmap.CompressFormat.PNG, 60, out): " + res); + //boolean res = thumbBitmap.compress(Bitmap.CompressFormat.PNG, 60, out); + thumbBitmap.compress(Bitmap.CompressFormat.PNG, 60, out); + //Log.w("myApp", "[#] GPSApplication.java - thumbBitmap.compress(Bitmap.CompressFormat.PNG, 60, out): " + res); out.flush(); - //Log.w("myApp", "[#] GPSApplication.java - out.flush();"); out.close(); - //Log.w("myApp", "[#] GPSApplication.java - out.close();"); } catch (Exception e) { e.printStackTrace(); - //Log.w("myApp", "[#] GPSApplication.java - Unable to save: " + Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/" + fname); + //Log.w("myApp", "[#] GPSApplication.java - Unable to save: " + DIRECTORY_TEMP + "/" + fname); } EventBus.getDefault().post(EventBusMSG.REFRESH_TRACKLIST); diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSService.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSService.java index db3e11af..7455505a 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSService.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/GPSService.java @@ -1,6 +1,9 @@ -/** +/* * GPSService - Java Class for Android - * Created by G.Capelli (BasicAirData) on 2/11/2016 + * Created by G.Capelli on 2/11/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,67 +38,44 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +/** + * The Foreground Service that keeps alive the app in background when recording. + * It shows a notification that shows the status of the recording, the traveled distance + * and the related duration. + */ public class GPSService extends Service { - private final int ID = 1; + private static final int ID = 1; // The id of the notification + private String oldNotificationText = ""; private NotificationCompat.Builder builder; private NotificationManager mNotificationManager; - private boolean recordingState = false; + private boolean recordingState; + private PowerManager.WakeLock wakeLock; // PARTIAL_WAKELOCK - public class LocalBinder extends Binder { // Returns the instance of the service + /** + * Returns the instance of the service + */ + public class LocalBinder extends Binder { public GPSService getServiceInstance(){ return GPSService.this; } } private final IBinder mBinder = new LocalBinder(); // IBinder - PowerManager.WakeLock wakeLock; // PARTIAL_WAKELOCK - - - private Notification getNotification() { - final String CHANNEL_ID = "GPSLoggerServiceChannel"; - - recordingState = isIconRecording(); - - builder = new NotificationCompat.Builder(this, CHANNEL_ID); - //builder.setSmallIcon(R.drawable.ic_notification_24dp) - builder.setSmallIcon(recordingState ? R.mipmap.ic_notify_recording_24dp : R.mipmap.ic_notify_24dp) - .setColor(getResources().getColor(R.color.colorPrimaryLight)) - .setContentTitle(getString(R.string.app_name)) - .setShowWhen(false) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setCategory(NotificationCompat.CATEGORY_SERVICE) - .setOngoing(true) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setContentText(composeContentText()); - - final Intent startIntent = new Intent(getApplicationContext(), GPSActivity.class); - startIntent.setAction(Intent.ACTION_MAIN); - startIntent.addCategory(Intent.CATEGORY_LAUNCHER); - startIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 1, startIntent, 0); - builder.setContentIntent(contentIntent); - return builder.build(); - } - - @Override public void onCreate() { super.onCreate(); - // PARTIAL_WAKELOCK PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"GPSLogger:wakelock"); Log.w("myApp", "[#] GPSService.java - CREATE = onCreate"); - // Workaround for Nokia Devices, Android 9 // https://github.com/BasicAirData/GPSLogger/issues/77 if (EventBus.getDefault().isRegistered(this)) { //Log.w("myApp", "[#] GPSActivity.java - EventBus: GPSActivity already registered"); EventBus.getDefault().unregister(this); } - EventBus.getDefault().register(this); } @@ -116,7 +96,6 @@ public IBinder onBind(Intent intent) { } Log.w("myApp", "[#] GPSService.java - BIND = onBind"); return mBinder; - //return null; } @Override @@ -126,27 +105,26 @@ public void onDestroy() { wakeLock.release(); Log.w("myApp", "[#] GPSService.java - WAKELOCK released"); } - EventBus.getDefault().unregister(this); - Log.w("myApp", "[#] GPSService.java - DESTROY = onDestroy"); // THREAD FOR DEBUG PURPOSE //if (t.isAlive()) t.interrupt(); super.onDestroy(); } + /** + * The EventBus receiver for Short Messages. + */ @Subscribe (threadMode = ThreadMode.MAIN) public void onEvent(Short msg) { if ((msg == EventBusMSG.UPDATE_FIX) && (builder != null)) { String notificationText = composeContentText(); if (!oldNotificationText.equals(notificationText)) { builder.setContentText(notificationText); - if (isIconRecording() != recordingState) { recordingState = isIconRecording(); builder.setSmallIcon(recordingState ? R.mipmap.ic_notify_recording_24dp : R.mipmap.ic_notify_24dp); } - mNotificationManager.notify(ID, builder.build()); oldNotificationText = notificationText; //Log.w("myApp", "[#] GPSService.java - Update Notification Text"); @@ -154,17 +132,50 @@ public void onEvent(Short msg) { } } + /** + * Creates and gets the Notification. + * + * @return the Notification + */ + private Notification getNotification() { + final String CHANNEL_ID = "GPSLoggerServiceChannel"; - private boolean isIconRecording () { - return ((GPSApplication.getInstance().getGPSStatus() == GPSApplication.GPS_OK) && GPSApplication.getInstance().getRecording()); + recordingState = isIconRecording(); + builder = new NotificationCompat.Builder(this, CHANNEL_ID); + //builder.setSmallIcon(R.drawable.ic_notification_24dp) + builder.setSmallIcon(recordingState ? R.mipmap.ic_notify_recording_24dp : R.mipmap.ic_notify_24dp) + .setColor(getResources().getColor(R.color.colorPrimaryLight)) + .setContentTitle(getString(R.string.app_name)) + .setShowWhen(false) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .setOngoing(true) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setContentText(composeContentText()); + + final Intent startIntent = new Intent(getApplicationContext(), GPSActivity.class); + startIntent.setAction(Intent.ACTION_MAIN); + startIntent.addCategory(Intent.CATEGORY_LAUNCHER); + startIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 1, startIntent, 0); + builder.setContentIntent(contentIntent); + return builder.build(); } + /** + * @return true if the icon should be the filled one, indicating that the app is recording. + */ + private boolean isIconRecording () { + return ((GPSApplication.getInstance().getGPSStatus() == GPSApplication.GPS_OK) && GPSApplication.getInstance().isRecording()); + } + /** + * @return The string to use as Notification description. + */ private String composeContentText () { String notificationText = ""; - - int GPSStatus = GPSApplication.getInstance().getGPSStatus(); - switch (GPSStatus) { + int gpsStatus = GPSApplication.getInstance().getGPSStatus(); + switch (gpsStatus) { case GPSApplication.GPS_DISABLED: notificationText = getString(R.string.gps_disabled); break; @@ -179,20 +190,20 @@ private String composeContentText () { notificationText = getString(R.string.gps_stabilizing); break; case GPSApplication.GPS_OK: - if (GPSApplication.getInstance().getRecording() && (GPSApplication.getInstance().getCurrentTrack() != null)) { + if (GPSApplication.getInstance().isRecording() && (GPSApplication.getInstance().getCurrentTrack() != null)) { PhysicalDataFormatter phdformatter = new PhysicalDataFormatter(); PhysicalData phdDuration; PhysicalData phdDistance; // Duration phdDuration = phdformatter.format(GPSApplication.getInstance().getCurrentTrack().getPrefTime(), PhysicalDataFormatter.FORMAT_DURATION); - if (phdDuration.Value.isEmpty()) phdDuration.Value = "00:00"; - notificationText = getString(R.string.duration) + ": " + phdDuration.Value; + if (phdDuration.value.isEmpty()) phdDuration.value = "00:00"; + notificationText = getString(R.string.duration) + ": " + phdDuration.value; // Distance (if available) phdDistance = phdformatter.format(GPSApplication.getInstance().getCurrentTrack().getEstimatedDistance(), PhysicalDataFormatter.FORMAT_DISTANCE); - if (!phdDistance.Value.isEmpty()) { - notificationText += " - " + getString(R.string.distance) + ": " + phdDistance.Value + " " + phdDistance.UM; + if (!phdDistance.value.isEmpty()) { + notificationText += " - " + getString(R.string.distance) + ": " + phdDistance.value + " " + phdDistance.um; } } else { notificationText = getString(R.string.notification_contenttext); diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/LatLng.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/LatLng.java index d68e8fcf..265c59e8 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/LatLng.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/LatLng.java @@ -1,6 +1,9 @@ /* * LatLng - Java Class for Android - * Created by G.Capelli (BasicAirData) on 3/7/2016 + * Created by G.Capelli on 3/7/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,7 +21,11 @@ package eu.basicairdata.graziano.gpslogger; +/** + * The data structure that describes a 2D point on the Earth surface. + * It is used to create the thumbnails of the Tracks. + */ class LatLng { - double Latitude; - double Longitude; + double latitude; + double longitude; } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/LocationExtended.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/LocationExtended.java index e42a9076..bac7cd3a 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/LocationExtended.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/LocationExtended.java @@ -1,6 +1,9 @@ /* * LocationExtended - Java Class for Android - * Created by G.Capelli (BasicAirData) on 2/6/2016 + * Created by G.Capelli on 2/6/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,79 +25,96 @@ import static eu.basicairdata.graziano.gpslogger.GPSApplication.NOT_AVAILABLE; - +/** + * The Location Class, including some extra stuff in order to manage the orthometric + * height using the EGM Correction. + */ public class LocationExtended { - - private Location _Location; - private String _Description = ""; - private double _AltitudeEGM96Correction = NOT_AVAILABLE; - private int _NumberOfSatellites = NOT_AVAILABLE; - private int _NumberOfSatellitesUsedInFix = NOT_AVAILABLE; - - - // Constructor + private Location location; + private String description = ""; + private double altitudeEGM96Correction = NOT_AVAILABLE; + private int numberOfSatellites = NOT_AVAILABLE; + private int numberOfSatellitesUsedInFix = NOT_AVAILABLE; + + /** + * The constructor. + * + * @param location the base Location + */ public LocationExtended(Location location) { - _Location = location; + this.location = location; EGM96 egm96 = EGM96.getInstance(); if (egm96 != null) { - if (egm96.isEGMGridLoaded()) _AltitudeEGM96Correction = egm96.getEGMCorrection(_Location.getLatitude(), _Location.getLongitude()); + if (egm96.isEGMGridLoaded()) altitudeEGM96Correction = egm96.getEGMCorrection(this.location.getLatitude(), this.location.getLongitude()); } } - // Getters and Setters ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- Getters and Setters public Location getLocation() { - return _Location; + return location; } - public double getLatitude() { return _Location.getLatitude(); } - public double getLongitude() { return _Location.getLongitude(); } - public double getAltitude() { return _Location.hasAltitude() ? _Location.getAltitude() : NOT_AVAILABLE; } - public float getSpeed() { return _Location.hasSpeed() ? _Location.getSpeed() : NOT_AVAILABLE; } - public float getAccuracy() { return _Location.hasAccuracy() ? _Location.getAccuracy() : NOT_AVAILABLE; } - public float getBearing() { return _Location.hasBearing() ? _Location.getBearing() : NOT_AVAILABLE; } - public long getTime() { return _Location.getTime(); } + public double getLatitude() { return location.getLatitude(); } + + public double getLongitude() { return location.getLongitude(); } + + public double getAltitude() { return location.hasAltitude() ? location.getAltitude() : NOT_AVAILABLE; } + + public float getSpeed() { return location.hasSpeed() ? location.getSpeed() : NOT_AVAILABLE; } + + public float getAccuracy() { return location.hasAccuracy() ? location.getAccuracy() : NOT_AVAILABLE; } + + public float getBearing() { return location.hasBearing() ? location.getBearing() : NOT_AVAILABLE; } + + public long getTime() { return location.getTime(); } public String getDescription() { - return _Description; + return description; } - public void setDescription(String Description) { - this._Description = Description; + public void setDescription(String description) { + this.description = description; } public void setNumberOfSatellites(int numberOfSatellites) { - _NumberOfSatellites = numberOfSatellites; + this.numberOfSatellites = numberOfSatellites; } public int getNumberOfSatellites() { - return _NumberOfSatellites; + return numberOfSatellites; } public void setNumberOfSatellitesUsedInFix(int numberOfSatellites) { - _NumberOfSatellitesUsedInFix = numberOfSatellites; + numberOfSatellitesUsedInFix = numberOfSatellites; } public int getNumberOfSatellitesUsedInFix() { - return _NumberOfSatellitesUsedInFix; + return numberOfSatellitesUsedInFix; } + /** + * @return the altitude correction, in meters, based on EGM96 + */ public double getAltitudeEGM96Correction(){ - if (_AltitudeEGM96Correction == NOT_AVAILABLE) { + if (altitudeEGM96Correction == NOT_AVAILABLE) { //Log.w("myApp", "[#] LocationExtended.java - _AltitudeEGM96Correction == NOT_AVAILABLE"); EGM96 egm96 = EGM96.getInstance(); if (egm96 != null) { - if (egm96.isEGMGridLoaded()) _AltitudeEGM96Correction = egm96.getEGMCorrection(_Location.getLatitude(), _Location.getLongitude()); + if (egm96.isEGMGridLoaded()) altitudeEGM96Correction = egm96.getEGMCorrection(location.getLatitude(), location.getLongitude()); } } - return _AltitudeEGM96Correction; + return altitudeEGM96Correction; } + /** + * @return the orthometric altitude in meters + */ public double getAltitudeCorrected(double AltitudeManualCorrection, boolean EGMCorrection) { - if (_Location != null) { - if (!_Location.hasAltitude()) return NOT_AVAILABLE; - if ((EGMCorrection) && (getAltitudeEGM96Correction() != NOT_AVAILABLE)) return _Location.getAltitude() - getAltitudeEGM96Correction() + AltitudeManualCorrection; - else return _Location.getAltitude() + AltitudeManualCorrection; + if (location != null) { + if (!location.hasAltitude()) return NOT_AVAILABLE; + if ((EGMCorrection) && (getAltitudeEGM96Correction() != NOT_AVAILABLE)) return location.getAltitude() - getAltitudeEGM96Correction() + AltitudeManualCorrection; + else return location.getAltitude() + AltitudeManualCorrection; } return NOT_AVAILABLE; } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/PhysicalData.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/PhysicalData.java index faf843a6..e47c489d 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/PhysicalData.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/PhysicalData.java @@ -1,6 +1,9 @@ /* * PhysicalData - Java Class for Android - * Created by G.Capelli (BasicAirData) on 21/3/2017 + * Created by G.Capelli on 21/3/2017 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,7 +21,11 @@ package eu.basicairdata.graziano.gpslogger; +/** + * The data structure that defines a physical measurement. + * A physical data includes a number and a unit of measurement. + */ class PhysicalData { - String Value; //The string of the Numerical value of the Physical Quantity - String UM; //The string of the Unit of Measurement -} + String value; // The Numerical value of the Physical Quantity + String um; // The Unit of Measurement +} \ No newline at end of file diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/PhysicalDataFormatter.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/PhysicalDataFormatter.java index 53e1a54d..b55c3b65 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/PhysicalDataFormatter.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/PhysicalDataFormatter.java @@ -1,6 +1,9 @@ -/** +/* * PhysicalDataFormatter - Java Class for Android - * Created by G.Capelli (BasicAirData) on 21/3/2017 + * Created by G.Capelli on 21/3/2017 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +29,10 @@ import static eu.basicairdata.graziano.gpslogger.GPSApplication.NOT_AVAILABLE; - +/** + * A helper Class for the formatting of the physical data. + * It returns the data formatted basing on the given criteria and on the Preferences. + */ class PhysicalDataFormatter { private static final int UM_METRIC_MS = 0; @@ -53,200 +59,219 @@ class PhysicalDataFormatter { private static final float MS_TO_KMH = 3.6f; private static final float MS_TO_KN = 1.943844491f; private static final float KM_TO_MI = 0.621371192237f; - - //private PhysicalData _PhysicalData = new PhysicalData(); - private GPSApplication gpsApplication = GPSApplication.getInstance(); - - - public PhysicalData format(float Number, byte Format) { - PhysicalData _PhysicalData = new PhysicalData(); - _PhysicalData.Value = ""; - _PhysicalData.UM = ""; + + private final GPSApplication gpsApp = GPSApplication.getInstance(); + + /** + * It returns a PhysicalData formatted basing on the given criteria and on the Preferences. + * + * @param number The float number to format as Physical Data + * @param format The desired Format (FORMAT_LATITUDE, FORMAT_LONGITUDE, FORMAT_ALTITUDE...) + * @return The Physical Data containing number and unit of measurement + */ + public PhysicalData format(float number, byte format) { + PhysicalData physicalData = new PhysicalData(); + physicalData.value = ""; + physicalData.um = ""; - if (Number == NOT_AVAILABLE) return(_PhysicalData); // Returns empty fields if the data is not available + if (number == NOT_AVAILABLE) return(physicalData); // Returns empty fields if the data is not available - switch (Format) { + switch (format) { case FORMAT_SPEED: // Speed - switch (gpsApplication.getPrefUM()) { + switch (gpsApp.getPrefUM()) { case UM_METRIC_KMH: - _PhysicalData.Value = String.valueOf(Math.round(Number * MS_TO_KMH)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_km_h); - return(_PhysicalData); + physicalData.value = String.valueOf(Math.round(number * MS_TO_KMH)); + physicalData.um = gpsApp.getString(R.string.UM_km_h); + return(physicalData); case UM_METRIC_MS: - _PhysicalData.Value = String.valueOf(Math.round(Number)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_m_s); - return(_PhysicalData); + physicalData.value = String.valueOf(Math.round(number)); + physicalData.um = gpsApp.getString(R.string.UM_m_s); + return(physicalData); case UM_IMPERIAL_MPH: case UM_NAUTICAL_MPH: - _PhysicalData.Value = String.valueOf(Math.round(Number * MS_TO_MPH)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_mph); - return(_PhysicalData); + physicalData.value = String.valueOf(Math.round(number * MS_TO_MPH)); + physicalData.um = gpsApp.getString(R.string.UM_mph); + return(physicalData); case UM_IMPERIAL_FPS: - _PhysicalData.Value = String.valueOf(Math.round(Number * M_TO_FT)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_fps); - return(_PhysicalData); + physicalData.value = String.valueOf(Math.round(number * M_TO_FT)); + physicalData.um = gpsApp.getString(R.string.UM_fps); + return(physicalData); case UM_NAUTICAL_KN: - _PhysicalData.Value = String.valueOf(Math.round(Number * MS_TO_KN)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_kn); - return(_PhysicalData); + physicalData.value = String.valueOf(Math.round(number * MS_TO_KN)); + physicalData.um = gpsApp.getString(R.string.UM_kn); + return(physicalData); } case FORMAT_SPEED_AVG: // Average Speed, formatted with 1 decimal - switch (gpsApplication.getPrefUM()) { + switch (gpsApp.getPrefUM()) { case UM_METRIC_KMH: - _PhysicalData.Value = String.format(Locale.getDefault(), "%.1f", (Number * MS_TO_KMH)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_km_h); - return(_PhysicalData); + physicalData.value = String.format(Locale.getDefault(), "%.1f", (number * MS_TO_KMH)); + physicalData.um = gpsApp.getString(R.string.UM_km_h); + return(physicalData); case UM_METRIC_MS: - _PhysicalData.Value = String.format(Locale.getDefault(), "%.1f", (Number)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_m_s); - return(_PhysicalData); + physicalData.value = String.format(Locale.getDefault(), "%.1f", (number)); + physicalData.um = gpsApp.getString(R.string.UM_m_s); + return(physicalData); case UM_IMPERIAL_MPH: case UM_NAUTICAL_MPH: - _PhysicalData.Value = String.format(Locale.getDefault(), "%.1f", (Number * MS_TO_MPH)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_mph); - return(_PhysicalData); + physicalData.value = String.format(Locale.getDefault(), "%.1f", (number * MS_TO_MPH)); + physicalData.um = gpsApp.getString(R.string.UM_mph); + return(physicalData); case UM_IMPERIAL_FPS: - _PhysicalData.Value = String.format(Locale.getDefault(), "%.1f", (Number * M_TO_FT)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_fps); - return(_PhysicalData); + physicalData.value = String.format(Locale.getDefault(), "%.1f", (number * M_TO_FT)); + physicalData.um = gpsApp.getString(R.string.UM_fps); + return(physicalData); case UM_NAUTICAL_KN: - _PhysicalData.Value = String.format(Locale.getDefault(), "%.1f", (Number * MS_TO_KN)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_kn); - return(_PhysicalData); + physicalData.value = String.format(Locale.getDefault(), "%.1f", (number * MS_TO_KN)); + physicalData.um = gpsApp.getString(R.string.UM_kn); + return(physicalData); } case FORMAT_ACCURACY: // Accuracy - switch (gpsApplication.getPrefUM()) { + switch (gpsApp.getPrefUM()) { case UM_METRIC_KMH: case UM_METRIC_MS: - _PhysicalData.Value = String.valueOf(Math.round(Number)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_m); - return(_PhysicalData); + physicalData.value = String.valueOf(Math.round(number)); + physicalData.um = gpsApp.getString(R.string.UM_m); + return(physicalData); case UM_IMPERIAL_MPH: case UM_IMPERIAL_FPS: case UM_NAUTICAL_MPH: case UM_NAUTICAL_KN: - _PhysicalData.Value = String.valueOf(Math.round(Number * M_TO_FT)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_ft); - return(_PhysicalData); + physicalData.value = String.valueOf(Math.round(number * M_TO_FT)); + physicalData.um = gpsApp.getString(R.string.UM_ft); + return(physicalData); } case FORMAT_BEARING: // Bearing (Direction) - switch (gpsApplication.getPrefShowDirections()) { + switch (gpsApp.getPrefShowDirections()) { case 0: // NSWE - int dr = (int) Math.round(Number / 22.5); + int dr = (int) Math.round(number / 22.5); switch (dr) { - case 0: _PhysicalData.Value = gpsApplication.getString(R.string.north); return(_PhysicalData); - case 1: _PhysicalData.Value = gpsApplication.getString(R.string.north_northeast); return(_PhysicalData); - case 2: _PhysicalData.Value = gpsApplication.getString(R.string.northeast); return(_PhysicalData); - case 3: _PhysicalData.Value = gpsApplication.getString(R.string.east_northeast); return(_PhysicalData); - case 4: _PhysicalData.Value = gpsApplication.getString(R.string.east); return(_PhysicalData); - case 5: _PhysicalData.Value = gpsApplication.getString(R.string.east_southeast); return(_PhysicalData); - case 6: _PhysicalData.Value = gpsApplication.getString(R.string.southeast); return(_PhysicalData); - case 7: _PhysicalData.Value = gpsApplication.getString(R.string.south_southeast); return(_PhysicalData); - case 8: _PhysicalData.Value = gpsApplication.getString(R.string.south); return(_PhysicalData); - case 9: _PhysicalData.Value = gpsApplication.getString(R.string.south_southwest); return(_PhysicalData); - case 10: _PhysicalData.Value = gpsApplication.getString(R.string.southwest); return(_PhysicalData); - case 11: _PhysicalData.Value = gpsApplication.getString(R.string.west_southwest); return(_PhysicalData); - case 12: _PhysicalData.Value = gpsApplication.getString(R.string.west); return(_PhysicalData); - case 13: _PhysicalData.Value = gpsApplication.getString(R.string.west_northwest); return(_PhysicalData); - case 14: _PhysicalData.Value = gpsApplication.getString(R.string.northwest); return(_PhysicalData); - case 15: _PhysicalData.Value = gpsApplication.getString(R.string.north_northwest); return(_PhysicalData); - case 16: _PhysicalData.Value = gpsApplication.getString(R.string.north); return(_PhysicalData); + case 0: physicalData.value = gpsApp.getString(R.string.north); return(physicalData); + case 1: physicalData.value = gpsApp.getString(R.string.north_northeast); return(physicalData); + case 2: physicalData.value = gpsApp.getString(R.string.northeast); return(physicalData); + case 3: physicalData.value = gpsApp.getString(R.string.east_northeast); return(physicalData); + case 4: physicalData.value = gpsApp.getString(R.string.east); return(physicalData); + case 5: physicalData.value = gpsApp.getString(R.string.east_southeast); return(physicalData); + case 6: physicalData.value = gpsApp.getString(R.string.southeast); return(physicalData); + case 7: physicalData.value = gpsApp.getString(R.string.south_southeast); return(physicalData); + case 8: physicalData.value = gpsApp.getString(R.string.south); return(physicalData); + case 9: physicalData.value = gpsApp.getString(R.string.south_southwest); return(physicalData); + case 10: physicalData.value = gpsApp.getString(R.string.southwest); return(physicalData); + case 11: physicalData.value = gpsApp.getString(R.string.west_southwest); return(physicalData); + case 12: physicalData.value = gpsApp.getString(R.string.west); return(physicalData); + case 13: physicalData.value = gpsApp.getString(R.string.west_northwest); return(physicalData); + case 14: physicalData.value = gpsApp.getString(R.string.northwest); return(physicalData); + case 15: physicalData.value = gpsApp.getString(R.string.north_northwest); return(physicalData); + case 16: physicalData.value = gpsApp.getString(R.string.north); return(physicalData); } case 1: // Angle - _PhysicalData.Value = String.valueOf(Math.round(Number)); - return(_PhysicalData); + physicalData.value = String.valueOf(Math.round(number)).concat("°"); + return(physicalData); } case FORMAT_DISTANCE: // Distance - switch (gpsApplication.getPrefUM()) { + switch (gpsApp.getPrefUM()) { case UM_METRIC_KMH: case UM_METRIC_MS: - if (Number < 1000) { - _PhysicalData.Value = String.format(Locale.getDefault(), "%.0f", (Math.floor(Number))); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_m); + if (number < 1000) { + physicalData.value = String.format(Locale.getDefault(), "%.0f", (Math.floor(number))); + physicalData.um = gpsApp.getString(R.string.UM_m); } else { - if (Number < 10000) _PhysicalData.Value = String.format(Locale.getDefault(), "%.2f" , ((Math.floor(Number / 10.0)))/100.0); - else _PhysicalData.Value = String.format(Locale.getDefault(), "%.1f" , ((Math.floor(Number / 100.0)))/10.0); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_km); + if (number < 10000) physicalData.value = String.format(Locale.getDefault(), "%.2f" , ((Math.floor(number / 10.0)))/100.0); + else physicalData.value = String.format(Locale.getDefault(), "%.1f" , ((Math.floor(number / 100.0)))/10.0); + physicalData.um = gpsApp.getString(R.string.UM_km); } - return(_PhysicalData); + return(physicalData); case UM_IMPERIAL_MPH: case UM_IMPERIAL_FPS: - if ((Number * M_TO_FT) < 1000) { - _PhysicalData.Value = String.format(Locale.getDefault(), "%.0f", (Math.floor(Number * M_TO_FT))); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_ft); + if ((number * M_TO_FT) < 1000) { + physicalData.value = String.format(Locale.getDefault(), "%.0f", (Math.floor(number * M_TO_FT))); + physicalData.um = gpsApp.getString(R.string.UM_ft); } else { - if ((Number * KM_TO_MI) < 10000) _PhysicalData.Value = String.format(Locale.getDefault(), "%.2f", ((Math.floor((Number * KM_TO_MI) / 10.0)))/100.0); - else _PhysicalData.Value = String.format(Locale.getDefault(), "%.1f", ((Math.floor((Number * KM_TO_MI) / 100.0)))/10.0); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_mi); + if ((number * KM_TO_MI) < 10000) physicalData.value = String.format(Locale.getDefault(), "%.2f", ((Math.floor((number * KM_TO_MI) / 10.0)))/100.0); + else physicalData.value = String.format(Locale.getDefault(), "%.1f", ((Math.floor((number * KM_TO_MI) / 100.0)))/10.0); + physicalData.um = gpsApp.getString(R.string.UM_mi); } - return(_PhysicalData); + return(physicalData); case UM_NAUTICAL_KN: case UM_NAUTICAL_MPH: - if ((Number * M_TO_NM) < 100) _PhysicalData.Value = String.format(Locale.getDefault(), "%.2f", ((Math.floor((Number * M_TO_NM) * 100.0))) / 100.0); - else _PhysicalData.Value = String.format(Locale.getDefault(), "%.1f", ((Math.floor((Number * M_TO_NM) * 10.0))) / 10.0); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_nm); - return(_PhysicalData); + if ((number * M_TO_NM) < 100) physicalData.value = String.format(Locale.getDefault(), "%.2f", ((Math.floor((number * M_TO_NM) * 100.0))) / 100.0); + else physicalData.value = String.format(Locale.getDefault(), "%.1f", ((Math.floor((number * M_TO_NM) * 10.0))) / 10.0); + physicalData.um = gpsApp.getString(R.string.UM_nm); + return(physicalData); } } - return(_PhysicalData); + return(physicalData); } - - public PhysicalData format(double Number, byte Format) { - PhysicalData _PhysicalData = new PhysicalData(); - _PhysicalData.Value = ""; - _PhysicalData.UM = ""; + + /** + * It returns a PhysicalData formatted basing on the given criteria and on the Preferences. + * + * @param number The double number to format as Physical Data + * @param format The desired format (FORMAT_LATITUDE, FORMAT_LONGITUDE, FORMAT_ALTITUDE...) + * @return The Physical Data containing number and unit of measurement + */ + public PhysicalData format(double number, byte format) { + PhysicalData physicalData = new PhysicalData(); + physicalData.value = ""; + physicalData.um = ""; - if (Number == NOT_AVAILABLE) return(_PhysicalData); // Returns empty fields if the data is not available + if (number == NOT_AVAILABLE) return(physicalData); // Returns empty fields if the data is not available - switch (Format) { + switch (format) { case FORMAT_LATITUDE: // Latitude - _PhysicalData.Value = gpsApplication.getPrefShowDecimalCoordinates() ? - String.format(Locale.getDefault(), "%.9f", Math.abs(Number)) : Location.convert(Math.abs(Number), Location.FORMAT_SECONDS); - _PhysicalData.UM = Number >= 0 ? gpsApplication.getString(R.string.north) : gpsApplication.getString(R.string.south); - return(_PhysicalData); - + physicalData.value = gpsApp.getPrefShowDecimalCoordinates() ? + String.format(Locale.getDefault(), "%.9f", Math.abs(number)) : + Location.convert(Math.abs(number), Location.FORMAT_SECONDS).replaceFirst(":", "°").replaceFirst(":", "' ").concat("\""); + physicalData.um = number >= 0 ? gpsApp.getString(R.string.north) : gpsApp.getString(R.string.south); + return(physicalData); case FORMAT_LONGITUDE: // Longitude - _PhysicalData.Value = gpsApplication.getPrefShowDecimalCoordinates() ? - String.format(Locale.getDefault(), "%.9f", Math.abs(Number)) : Location.convert(Math.abs(Number), Location.FORMAT_SECONDS); - _PhysicalData.UM = Number >= 0 ? - gpsApplication.getString(R.string.east) : gpsApplication.getString(R.string.west); - return(_PhysicalData); - + physicalData.value = gpsApp.getPrefShowDecimalCoordinates() ? + String.format(Locale.getDefault(), "%.9f", Math.abs(number)) : + Location.convert(Math.abs(number), Location.FORMAT_SECONDS).replaceFirst(":", "°").replaceFirst(":", "' ").concat("\""); + physicalData.um = number >= 0 ? + gpsApp.getString(R.string.east) : gpsApp.getString(R.string.west); + return(physicalData); case FORMAT_ALTITUDE: // Altitude - switch (gpsApplication.getPrefUM()) { + switch (gpsApp.getPrefUM()) { case UM_METRIC_KMH: case UM_METRIC_MS: - _PhysicalData.Value = String.valueOf(Math.round(Number)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_m); - return(_PhysicalData); + physicalData.value = String.valueOf(Math.round(number)); + physicalData.um = gpsApp.getString(R.string.UM_m); + return(physicalData); case UM_IMPERIAL_MPH: case UM_IMPERIAL_FPS: case UM_NAUTICAL_KN: case UM_NAUTICAL_MPH: - _PhysicalData.Value = String.valueOf(Math.round(Number * M_TO_FT)); - _PhysicalData.UM = gpsApplication.getString(R.string.UM_ft); - return(_PhysicalData); + physicalData.value = String.valueOf(Math.round(number * M_TO_FT)); + physicalData.um = gpsApp.getString(R.string.UM_ft); + return(physicalData); } } - return(_PhysicalData); + return(physicalData); } - public PhysicalData format(long Number, byte Format) { - PhysicalData _PhysicalData = new PhysicalData(); - _PhysicalData.Value = ""; - _PhysicalData.UM = ""; + /** + * It returns a PhysicalData formatted basing on the given criteria and on the Preferences. + * + * @param number The long number to format as Physical Data + * @param format The desired format (FORMAT_LATITUDE, FORMAT_LONGITUDE, FORMAT_ALTITUDE...) + * @return The Physical Data containing number and unit of measurement + */ + public PhysicalData format(long number, byte format) { + PhysicalData physicalData = new PhysicalData(); + physicalData.value = ""; + physicalData.um = ""; - if (Number == NOT_AVAILABLE) return(_PhysicalData); // Returns empty fields if the data is not available + if (number == NOT_AVAILABLE) return(physicalData); // Returns empty fields if the data is not available - switch (Format) { + switch (format) { case FORMAT_DURATION: // Durations - long time = Number / 1000; + long time = number / 1000; String seconds = Integer.toString((int) (time % 60)); String minutes = Integer.toString((int) ((time % 3600) / 60)); String hours = Integer.toString((int) (time / 3600)); @@ -261,23 +286,22 @@ public PhysicalData format(long Number, byte Format) { hours = "0" + hours; } } - _PhysicalData.Value = hours.equals("00") ? minutes + ":" + seconds : hours + ":" + minutes + ":" + seconds; - return(_PhysicalData); - + physicalData.value = hours.equals("00") ? minutes + ":" + seconds : hours + ":" + minutes + ":" + seconds; + return(physicalData); case FORMAT_TIME: // Timestamps - if (gpsApplication.getPrefShowLocalTime()) { + if (gpsApp.getPrefShowLocalTime()) { SimpleDateFormat dfdTime = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); // date and time formatter SimpleDateFormat dfdTimeZone = new SimpleDateFormat("ZZZZZ", Locale.getDefault()); // timezone formatter - _PhysicalData.Value = dfdTime.format(Number); - _PhysicalData.UM = dfdTimeZone.format(Number); - return (_PhysicalData); + physicalData.value = dfdTime.format(number); + physicalData.um = dfdTimeZone.format(number); + return (physicalData); } else { SimpleDateFormat dfdTime = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); // date and time formatter dfdTime.setTimeZone(TimeZone.getTimeZone("GMT")); - _PhysicalData.Value = dfdTime.format(Number); - return (_PhysicalData); + physicalData.value = dfdTime.format(number); + return (physicalData); } } - return(_PhysicalData); + return(physicalData); } -} +} \ No newline at end of file diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/Satellites.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/Satellites.java index 577f50b0..abeaa049 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/Satellites.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/Satellites.java @@ -1,6 +1,9 @@ -/** +/* * Satellites - Java Class for Android - * Created by G.Capelli (BasicAirData) on 26/10/2020 + * Created by G.Capelli on 26/10/2020 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,11 +27,13 @@ import android.os.Build; import androidx.annotation.RequiresApi; -import static eu.basicairdata.graziano.gpslogger.GPSApplication.NOT_AVAILABLE; - - import java.util.ArrayList; +import static eu.basicairdata.graziano.gpslogger.GPSApplication.NOT_AVAILABLE; + +/** + * Stores and manages the updating of the status of the satellites constellations. + */ public class Satellites { //private static final float NO_DATA = 0.0f; @@ -36,7 +41,9 @@ public class Satellites { //private int numSatsInView = NOT_AVAILABLE; private int numSatsUsedInFix = NOT_AVAILABLE; - + /** + * The data structure that describes a satellite + */ private static class Satellite { public int svid; public int constellationType; @@ -44,22 +51,27 @@ private static class Satellite { //public boolean isInView = false; } - + /** + * @return The total number of available satellites + */ public int getNumSatsTotal() { return numSatsTotal; } - + /** + * @return The number of satellites that are used to obtain the FIX + */ public int getNumSatsUsedInFix() { return numSatsUsedInFix; } - // public int getNumSatsInView() { // return numSatsInView; // } - + /** + * Updates the status of the satellites using the Legacy GpsStatus. + */ public void updateStatus(GpsStatus gpsStatus) { if (gpsStatus != null) { int satsTotal = 0; // Total number of Satellites; @@ -81,7 +93,10 @@ public void updateStatus(GpsStatus gpsStatus) { } } - + /** + * Updates the status of the satellites using the new GnssStatus. + * For Android VERSION_CODES.N and above. + */ @RequiresApi(api = Build.VERSION_CODES.N) public void updateStatus(GnssStatus gnssStatus) { if (gnssStatus != null) { diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/SettingsActivity.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/SettingsActivity.java index 01420ba2..53d0455c 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/SettingsActivity.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/SettingsActivity.java @@ -1,6 +1,9 @@ -/** +/* * SettingsActivity - Java Class for Android - * Created by G.Capelli (BasicAirData) on 23/7/2016 + * Created by G.Capelli on 23/7/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,24 +31,22 @@ import android.util.Log; import android.view.MenuItem; +/** + * The Activity that shows and manages the Preference Screen. + */ public class SettingsActivity extends AppCompatActivity { private Toolbar toolbar; @Override protected void onCreate(Bundle savedInstanceState) { - AppCompatDelegate.setDefaultNightMode(Integer.valueOf(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("prefColorTheme", "2"))); - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_settings); - toolbar = findViewById(R.id.id_toolbar2); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle(R.string.menu_settings); - if (savedInstanceState == null) { FragmentSettings wvf = new FragmentSettings(); FragmentManager fm = getSupportFragmentManager(); diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/SpikesChecker.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/SpikesChecker.java index 0bcd7466..71836a0b 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/SpikesChecker.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/SpikesChecker.java @@ -1,6 +1,9 @@ /* * SpikesChecker - Java Class for Android - * Created by G.Capelli (BasicAirData) on 15/9/2016 + * Created by G.Capelli on 15/9/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,58 +23,69 @@ import static eu.basicairdata.graziano.gpslogger.GPSApplication.NOT_AVAILABLE; - +/** + * Checks the evolution of the altitudes with the purpose to detect the altitude spikes. + */ class SpikesChecker { - private long Good_Time = NOT_AVAILABLE; // The time of the last good value + private long goodTime = NOT_AVAILABLE; // The time of the last good value - private double Prev_Altitude = NOT_AVAILABLE; // the previous data loaded - private long Prev_Time = NOT_AVAILABLE; - private float Prev_VerticalSpeed = NOT_AVAILABLE; + private double prevAltitude = NOT_AVAILABLE; // the previous data loaded + private long prevTime = NOT_AVAILABLE; + private float prevVerticalSpeed = NOT_AVAILABLE; - private double New_Altitude = NOT_AVAILABLE; // the new (current) data loaded - private long New_Time = NOT_AVAILABLE; - private float New_VerticalSpeed = NOT_AVAILABLE; + private double newAltitude = NOT_AVAILABLE; // the new (current) data loaded + private long newTime = NOT_AVAILABLE; + private float newVerticalSpeed = NOT_AVAILABLE; - private long Time_Interval = NOT_AVAILABLE; // Interval between fixes (in seconds) - private float VerticalAcceleration; + private long timeInterval = NOT_AVAILABLE; // Interval between fixes (in seconds) + private float verticalAcceleration; - private final float MAX_ACCELERATION; // The maximum vertical acceleration allowed - private final int STABILIZATION_TIME; // Stabilization window, in seconds. It must be > 0 + private final float MAX_ACCELERATION; // The maximum vertical acceleration allowed + private final int STABILIZATION_TIME; // Stabilization window, in seconds. It must be > 0 - // Constructor - SpikesChecker(float max_acceleration, int Stabilization_Time) { - MAX_ACCELERATION = max_acceleration; - STABILIZATION_TIME = Stabilization_Time; + /** + * Creates a SpikesChecker with the given parameters. + * + * @param maxAcceleration The maximum valid acceleration + * @param stabilizationTime The time that passes from an invalid value to the next valid one + */ + SpikesChecker(float maxAcceleration, int stabilizationTime) { + MAX_ACCELERATION = maxAcceleration; + STABILIZATION_TIME = stabilizationTime; } - void load(long Time, double Altitude) { - if (Time > New_Time) { - Prev_Time = New_Time; - New_Time = Time; - Prev_Altitude = New_Altitude; - Prev_VerticalSpeed = New_VerticalSpeed; + /** + * Loads a new sample into the checker. + * + * @param time The time of the sample + * @param altitude The related altitude in meters + */ + void load(long time, double altitude) { + if (time > newTime) { + prevTime = newTime; + newTime = time; + prevAltitude = newAltitude; + prevVerticalSpeed = newVerticalSpeed; } - - Time_Interval = Prev_Time != NOT_AVAILABLE ? (New_Time - Prev_Time) / 1000 : NOT_AVAILABLE; - New_Altitude = Altitude; - - if ((Time_Interval > 0) && (Prev_Altitude != NOT_AVAILABLE)) { - New_VerticalSpeed = (float) (New_Altitude - Prev_Altitude) / Time_Interval; - - if (Prev_VerticalSpeed != NOT_AVAILABLE) { - if (Time_Interval > 1000) VerticalAcceleration = NOT_AVAILABLE; // Prevent Vertical Acceleration value from exploding - else VerticalAcceleration = 2 * (-Prev_VerticalSpeed * Time_Interval + (float)(New_Altitude - Prev_Altitude)) / (Time_Interval * Time_Interval); + timeInterval = prevTime != NOT_AVAILABLE ? (newTime - prevTime) / 1000 : NOT_AVAILABLE; + newAltitude = altitude; + if ((timeInterval > 0) && (prevAltitude != NOT_AVAILABLE)) { + newVerticalSpeed = (float) (newAltitude - prevAltitude) / timeInterval; + if (prevVerticalSpeed != NOT_AVAILABLE) { + if (timeInterval > 1000) verticalAcceleration = NOT_AVAILABLE; // Prevent Vertical Acceleration value from exploding + else verticalAcceleration = 2 * (-prevVerticalSpeed * timeInterval + (float)(newAltitude - prevAltitude)) / (timeInterval * timeInterval); } } - - if (Math.abs(VerticalAcceleration) >= MAX_ACCELERATION) Good_Time = New_Time ; - + if (Math.abs(verticalAcceleration) >= MAX_ACCELERATION) goodTime = newTime; //Log.w("myApp", "[#] SpikesChecker.java - Vertical Acceleration = " + VerticalAcceleration); //Log.w("myApp", "[#] SpikesChecker.java - Validation window = " + (New_Time - Good_Time) / 1000); } + /** + * @return true if the last altitude loaded is valid (is not a spike). + */ boolean isValid() { - return (New_Time - Good_Time) / 1000 >= STABILIZATION_TIME; + return (newTime - goodTime) / 1000 >= STABILIZATION_TIME; } } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/ToolbarActionMode.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/ToolbarActionMode.java index e10bc65c..9e1823e0 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/ToolbarActionMode.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/ToolbarActionMode.java @@ -1,3 +1,24 @@ +/* + * ToolbarActionMode - Java Class for Android + * Created by G.Capelli on 6/01/2019 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package eu.basicairdata.graziano.gpslogger; import android.os.Handler; @@ -11,35 +32,31 @@ import static eu.basicairdata.graziano.gpslogger.GPSApplication.NOT_AVAILABLE; - +/** + * The Actionmode Toolbar for the Tracklist. + * It comes out when one or more Tracks are selected on Tracklist. + * This Toolbar contains the action that could be done with the selected Tracks. + * For example Delete, Share, View, Export... + */ public class ToolbarActionMode implements ActionMode.Callback { - private Menu actionmenu; - private GPSApplication gpsApplication = GPSApplication.getInstance(); - - - // A flag that avoids to start more than one job at a time on Actionmode Toolbar - private boolean ActionmodeButtonPressed = false; - - private final Handler handler_ActionmodeButtonPressed = new Handler(); - private Runnable r_ActionmodeButtonPressed = new Runnable() { + private Menu actionMenu; + private MenuItem menuItemDelete; + private MenuItem menuItemExport; + private MenuItem menuItemShare; + private MenuItem menuItemView; + private MenuItem menuItemEdit; + private boolean isActionmodeButtonPressed = false; // A flag that avoids to start more than one job at a time + + private final GPSApplication gpsApp = GPSApplication.getInstance(); + private final Handler actionmodeButtonPressedHandler = new Handler(); + private final Runnable actionmodeButtonPressedRunnable = new Runnable() { @Override public void run() { setActionmodeButtonPressed(false); } }; - public boolean isActionmodeButtonPressed() { - return ActionmodeButtonPressed; - } - - public void setActionmodeButtonPressed(boolean actionmodeButtonPressed) { - ActionmodeButtonPressed = actionmodeButtonPressed; - if (actionmodeButtonPressed) { - handler_ActionmodeButtonPressed.postDelayed(r_ActionmodeButtonPressed, 500); // The Flag remains active for 500 ms - } else handler_ActionmodeButtonPressed.removeCallbacks(r_ActionmodeButtonPressed); - } - @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { mode.getMenuInflater().inflate(R.menu.card_menu, menu); @@ -49,11 +66,17 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) { @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - actionmenu = menu; - actionmenu.findItem(R.id.cardmenu_share).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - actionmenu.findItem(R.id.cardmenu_view).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - actionmenu.findItem(R.id.cardmenu_export).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - actionmenu.findItem(R.id.cardmenu_delete).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + actionMenu = menu; + menuItemEdit = actionMenu.findItem(R.id.cardmenu_edit); + menuItemEdit.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + menuItemShare = actionMenu.findItem(R.id.cardmenu_share); + menuItemShare.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + menuItemView = actionMenu.findItem(R.id.cardmenu_view); + menuItemView.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + menuItemExport = actionMenu.findItem(R.id.cardmenu_export); + menuItemExport.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + menuItemDelete = actionMenu.findItem(R.id.cardmenu_delete); + menuItemDelete.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); EvaluateVisibility(); return true; } @@ -78,6 +101,10 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { setActionmodeButtonPressed(true); EventBus.getDefault().post(EventBusMSG.ACTION_BULK_SHARE_TRACKS); break; + case R.id.cardmenu_edit: + setActionmodeButtonPressed(true); + EventBus.getDefault().post(EventBusMSG.ACTION_EDIT_TRACK); + break; default: return false; } @@ -89,21 +116,27 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { @Override public void onDestroyActionMode(ActionMode mode) { EventBus.getDefault().unregister(this); - if ((gpsApplication.getNumberOfSelectedTracks() > 0) && gpsApplication.getGPSActivity_activeTab() == 2) { - GPSApplication.getInstance().DeselectAllTracks(); + if ((gpsApp.getNumberOfSelectedTracks() > 0) && gpsApp.getGPSActivityActiveTab() == 2) { + GPSApplication.getInstance().deselectAllTracks(); GPSApplication.getInstance().setLastClickId(NOT_AVAILABLE); } } + /** + * The EventBus receiver for Normal Messages. + */ @Subscribe (threadMode = ThreadMode.MAIN) public void onEvent(EventBusMSGNormal msg) { - switch (msg.MSGType) { + switch (msg.eventBusMSG) { case EventBusMSG.TRACKLIST_SELECT: case EventBusMSG.TRACKLIST_DESELECT: EvaluateVisibility(); } } + /** + * The EventBus receiver for Short Messages. + */ @Subscribe (threadMode = ThreadMode.MAIN) public void onEvent(Short msg) { switch (msg) { @@ -113,22 +146,43 @@ public void onEvent(Short msg) { } } + public boolean isActionmodeButtonPressed() { + return isActionmodeButtonPressed; + } + + /** + * Sets the isActionmodeButtonPressed. + * If sets to true, it starts the handler that resets the value to false. + */ + public void setActionmodeButtonPressed(boolean actionmodeButtonPressed) { + isActionmodeButtonPressed = actionmodeButtonPressed; + if (actionmodeButtonPressed) { + actionmodeButtonPressedHandler.postDelayed(actionmodeButtonPressedRunnable, 500); // The Flag remains active for 500 ms + } else actionmodeButtonPressedHandler.removeCallbacks(actionmodeButtonPressedRunnable); + } + + /** + * Evaluates the visibility of the buttons on the Toolbar basing on the selection, + * the installed apps, and the Preferences. + * It sets also the tooltip text and the icon of the View button. + */ public void EvaluateVisibility() { if (GPSApplication.getInstance().getNumberOfSelectedTracks() > 0) { - actionmenu.findItem(R.id.cardmenu_view).setVisible((gpsApplication.getNumberOfSelectedTracks() <= 1) && (gpsApplication.isContextMenuViewVisible())); - actionmenu.findItem(R.id.cardmenu_share).setVisible(gpsApplication.isContextMenuShareVisible() && (gpsApplication.getPrefExportGPX() || gpsApplication.getPrefExportKML() || gpsApplication.getPrefExportTXT())); - actionmenu.findItem(R.id.cardmenu_export).setVisible(gpsApplication.getPrefExportGPX() || gpsApplication.getPrefExportKML() || gpsApplication.getPrefExportTXT()); - actionmenu.findItem(R.id.cardmenu_delete).setVisible(!gpsApplication.getSelectedTracks().contains(gpsApplication.getCurrentTrack())); - - if (actionmenu.findItem(R.id.cardmenu_view).isVisible()) { - if (!gpsApplication.getViewInApp().equals("")) { - actionmenu.findItem(R.id.cardmenu_view).setTitle(gpsApplication.getString(R.string.card_menu_view, gpsApplication.getViewInApp())); - if (gpsApplication.getViewInAppIcon() != null) - actionmenu.findItem(R.id.cardmenu_view).setIcon(gpsApplication.getViewInAppIcon()); + menuItemView.setVisible((gpsApp.getNumberOfSelectedTracks() <= 1) && (gpsApp.isContextMenuViewVisible())); + menuItemEdit.setVisible(gpsApp.getNumberOfSelectedTracks() <= 1); + menuItemShare.setVisible(gpsApp.isContextMenuShareVisible() && (gpsApp.getPrefExportGPX() || gpsApp.getPrefExportKML() || gpsApp.getPrefExportTXT())); + menuItemExport.setVisible(gpsApp.getPrefExportGPX() || gpsApp.getPrefExportKML() || gpsApp.getPrefExportTXT()); + menuItemDelete.setVisible(!gpsApp.getSelectedTracks().contains(gpsApp.getCurrentTrack())); + + if (menuItemView.isVisible()) { + if (!gpsApp.getViewInApp().equals("")) { + menuItemView.setTitle(gpsApp.getString(R.string.card_menu_view, gpsApp.getViewInApp())); + if (gpsApp.getViewInAppIcon() != null) + menuItemView.setIcon(gpsApp.getViewInAppIcon()); else - actionmenu.findItem(R.id.cardmenu_view).setIcon(R.drawable.ic_visibility_24dp); + menuItemView.setIcon(R.drawable.ic_visibility_24dp); } else { - actionmenu.findItem(R.id.cardmenu_view).setTitle(gpsApplication.getString(R.string.card_menu_view_selector)).setIcon(R.drawable.ic_visibility_24dp); + menuItemView.setTitle(gpsApp.getString(R.string.card_menu_view_selector)).setIcon(R.drawable.ic_visibility_24dp); } } } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/Track.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/Track.java index d8d19910..417c884e 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/Track.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/Track.java @@ -1,6 +1,9 @@ -/** +/* * Track - Java Class for Android - * Created by G.Capelli (BasicAirData) on 1/5/2016 + * Created by G.Capelli on 1/5/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,332 +28,396 @@ import static eu.basicairdata.graziano.gpslogger.GPSApplication.NOT_AVAILABLE; - +/** + * Describes and manages a Track. + */ public class Track { // Constants - private static final double MIN_ALTITUDE_STEP = 8.0; - private static final float MOVEMENT_SPEED_THRESHOLD = 0.5f; // The minimum speed (in m/s) to consider the user in movement - private static final float STANDARD_ACCURACY = 10.0f; - private static final float SECURITY_COEFF = 1.7f; - - private static final int TRACK_TYPE_STEADY = 0; - private static final int TRACK_TYPE_WALK = 1; - private static final int TRACK_TYPE_MOUNTAIN = 2; - private static final int TRACK_TYPE_RUN = 3; - private static final int TRACK_TYPE_BICYCLE = 4; - private static final int TRACK_TYPE_CAR = 5; - private static final int TRACK_TYPE_FLIGHT = 6; - private static final int TRACK_TYPE_ND = NOT_AVAILABLE; + private static final double MIN_ALTITUDE_STEP = 8.0; + private static final float MOVEMENT_SPEED_THRESHOLD = 0.5f; // The minimum speed (in m/s) to consider the user in movement + private static final float STANDARD_ACCURACY = 10.0f; + private static final float SECURITY_COEFFICIENT = 1.7f; + + public static final int TRACK_TYPE_STEADY = 0; + public static final int TRACK_TYPE_WALK = 1; + public static final int TRACK_TYPE_MOUNTAIN = 2; + public static final int TRACK_TYPE_RUN = 3; + public static final int TRACK_TYPE_BICYCLE = 4; + public static final int TRACK_TYPE_CAR = 5; + public static final int TRACK_TYPE_FLIGHT = 6; + public static final int TRACK_TYPE_ND = NOT_AVAILABLE; // Variables - private long id; // Saved in DB - private String Name = ""; // Saved in DB - - private double Start_Latitude = NOT_AVAILABLE; // Saved in DB - private double Start_Longitude = NOT_AVAILABLE; // Saved in DB - private double Start_Altitude = NOT_AVAILABLE; // Saved in DB - private double Start_EGMAltitudeCorrection = NOT_AVAILABLE; - private float Start_Accuracy = STANDARD_ACCURACY;// Saved in DB - private float Start_Speed = NOT_AVAILABLE; // Saved in DB - private long Start_Time = NOT_AVAILABLE; // Saved in DB - - private long LastFix_Time = NOT_AVAILABLE; // Saved in DB - - private double End_Latitude = NOT_AVAILABLE; // Saved in DB - private double End_Longitude = NOT_AVAILABLE; // Saved in DB - private double End_Altitude = NOT_AVAILABLE; // Saved in DB - private double End_EGMAltitudeCorrection = NOT_AVAILABLE; - private float End_Accuracy = STANDARD_ACCURACY;// Saved in DB - private float End_Speed = NOT_AVAILABLE; // Saved in DB - private long End_Time = NOT_AVAILABLE; // Saved in DB - - private double LastStepDistance_Latitude = NOT_AVAILABLE; // Saved in DB - private double LastStepDistance_Longitude = NOT_AVAILABLE; // Saved in DB - private float LastStepDistance_Accuracy = STANDARD_ACCURACY;// Saved in DB - - private double LastStepAltitude_Altitude = NOT_AVAILABLE; // Saved in DB - private float LastStepAltitude_Accuracy = STANDARD_ACCURACY;// Saved in DB - - private double Min_Latitude = NOT_AVAILABLE; // Saved in DB - private double Min_Longitude = NOT_AVAILABLE; // Saved in DB - - private double Max_Latitude = NOT_AVAILABLE; // Saved in DB - private double Max_Longitude = NOT_AVAILABLE; // Saved in DB - - private long Duration = NOT_AVAILABLE; // Saved in DB - private long Duration_Moving = NOT_AVAILABLE; // Saved in DB - - private float Distance = NOT_AVAILABLE; // Saved in DB - private float DistanceInProgress = NOT_AVAILABLE; // Saved in DB - private long DistanceLastAltitude = NOT_AVAILABLE; // Saved in DB - - private double Altitude_Up = NOT_AVAILABLE; // Saved in DB - private double Altitude_Down = NOT_AVAILABLE; // Saved in DB - private double Altitude_InProgress = NOT_AVAILABLE; // Saved in DB - - private float SpeedMax = NOT_AVAILABLE; // Saved in DB - private float SpeedAverage = NOT_AVAILABLE; // Saved in DB - private float SpeedAverageMoving = NOT_AVAILABLE; // Saved in DB - - private long NumberOfLocations = 0; // Saved in DB - private long NumberOfPlacemarks = 0; // Saved in DB - - private int ValidMap = 1; // Saved in DB + private long id; // Saved in DB + private String name = ""; // Saved in DB + private String description = ""; // Saved in DB + // The data related to the Start Point + private double latitudeStart = NOT_AVAILABLE; // Saved in DB + private double longitudeStart = NOT_AVAILABLE; // Saved in DB + private double altitudeStart = NOT_AVAILABLE; // Saved in DB + private double egmAltitudeCorrectionStart = NOT_AVAILABLE; + private float accuracyStart = STANDARD_ACCURACY;// Saved in DB + private float speedStart = NOT_AVAILABLE; // Saved in DB + private long timeStart = NOT_AVAILABLE; // Saved in DB + // The data related to the Last FIX + // added to the track + private long timeLastFix = NOT_AVAILABLE; // Saved in DB + // The data related to the End Point + private double latitudeEnd = NOT_AVAILABLE; // Saved in DB + private double longitudeEnd = NOT_AVAILABLE; // Saved in DB + private double altitudeEnd = NOT_AVAILABLE; // Saved in DB + private double egmAltitudeCorrectionEnd = NOT_AVAILABLE; + private float accuracyEnd = STANDARD_ACCURACY;// Saved in DB + private float speedEnd = NOT_AVAILABLE; // Saved in DB + private long timeEnd = NOT_AVAILABLE; // Saved in DB + // The data related to the Point + // stored as last Step for Distance calculation + private double latitudeLastStepDistance = NOT_AVAILABLE; // Saved in DB + private double longitudeLastStepDistance = NOT_AVAILABLE; // Saved in DB + private float accuracyLastStepDistance = STANDARD_ACCURACY;// Saved in DB + // The data related to the Point + // stored as last Step for Altitude + private double altitudeLastStepAltitude = NOT_AVAILABLE; // Saved in DB + private float accuracyLastStepAltitude = STANDARD_ACCURACY;// Saved in DB + + private double latitudeMin = NOT_AVAILABLE; // Saved in DB + private double longitudeMin = NOT_AVAILABLE; // Saved in DB + + private double latitudeMax = NOT_AVAILABLE; // Saved in DB + private double longitudeMax = NOT_AVAILABLE; // Saved in DB + + private long duration = NOT_AVAILABLE; // Saved in DB + private long durationMoving = NOT_AVAILABLE; // Saved in DB + + private float distance = NOT_AVAILABLE; // Saved in DB + private float distanceInProgress = NOT_AVAILABLE; // Saved in DB + private long distanceLastAltitude = NOT_AVAILABLE; // Saved in DB + + private double altitudeUp = NOT_AVAILABLE; // Saved in DB + private double altitudeDown = NOT_AVAILABLE; // Saved in DB + private double altitudeInProgress = NOT_AVAILABLE; // Saved in DB + + private float speedMax = NOT_AVAILABLE; // Saved in DB + private float speedAverage = NOT_AVAILABLE; // Saved in DB + private float speedAverageMoving = NOT_AVAILABLE; // Saved in DB + + private long numberOfLocations = 0; // Saved in DB + private long numberOfPlacemarks = 0; // Saved in DB + + private int validMap = 1; // Saved in DB // 1 = Map extents valid, OK generation of Thumb - // 0 = Do not generate thumb (track crosses antimeridian) + // 0 = Do not generate thumb (track crosses anti-meridian) - private int Type = TRACK_TYPE_ND; // Saved in DB + private int type = TRACK_TYPE_ND; // Saved in DB // True if the card view is selected - private boolean Selected = false; + private boolean isSelected = false; // The altitude validator (the anti spikes filter): // - Max Acceleration = 12 m/s^2 // - Stabilization time = 4 s - private SpikesChecker AltitudeFilter = new SpikesChecker(12, 4); + private final SpikesChecker altitudeFilter = new SpikesChecker(12, 4); + /** + * Add a LocationExtended to the Track, and updates the Track statistics. + * + * @param location the location to be added to the Track + */ public void add(LocationExtended location) { - if (NumberOfLocations == 0) { + if (numberOfLocations == 0) { // Init "Start" variables - Start_Latitude = location.getLocation().getLatitude(); - Start_Longitude = location.getLocation().getLongitude(); + latitudeStart = location.getLocation().getLatitude(); + longitudeStart = location.getLocation().getLongitude(); if (location.getLocation().hasAltitude()) { - Start_Altitude = location.getLocation().getAltitude(); + altitudeStart = location.getLocation().getAltitude(); } else { - Start_Altitude = NOT_AVAILABLE; + altitudeStart = NOT_AVAILABLE; } - Start_EGMAltitudeCorrection = location.getAltitudeEGM96Correction(); - Start_Speed = location.getLocation().hasSpeed() ? location.getLocation().getSpeed() : NOT_AVAILABLE; - Start_Accuracy = location.getLocation().hasAccuracy() ? location.getLocation().getAccuracy() : STANDARD_ACCURACY; - Start_Time = location.getLocation().getTime(); + egmAltitudeCorrectionStart = location.getAltitudeEGM96Correction(); + speedStart = location.getLocation().hasSpeed() ? location.getLocation().getSpeed() : NOT_AVAILABLE; + accuracyStart = location.getLocation().hasAccuracy() ? location.getLocation().getAccuracy() : STANDARD_ACCURACY; + timeStart = location.getLocation().getTime(); - LastStepDistance_Latitude = Start_Latitude; - LastStepDistance_Longitude = Start_Longitude; - LastStepDistance_Accuracy = Start_Accuracy; + latitudeLastStepDistance = latitudeStart; + longitudeLastStepDistance = longitudeStart; + accuracyLastStepDistance = accuracyStart; - Max_Latitude = Start_Latitude; - Max_Longitude = Start_Longitude; - Min_Latitude = Start_Latitude; - Min_Longitude = Start_Longitude; + latitudeMax = latitudeStart; + longitudeMax = longitudeStart; + latitudeMin = latitudeStart; + longitudeMin = longitudeStart; - if (Name.equals("")) { + if (name.equals("")) { SimpleDateFormat df2 = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US); - Name = df2.format(Start_Time); + name = df2.format(timeStart); } - LastFix_Time = Start_Time; - End_Time = Start_Time; + timeLastFix = timeStart; + timeEnd = timeStart; - Duration_Moving = 0; - Duration = 0; - Distance = 0; + durationMoving = 0; + duration = 0; + distance = 0; } - LastFix_Time = End_Time; + timeLastFix = timeEnd; - End_Latitude = location.getLocation().getLatitude(); - End_Longitude = location.getLocation().getLongitude(); + latitudeEnd = location.getLocation().getLatitude(); + longitudeEnd = location.getLocation().getLongitude(); if (location.getLocation().hasAltitude()) { - End_Altitude = location.getLocation().getAltitude(); + altitudeEnd = location.getLocation().getAltitude(); } else { - End_Altitude = NOT_AVAILABLE; + altitudeEnd = NOT_AVAILABLE; } - End_EGMAltitudeCorrection = location.getAltitudeEGM96Correction(); + egmAltitudeCorrectionEnd = location.getAltitudeEGM96Correction(); - End_Speed = location.getLocation().hasSpeed() ? location.getLocation().getSpeed() : NOT_AVAILABLE; - End_Accuracy = location.getLocation().hasAccuracy() ? location.getLocation().getAccuracy() : STANDARD_ACCURACY; - End_Time = location.getLocation().getTime(); + speedEnd = location.getLocation().hasSpeed() ? location.getLocation().getSpeed() : NOT_AVAILABLE; + accuracyEnd = location.getLocation().hasAccuracy() ? location.getLocation().getAccuracy() : STANDARD_ACCURACY; + timeEnd = location.getLocation().getTime(); - if (End_EGMAltitudeCorrection == NOT_AVAILABLE) getEnd_EGMAltitudeCorrection(); - if (Start_EGMAltitudeCorrection == NOT_AVAILABLE) getStart_EGMAltitudeCorrection(); + if (egmAltitudeCorrectionEnd == NOT_AVAILABLE) getEGMAltitudeCorrectionEnd(); + if (egmAltitudeCorrectionStart == NOT_AVAILABLE) getEGMAltitudeCorrectionStart(); // ---------------------------------------------- Load the new value into antispikes filter - if (End_Altitude != NOT_AVAILABLE) AltitudeFilter.load(End_Time, End_Altitude); + if (altitudeEnd != NOT_AVAILABLE) altitudeFilter.load(timeEnd, altitudeEnd); // ------------------------------------------------------------- Coords for thumb and stats - if (ValidMap != 0) { - if (End_Latitude > Max_Latitude) Max_Latitude = End_Latitude; - if (End_Longitude > Max_Longitude) Max_Longitude = End_Longitude; - if (End_Latitude < Min_Latitude) Min_Latitude = End_Latitude; - if (End_Longitude < Min_Longitude) Min_Longitude = End_Longitude; + if (validMap != 0) { + if (latitudeEnd > latitudeMax) latitudeMax = latitudeEnd; + if (longitudeEnd > longitudeMax) longitudeMax = longitudeEnd; + if (latitudeEnd < latitudeMin) latitudeMin = latitudeEnd; + if (longitudeEnd < longitudeMin) longitudeMin = longitudeEnd; - if (Math.abs(LastStepDistance_Longitude - End_Longitude) > 90) ValidMap = 0; + if (Math.abs(longitudeLastStepDistance - longitudeEnd) > 90) validMap = 0; // YOU PASS FROM -180 TO +180, OR REVERSE. iN THE PACIFIC OCEAN. // in that case the app doesn't generate the thumb map. } // ---------------------------------------------------------------------------------- Times - Duration = End_Time - Start_Time; - if (End_Speed >= MOVEMENT_SPEED_THRESHOLD) Duration_Moving += End_Time - LastFix_Time; + duration = timeEnd - timeStart; + if (speedEnd >= MOVEMENT_SPEED_THRESHOLD) durationMoving += timeEnd - timeLastFix; // --------------------------- Spaces (Distances) increment if distance > sum of accuracies // -- Temp locations for "DistanceTo" Location LastStepDistanceLoc = new Location("TEMP"); - LastStepDistanceLoc.setLatitude(LastStepDistance_Latitude); - LastStepDistanceLoc.setLongitude(LastStepDistance_Longitude); + LastStepDistanceLoc.setLatitude(latitudeLastStepDistance); + LastStepDistanceLoc.setLongitude(longitudeLastStepDistance); Location EndLoc = new Location("TEMP"); - EndLoc.setLatitude(End_Latitude); - EndLoc.setLongitude(End_Longitude); + EndLoc.setLatitude(latitudeEnd); + EndLoc.setLongitude(longitudeEnd); // ----------------------------------- - DistanceInProgress = LastStepDistanceLoc.distanceTo(EndLoc); - float DeltaDistancePlusAccuracy = DistanceInProgress + End_Accuracy; + distanceInProgress = LastStepDistanceLoc.distanceTo(EndLoc); + float DeltaDistancePlusAccuracy = distanceInProgress + accuracyEnd; - if (DeltaDistancePlusAccuracy < DistanceInProgress + End_Accuracy) { - LastStepDistance_Accuracy = DeltaDistancePlusAccuracy; + if (DeltaDistancePlusAccuracy < distanceInProgress + accuracyEnd) { + accuracyLastStepDistance = DeltaDistancePlusAccuracy; //Log.w("myApp", "[#] Track.java - LastStepDistance_Accuracy updated to " + LastStepDistance_Accuracy ); } - if (DistanceInProgress > End_Accuracy + LastStepDistance_Accuracy) { - Distance += DistanceInProgress; - if (DistanceLastAltitude != NOT_AVAILABLE) DistanceLastAltitude += DistanceInProgress; - DistanceInProgress = 0; + if (distanceInProgress > accuracyEnd + accuracyLastStepDistance) { + distance += distanceInProgress; + if (distanceLastAltitude != NOT_AVAILABLE) distanceLastAltitude += distanceInProgress; + distanceInProgress = 0; - LastStepDistance_Latitude = End_Latitude; - LastStepDistance_Longitude = End_Longitude; - LastStepDistance_Accuracy = End_Accuracy; + latitudeLastStepDistance = latitudeEnd; + longitudeLastStepDistance = longitudeEnd; + accuracyLastStepDistance = accuracyEnd; } // Found a first fix with altitude!! - if ((End_Altitude != NOT_AVAILABLE) && (DistanceLastAltitude == NOT_AVAILABLE)) { - DistanceLastAltitude = 0; - Altitude_Up = 0; - Altitude_Down = 0; - if (Start_Altitude == NOT_AVAILABLE) Start_Altitude = End_Altitude; - LastStepAltitude_Altitude = End_Altitude; - LastStepAltitude_Accuracy = End_Accuracy; + if ((altitudeEnd != NOT_AVAILABLE) && (distanceLastAltitude == NOT_AVAILABLE)) { + distanceLastAltitude = 0; + altitudeUp = 0; + altitudeDown = 0; + if (altitudeStart == NOT_AVAILABLE) altitudeStart = altitudeEnd; + altitudeLastStepAltitude = altitudeEnd; + accuracyLastStepAltitude = accuracyEnd; } - if ((LastStepAltitude_Altitude != NOT_AVAILABLE) && (End_Altitude != NOT_AVAILABLE)) { - Altitude_InProgress = End_Altitude - LastStepAltitude_Altitude; + if ((altitudeLastStepAltitude != NOT_AVAILABLE) && (altitudeEnd != NOT_AVAILABLE)) { + altitudeInProgress = altitudeEnd - altitudeLastStepAltitude; // Improve last step accuracy in case of new data elements: - float DeltaAltitudePlusAccuracy = (float) Math.abs(Altitude_InProgress) + End_Accuracy; - if (DeltaAltitudePlusAccuracy <= LastStepAltitude_Accuracy) { - LastStepAltitude_Accuracy = DeltaAltitudePlusAccuracy; - DistanceLastAltitude = 0; + float DeltaAltitudePlusAccuracy = (float) Math.abs(altitudeInProgress) + accuracyEnd; + if (DeltaAltitudePlusAccuracy <= accuracyLastStepAltitude) { + accuracyLastStepAltitude = DeltaAltitudePlusAccuracy; + distanceLastAltitude = 0; //Log.w("myApp", "[#] Track.java - LastStepAltitude_Accuracy updated to " + LastStepAltitude_Accuracy ); } // Evaluate the altitude step convalidation: - if ((Math.abs(Altitude_InProgress) > MIN_ALTITUDE_STEP) && AltitudeFilter.isValid() - && ((float) Math.abs(Altitude_InProgress) > (SECURITY_COEFF * (LastStepAltitude_Accuracy + End_Accuracy)))) { + if ((Math.abs(altitudeInProgress) > MIN_ALTITUDE_STEP) && altitudeFilter.isValid() + && ((float) Math.abs(altitudeInProgress) > (SECURITY_COEFFICIENT * (accuracyLastStepAltitude + accuracyEnd)))) { // Altitude step: // increment distance only if the inclination is relevant (assume deltah=20m in max 5000m) - if (DistanceLastAltitude < 5000) { - float hypotenuse = (float) Math.sqrt((double) (DistanceLastAltitude * DistanceLastAltitude) + (Altitude_InProgress * Altitude_InProgress)); - Distance = Distance + hypotenuse - DistanceLastAltitude; + if (distanceLastAltitude < 5000) { + float hypotenuse = (float) Math.sqrt((double) (distanceLastAltitude * distanceLastAltitude) + (altitudeInProgress * altitudeInProgress)); + distance = distance + hypotenuse - distanceLastAltitude; //Log.w("myApp", "[#] Track.java - Distance += " + (hypotenuse - DistanceLastAltitude)); } //Reset variables - LastStepAltitude_Altitude = End_Altitude; - LastStepAltitude_Accuracy = End_Accuracy; - DistanceLastAltitude = 0; + altitudeLastStepAltitude = altitudeEnd; + accuracyLastStepAltitude = accuracyEnd; + distanceLastAltitude = 0; - if (Altitude_InProgress > 0) Altitude_Up += Altitude_InProgress; // Increment the correct value of Altitude UP/DOWN - else Altitude_Down -= Altitude_InProgress; - Altitude_InProgress = 0; + if (altitudeInProgress > 0) altitudeUp += altitudeInProgress; // Increment the correct value of Altitude UP/DOWN + else altitudeDown -= altitudeInProgress; + altitudeInProgress = 0; } } // --------------------------------------------------------------------------------- Speeds - if ((End_Speed != NOT_AVAILABLE) && (End_Speed > SpeedMax)) SpeedMax = End_Speed; - if (Duration > 0) SpeedAverage = (Distance + DistanceInProgress) / (((float) Duration) / 1000f); - if (Duration_Moving > 0) SpeedAverageMoving = (Distance + DistanceInProgress) / (((float) Duration_Moving) / 1000f); - NumberOfLocations++; + if ((speedEnd != NOT_AVAILABLE) && (speedEnd > speedMax)) speedMax = speedEnd; + if (duration > 0) speedAverage = (distance + distanceInProgress) / (((float) duration) / 1000f); + if (durationMoving > 0) speedAverageMoving = (distance + distanceInProgress) / (((float) durationMoving) / 1000f); + numberOfLocations++; } - // Empty constructor + /** + * Creates a void Track. + */ public Track(){ } - // constructor - public Track(String Name){ - this.Name = Name; - } - - public void FromDB(long id, String Name, String From, String To, - double Start_Latitude,double Start_Longitude, double Start_Altitude, - float Start_Accuracy, float Start_Speed, long Start_Time, long LastFix_Time, - double End_Latitude, double End_Longitude, double End_Altitude, - float End_Accuracy, float End_Speed, long End_Time, - double LastStepDistance_Latitude, double LastStepDistance_Longitude, float LastStepDistance_Accuracy, - double LastStepAltitude_Altitude, float LastStepAltitude_Accuracy, - double Min_Latitude, double Min_Longitude, - double Max_Latitude, double Max_Longitude, - long Duration, long Duration_Moving, float Distance, float DistanceInProgress, - long DistanceLastAltitude, double Altitude_Up, double Altitude_Down, - double Altitude_InProgress, float SpeedMax, float SpeedAverage, - float SpeedAverageMoving, long NumberOfLocations, long NumberOfPlacemarks, - int ValidMap, int Type) { + /** + * Creates a Track with the specified name. + * + * @param name The name of the Track + */ + public Track(String name){ + this.name = name; + } + + /** + * Creates a Track with the given data. + * This method is used to load the Track from the Database. + * + * @param id The id of the Track + * @param name The name of the track + * @param from The description of the start point + * @param to The description of the endpoint + * @param latitudeStart The latitude of the start point + * @param longitudeStart The longitude of the start point + * @param altitudeStart The raw altitude of the start point (without any correction) + * @param accuracyStart The accuracy of the start point + * @param speedStart The speed of the start point + * @param timeStart The time of the start point + * @param timeLastFix The time of the last fix + * @param latitudeEnd The latitude of the endpoint + * @param longitudeEnd The longitude of the endpoint + * @param altitudeEnd The raw altitude of the endpoint (without any correction) + * @param accuracyEnd The accuracy of the endpoint + * @param speedEnd The speed of the endpoint + * @param timeEnd The time of the endpoint + * @param latitudeLastStepDistance The latitude of the point stored as last step for distance calculation + * @param longitudeLastStepDistance The longitude of the point stored as last step for distance calculation + * @param accuracyLastStepDistance The accuracy of the point stored as last step for distance calculation + * @param altitudeLastStepAltitude The altitude of the point stored as last step for altitude + * @param accuracyLastStepAltitude The accuracy of the point stored as last step for altitude + * @param latitudeMin The minimum latitude reached + * @param longitudeMin The minimum longitude reached + * @param latitudeMax The maximum latitude reached + * @param longitudeMax The maximum longitude reached + * @param duration The duration of the Track + * @param durationMoving The time in movement of the Track + * @param distance The distance of the Track + * @param distanceInProgress The part of the distance of the Track not yet validated + * @param distanceLastAltitude The distance walked since the last step of altitude + * @param altitudeUp The total ascending + * @param altitudeDown The total descending + * @param altitudeInProgress The altitude gap since the last altitude step + * @param speedMax The maximum speed reached + * @param speedAverage The average speed based on total time + * @param speedAverageMoving The average speed based on the time in movement + * @param numberOfLocations The number of Locations recorded + * @param numberOfPlacemarks The number of Placemarks recorded + * @param validMap 1 if the map should be drawn + * @param type The type of activity done during the Track recording + * @param description The description of the Track + */ + public void fromDB(long id, String name, String from, String to, + double latitudeStart, double longitudeStart, double altitudeStart, + float accuracyStart, float speedStart, long timeStart, long timeLastFix, + double latitudeEnd, double longitudeEnd, double altitudeEnd, + float accuracyEnd, float speedEnd, long timeEnd, + double latitudeLastStepDistance, double longitudeLastStepDistance, float accuracyLastStepDistance, + double altitudeLastStepAltitude, float accuracyLastStepAltitude, + double latitudeMin, double longitudeMin, + double latitudeMax, double longitudeMax, + long duration, long durationMoving, float distance, float distanceInProgress, + long distanceLastAltitude, double altitudeUp, double altitudeDown, + double altitudeInProgress, float speedMax, float speedAverage, + float speedAverageMoving, long numberOfLocations, long numberOfPlacemarks, + int validMap, int type, String description) { this.id = id; - this.Name = Name; + this.name = name; + this.description = description; - this.Start_Latitude = Start_Latitude; - this.Start_Longitude = Start_Longitude; - this.Start_Altitude = Start_Altitude; - this.Start_Accuracy = Start_Accuracy; - this.Start_Speed = Start_Speed; - this.Start_Time = Start_Time; + this.latitudeStart = latitudeStart; + this.longitudeStart = longitudeStart; + this.altitudeStart = altitudeStart; + this.accuracyStart = accuracyStart; + this.speedStart = speedStart; + this.timeStart = timeStart; - this.LastFix_Time = LastFix_Time; + this.timeLastFix = timeLastFix; - this.End_Latitude = End_Latitude; - this.End_Longitude = End_Longitude; - this.End_Altitude = End_Altitude; - this.End_Accuracy = End_Accuracy; - this.End_Speed = End_Speed; - this.End_Time = End_Time; + this.latitudeEnd = latitudeEnd; + this.longitudeEnd = longitudeEnd; + this.altitudeEnd = altitudeEnd; + this.accuracyEnd = accuracyEnd; + this.speedEnd = speedEnd; + this.timeEnd = timeEnd; - this.LastStepDistance_Latitude = LastStepDistance_Latitude; - this.LastStepDistance_Longitude = LastStepDistance_Longitude; - this.LastStepDistance_Accuracy = LastStepDistance_Accuracy; + this.latitudeLastStepDistance = latitudeLastStepDistance; + this.longitudeLastStepDistance = longitudeLastStepDistance; + this.accuracyLastStepDistance = accuracyLastStepDistance; - this.LastStepAltitude_Altitude = LastStepAltitude_Altitude; - this.LastStepAltitude_Accuracy = LastStepAltitude_Accuracy; + this.altitudeLastStepAltitude = altitudeLastStepAltitude; + this.accuracyLastStepAltitude = accuracyLastStepAltitude; - this.Min_Latitude = Min_Latitude; - this.Min_Longitude = Min_Longitude; + this.latitudeMin = latitudeMin; + this.longitudeMin = longitudeMin; - this.Max_Latitude = Max_Latitude; - this.Max_Longitude = Max_Longitude; + this.latitudeMax = latitudeMax; + this.longitudeMax = longitudeMax; - this.Duration = Duration; - this.Duration_Moving = Duration_Moving; + this.duration = duration; + this.durationMoving = durationMoving; - this.Distance = Distance; - this.DistanceInProgress = DistanceInProgress; - this.DistanceLastAltitude = DistanceLastAltitude; + this.distance = distance; + this.distanceInProgress = distanceInProgress; + this.distanceLastAltitude = distanceLastAltitude; - this.Altitude_Up = Altitude_Up; - this.Altitude_Down = Altitude_Down; - this.Altitude_InProgress = Altitude_InProgress; + this.altitudeUp = altitudeUp; + this.altitudeDown = altitudeDown; + this.altitudeInProgress = altitudeInProgress; - this.SpeedMax = SpeedMax; - this.SpeedAverage = SpeedAverage; - this.SpeedAverageMoving = SpeedAverageMoving; + this.speedMax = speedMax; + this.speedAverage = speedAverage; + this.speedAverageMoving = speedAverageMoving; - this.NumberOfLocations = NumberOfLocations; - this.NumberOfPlacemarks = NumberOfPlacemarks; + this.numberOfLocations = numberOfLocations; + this.numberOfPlacemarks = numberOfPlacemarks; - this.ValidMap = ValidMap; - this.Type = Type; + this.validMap = validMap; + this.type = type; EGM96 egm96 = EGM96.getInstance(); if (egm96 != null) { if (egm96.isEGMGridLoaded()) { - if (Start_Latitude != NOT_AVAILABLE) Start_EGMAltitudeCorrection = egm96.getEGMCorrection(Start_Latitude, Start_Longitude); - if (End_Latitude != NOT_AVAILABLE) End_EGMAltitudeCorrection = egm96.getEGMCorrection(End_Latitude, End_Longitude); + if (latitudeStart != NOT_AVAILABLE) egmAltitudeCorrectionStart = egm96.getEGMCorrection(latitudeStart, longitudeStart); + if (latitudeEnd != NOT_AVAILABLE) egmAltitudeCorrectionEnd = egm96.getEGMCorrection(latitudeEnd, longitudeEnd); } } } - // ------------------------------------------------------------------------ Getters and Setters public long getId() { @@ -362,238 +429,265 @@ public void setId(long id) { } public String getName() { - return Name; + return name; } public void setName(String name) { - Name = name; + this.name = name; + } + + public String getDescription() { + return description; } - public double getStart_Latitude() { - return Start_Latitude; + public void setDescription(String description) { + this.description = description; } - public double getStart_Longitude() { - return Start_Longitude; + public double getLatitudeStart() { + return latitudeStart; } - public double getStart_Altitude() { - return Start_Altitude; + public double getLongitudeStart() { + return longitudeStart; } - public double getStart_EGMAltitudeCorrection() { + public double getAltitudeStart() { + return altitudeStart; + } - if (Start_EGMAltitudeCorrection == NOT_AVAILABLE) { + public double getEGMAltitudeCorrectionStart() { + if (egmAltitudeCorrectionStart == NOT_AVAILABLE) { EGM96 egm96 = EGM96.getInstance(); if (egm96 != null) { if (egm96.isEGMGridLoaded()) { - if (Start_Latitude != NOT_AVAILABLE) - Start_EGMAltitudeCorrection = egm96.getEGMCorrection(Start_Latitude, Start_Longitude); + if (latitudeStart != NOT_AVAILABLE) + egmAltitudeCorrectionStart = egm96.getEGMCorrection(latitudeStart, longitudeStart); } } } - return Start_EGMAltitudeCorrection; + return egmAltitudeCorrectionStart; } - public float getStart_Accuracy() { - return Start_Accuracy; + public float getAccuracyStart() { + return accuracyStart; } - public float getStart_Speed() { - return Start_Speed; + public float getSpeedStart() { + return speedStart; } - public long getStart_Time() { - return Start_Time; + public long getTimeStart() { + return timeStart; } - public long getLastFix_Time() { - return LastFix_Time; + public long getTimeLastFix() { + return timeLastFix; } - public double getEnd_Latitude() { - return End_Latitude; + public double getLatitudeEnd() { + return latitudeEnd; } - public double getEnd_Longitude() { - return End_Longitude; + public double getLongitudeEnd() { + return longitudeEnd; } - public double getEnd_Altitude() { - return End_Altitude; + public double getAltitudeEnd() { + return altitudeEnd; } - public double getEnd_EGMAltitudeCorrection() { - if (End_EGMAltitudeCorrection == NOT_AVAILABLE) { + public double getEGMAltitudeCorrectionEnd() { + if (egmAltitudeCorrectionEnd == NOT_AVAILABLE) { EGM96 egm96 = EGM96.getInstance(); if (egm96 != null) { if (egm96.isEGMGridLoaded()) { - if (End_Latitude != NOT_AVAILABLE) - End_EGMAltitudeCorrection = egm96.getEGMCorrection(End_Latitude, End_Longitude); + if (latitudeEnd != NOT_AVAILABLE) + egmAltitudeCorrectionEnd = egm96.getEGMCorrection(latitudeEnd, longitudeEnd); } } } - return End_EGMAltitudeCorrection; + return egmAltitudeCorrectionEnd; } - public float getEnd_Accuracy() { - return End_Accuracy; + public float getAccuracyEnd() { + return accuracyEnd; } - public float getEnd_Speed() { - return End_Speed; + public float getSpeedEnd() { + return speedEnd; } - public long getEnd_Time() { - return End_Time; + public long getTimeEnd() { + return timeEnd; } - public double getLastStepDistance_Latitude() { - return LastStepDistance_Latitude; + public double getLatitudeLastStepDistance() { + return latitudeLastStepDistance; } - public double getLastStepDistance_Longitude() { - return LastStepDistance_Longitude; + public double getLongitudeLastStepDistance() { + return longitudeLastStepDistance; } - public float getLastStepDistance_Accuracy() { - return LastStepDistance_Accuracy; + public float getAccuracyLastStepDistance() { + return accuracyLastStepDistance; } - public double getLastStepAltitude_Altitude() { - return LastStepAltitude_Altitude; + public double getAltitudeLastStepAltitude() { + return altitudeLastStepAltitude; } - public float getLastStepAltitude_Accuracy() { - return LastStepAltitude_Accuracy; + public float getAccuracyLastStepAltitude() { + return accuracyLastStepAltitude; } - public double getMin_Latitude() { - return Min_Latitude; + public double getLatitudeMin() { + return latitudeMin; } - public double getMin_Longitude() { - return Min_Longitude; + public double getLongitudeMin() { + return longitudeMin; } - public double getMax_Latitude() { - return Max_Latitude; + public double getLatitudeMax() { + return latitudeMax; } - public double getMax_Longitude() { - return Max_Longitude; + public double getLongitudeMax() { + return longitudeMax; } public long getDuration() { - return Duration; + return duration; } - public long getDuration_Moving() { - return Duration_Moving; + public long getDurationMoving() { + return durationMoving; } public float getDistance() { - return Distance; + return distance; } public float getDistanceInProgress() { - return DistanceInProgress; + return distanceInProgress; } public long getDistanceLastAltitude() { - return DistanceLastAltitude; + return distanceLastAltitude; } - public double getAltitude_Up() { - return Altitude_Up; + public double getAltitudeUp() { + return altitudeUp; } - public double getAltitude_Down() { - return Altitude_Down; + public double getAltitudeDown() { + return altitudeDown; } - public double getAltitude_InProgress() { - return Altitude_InProgress; + public double getAltitudeInProgress() { + return altitudeInProgress; } public float getSpeedMax() { - return SpeedMax; + return speedMax; } public float getSpeedAverage() { - return SpeedAverage; + return speedAverage; } public float getSpeedAverageMoving() { - return SpeedAverageMoving; + return speedAverageMoving; } public long getNumberOfLocations() { - return NumberOfLocations; + return numberOfLocations; } public long getNumberOfPlacemarks() { - return NumberOfPlacemarks; + return numberOfPlacemarks; } public int getValidMap() { - return ValidMap; + return validMap; } public int getType() { - return Type; + return type; + } + + public void setType(int type){ + this.type = type; } public boolean isSelected() { - return Selected; + return isSelected; } public void setSelected(boolean selected) { - Selected = selected; + isSelected = selected; } // -------------------------------------------------------------------------------------------- + /** + * @return true if the altitude is valid. false when in the middle of a spike. + */ public boolean isValidAltitude() { - return AltitudeFilter.isValid(); + return altitudeFilter.isValid(); } + /** + * Notifies that a Placemark has been added to the Track into the Database + * + * @return the number of Placemarks on the Track. + */ public long addPlacemark(LocationExtended location) { - this.NumberOfPlacemarks++ ; - - if (Name.equals("")) { + this.numberOfPlacemarks++ ; + // If the Track name has not yet been set, sets it now. + // This means that this Placemark is the first item added to the track. + if (name.equals("")) { SimpleDateFormat df2 = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US); - Name = df2.format(location.getLocation().getTime()); + name = df2.format(location.getLocation().getTime()); } - - return NumberOfPlacemarks; + return numberOfPlacemarks; } + /** + * @return the total distance, including the in-progress part. + */ public float getEstimatedDistance(){ - if (NumberOfLocations == 0) return NOT_AVAILABLE; - if (NumberOfLocations == 1) return 0; - return Distance + DistanceInProgress; - } - - - public double getEstimatedAltitudeUp(boolean EGMCorrection){ + if (numberOfLocations == 0) return NOT_AVAILABLE; + if (numberOfLocations == 1) return 0; + return distance + distanceInProgress; + } + + /** + * Returns the estimated ascending altitude. + * + * @param egmCorrection if true, it estimates the altitude using also the EGM Correction. + * @return the estimated ascending altitude. + */ + public double getEstimatedAltitudeUp(boolean egmCorrection){ // Retrieve EGM Corrections if available - if ((Start_EGMAltitudeCorrection == NOT_AVAILABLE) || (End_EGMAltitudeCorrection == NOT_AVAILABLE)) { + if ((egmAltitudeCorrectionStart == NOT_AVAILABLE) || (egmAltitudeCorrectionEnd == NOT_AVAILABLE)) { EGM96 egm96 = EGM96.getInstance(); if (egm96 != null) { if (egm96.isEGMGridLoaded()) { - if (Start_Latitude != NOT_AVAILABLE) Start_EGMAltitudeCorrection = egm96.getEGMCorrection(Start_Latitude, Start_Longitude); - if (End_Latitude != NOT_AVAILABLE) End_EGMAltitudeCorrection = egm96.getEGMCorrection(End_Latitude, End_Longitude); + if (latitudeStart != NOT_AVAILABLE) egmAltitudeCorrectionStart = egm96.getEGMCorrection(latitudeStart, longitudeStart); + if (latitudeEnd != NOT_AVAILABLE) egmAltitudeCorrectionEnd = egm96.getEGMCorrection(latitudeEnd, longitudeEnd); } } } double egmcorr = 0; - if ((EGMCorrection) && ((Start_EGMAltitudeCorrection != NOT_AVAILABLE) && (End_EGMAltitudeCorrection != NOT_AVAILABLE))) { - egmcorr = Start_EGMAltitudeCorrection - End_EGMAltitudeCorrection; + if ((egmCorrection) && ((egmAltitudeCorrectionStart != NOT_AVAILABLE) && (egmAltitudeCorrectionEnd != NOT_AVAILABLE))) { + egmcorr = egmAltitudeCorrectionStart - egmAltitudeCorrectionEnd; } - double dresultUp = Altitude_InProgress > 0 ? Altitude_Up + Altitude_InProgress : Altitude_Up; + double dresultUp = altitudeInProgress > 0 ? altitudeUp + altitudeInProgress : altitudeUp; dresultUp -= egmcorr < 0 ? egmcorr : 0; - double dresultDown = Altitude_InProgress < 0 ? Altitude_Down - Altitude_InProgress : Altitude_Down; + double dresultDown = altitudeInProgress < 0 ? altitudeDown - altitudeInProgress : altitudeDown; dresultDown -= egmcorr > 0 ? egmcorr : 0; if (dresultUp < 0) { @@ -607,25 +701,30 @@ public double getEstimatedAltitudeUp(boolean EGMCorrection){ return dresultUp; } - - public double getEstimatedAltitudeDown(boolean EGMCorrection){ + /** + * Returns the estimated descending altitude. + * + * @param egmCorrection if true, it estimates the altitude using also the EGM Correction. + * @return the estimated descending altitude. + */ + public double getEstimatedAltitudeDown(boolean egmCorrection){ // Retrieve EGM Corrections if available - if ((Start_EGMAltitudeCorrection == NOT_AVAILABLE) || (End_EGMAltitudeCorrection == NOT_AVAILABLE)) { + if ((egmAltitudeCorrectionStart == NOT_AVAILABLE) || (egmAltitudeCorrectionEnd == NOT_AVAILABLE)) { EGM96 egm96 = EGM96.getInstance(); if (egm96 != null) { if (egm96.isEGMGridLoaded()) { - if (Start_Latitude != NOT_AVAILABLE) Start_EGMAltitudeCorrection = egm96.getEGMCorrection(Start_Latitude, Start_Longitude); - if (End_Latitude != NOT_AVAILABLE) End_EGMAltitudeCorrection = egm96.getEGMCorrection(End_Latitude, End_Longitude); + if (latitudeStart != NOT_AVAILABLE) egmAltitudeCorrectionStart = egm96.getEGMCorrection(latitudeStart, longitudeStart); + if (latitudeEnd != NOT_AVAILABLE) egmAltitudeCorrectionEnd = egm96.getEGMCorrection(latitudeEnd, longitudeEnd); } } } double egmcorr = 0; - if ((EGMCorrection) && ((Start_EGMAltitudeCorrection != NOT_AVAILABLE) && (End_EGMAltitudeCorrection != NOT_AVAILABLE))) { - egmcorr = Start_EGMAltitudeCorrection - End_EGMAltitudeCorrection; + if ((egmCorrection) && ((egmAltitudeCorrectionStart != NOT_AVAILABLE) && (egmAltitudeCorrectionEnd != NOT_AVAILABLE))) { + egmcorr = egmAltitudeCorrectionStart - egmAltitudeCorrectionEnd; } - double dresultUp = Altitude_InProgress > 0 ? Altitude_Up + Altitude_InProgress : Altitude_Up; + double dresultUp = altitudeInProgress > 0 ? altitudeUp + altitudeInProgress : altitudeUp; dresultUp -= egmcorr < 0 ? egmcorr : 0; - double dresultDown = Altitude_InProgress < 0 ? Altitude_Down - Altitude_InProgress : Altitude_Down; + double dresultDown = altitudeInProgress < 0 ? altitudeDown - altitudeInProgress : altitudeDown; dresultDown -= egmcorr > 0 ? egmcorr : 0; if (dresultUp < 0) { @@ -639,89 +738,101 @@ public double getEstimatedAltitudeDown(boolean EGMCorrection){ return dresultDown; } - public double getEstimatedAltitudeGap(boolean EGMCorrection){ - return getEstimatedAltitudeUp(EGMCorrection) - getEstimatedAltitudeDown(EGMCorrection); + /** + * Returns the estimated gap of altitude. + * The Altitude Gap is the difference between the current altitude and the + * altitude of the start point. + * + * @param egmCorrection if true, it estimates the altitude using also the EGM Correction. + * @return the estimated altitude gap. + */ + public double getEstimatedAltitudeGap(boolean egmCorrection){ + return getEstimatedAltitudeUp(egmCorrection) - getEstimatedAltitudeDown(egmCorrection); } - + /** + * @return the overall direction of the Track. + */ public float getBearing() { - if (End_Latitude != NOT_AVAILABLE) { - if (((Start_Latitude == End_Latitude) && (Start_Longitude == End_Longitude)) || (Distance == 0)) + if (latitudeEnd != NOT_AVAILABLE) { + if (((latitudeStart == latitudeEnd) && (longitudeStart == longitudeEnd)) || (distance == 0)) return NOT_AVAILABLE; - Location EndLoc = new Location("TEMP"); - EndLoc.setLatitude(End_Latitude); - EndLoc.setLongitude(End_Longitude); - Location StartLoc = new Location("TEMP"); - StartLoc.setLatitude(Start_Latitude); - StartLoc.setLongitude(Start_Longitude); - float BTo = StartLoc.bearingTo(EndLoc); - if (BTo < 0) BTo += 360f; - return BTo; + Location endLoc = new Location("TEMP"); + endLoc.setLatitude(latitudeEnd); + endLoc.setLongitude(longitudeEnd); + Location startLoc = new Location("TEMP"); + startLoc.setLatitude(latitudeStart); + startLoc.setLongitude(longitudeStart); + float bTo = startLoc.bearingTo(endLoc); + if (bTo < 0) bTo += 360f; + return bTo; } return NOT_AVAILABLE; } - - // Returns the time, based on preferences (Total or Moving) + /** + * @return the time, based on preferences (Total or Moving). + */ public long getPrefTime() { - GPSApplication gpsApplication = GPSApplication.getInstance(); - int pTime = gpsApplication.getPrefShowTrackStatsType(); + GPSApplication gpsApp = GPSApplication.getInstance(); + int pTime = gpsApp.getPrefShowTrackStatsType(); switch (pTime) { case 0: // Total based - return Duration; + return duration; case 1: // Moving based - return Duration_Moving; + return durationMoving; default: - return Duration; + return duration; } } - - // Returns the average speed, based on preferences (Total or Moving) + /** + * @return the average speed, based on preferences (Total or Moving) + */ public float getPrefSpeedAverage() { - if (NumberOfLocations == 0) return NOT_AVAILABLE; - GPSApplication gpsApplication = GPSApplication.getInstance(); - int pTime = gpsApplication.getPrefShowTrackStatsType(); + if (numberOfLocations == 0) return NOT_AVAILABLE; + GPSApplication gpsApp = GPSApplication.getInstance(); + int pTime = gpsApp.getPrefShowTrackStatsType(); switch (pTime) { case 0: // Total based - return SpeedAverage; + return speedAverage; case 1: // Moving based - return SpeedAverageMoving; + return speedAverageMoving; default: - return SpeedAverage; + return speedAverage; } } - - public int getTrackType() { - - //if (Type != TRACK_TYPE_ND) return Type; - - if ((Distance == NOT_AVAILABLE) || (SpeedMax == NOT_AVAILABLE)) { - if (NumberOfPlacemarks == 0) return TRACK_TYPE_ND; + /** + * @return the track Type. If not set, it returns an estimation of the activity Type, basing on Track's data. + */ + public int getEstimatedTrackType() { + if (type != TRACK_TYPE_ND) return type; + if ((distance == NOT_AVAILABLE) || (speedMax == NOT_AVAILABLE)) { + if (numberOfPlacemarks == 0) return TRACK_TYPE_ND; else return TRACK_TYPE_STEADY; } - if ((Distance < 15.0f) || (SpeedMax == 0.0f) || (SpeedAverageMoving == NOT_AVAILABLE)) return TRACK_TYPE_STEADY; - if (SpeedMax < (7.0f / 3.6f)) { - if ((Altitude_Up != NOT_AVAILABLE) && (Altitude_Down != NOT_AVAILABLE)) - if ((Altitude_Down + Altitude_Up > (0.1f * Distance)) && (Distance > 500.0f)) return TRACK_TYPE_MOUNTAIN; + if ((distance < 15.0f) || (speedMax == 0.0f) || (speedAverageMoving == NOT_AVAILABLE)) return TRACK_TYPE_STEADY; + if (speedMax < (7.0f / 3.6f)) { + if ((altitudeUp != NOT_AVAILABLE) && (altitudeDown != NOT_AVAILABLE)) + if ((altitudeDown + altitudeUp > (0.1f * distance)) && (distance > 500.0f)) return TRACK_TYPE_MOUNTAIN; else return TRACK_TYPE_WALK; } - if (SpeedMax < (15.0f / 3.6f)) { - if (SpeedAverageMoving > 8.0f / 3.6f) return TRACK_TYPE_RUN; + if (speedMax < (15.0f / 3.6f)) { + if (speedAverageMoving > 8.0f / 3.6f) return TRACK_TYPE_RUN; else { - if ((Altitude_Up != NOT_AVAILABLE) && (Altitude_Down != NOT_AVAILABLE)) - if ((Altitude_Down + Altitude_Up > (0.1f * Distance)) && (Distance > 500.0f)) return TRACK_TYPE_MOUNTAIN; + if ((altitudeUp != NOT_AVAILABLE) && (altitudeDown != NOT_AVAILABLE)) + if ((altitudeDown + altitudeUp > (0.1f * distance)) && (distance > 500.0f)) return TRACK_TYPE_MOUNTAIN; else return TRACK_TYPE_WALK; } } - if (SpeedMax < (50.0f / 3.6f)) { - if ((SpeedAverageMoving + SpeedMax) / 2 > 35.0f / 3.6f) return TRACK_TYPE_CAR; - if ((SpeedAverageMoving + SpeedMax) / 2 > 20.0f / 3.6) return TRACK_TYPE_BICYCLE; - else if ((SpeedAverageMoving + SpeedMax) / 2 > 12.0f / 3.6f) return TRACK_TYPE_RUN; + if (speedMax < (50.0f / 3.6f)) { + if ((speedAverageMoving + speedMax) / 2 > 35.0f / 3.6f) return TRACK_TYPE_CAR; + if ((speedAverageMoving + speedMax) / 2 > 20.0f / 3.6) return TRACK_TYPE_BICYCLE; + else if ((speedAverageMoving + speedMax) / 2 > 12.0f / 3.6f) return TRACK_TYPE_RUN; else { - if ((Altitude_Up != NOT_AVAILABLE) && (Altitude_Down != NOT_AVAILABLE)) - if ((Altitude_Down + Altitude_Up > (0.1f * Distance)) && (Distance > 500.0f)) + if ((altitudeUp != NOT_AVAILABLE) && (altitudeDown != NOT_AVAILABLE)) + if ((altitudeDown + altitudeUp > (0.1f * distance)) && (distance > 500.0f)) return TRACK_TYPE_MOUNTAIN; else return TRACK_TYPE_WALK; } @@ -736,9 +847,8 @@ public int getTrackType() { else return TRACK_TYPE_WALK; }*/ } - if ((Altitude_Up != NOT_AVAILABLE) && (Altitude_Down != NOT_AVAILABLE)) - if ((Altitude_Down + Altitude_Up > 5000.0) && (SpeedMax > 300.0f / 3.6f)) return TRACK_TYPE_FLIGHT; - + if ((altitudeUp != NOT_AVAILABLE) && (altitudeDown != NOT_AVAILABLE)) + if ((altitudeDown + altitudeUp > 5000.0) && (speedMax > 300.0f / 3.6f)) return TRACK_TYPE_FLIGHT; return TRACK_TYPE_CAR; } } diff --git a/app/src/main/java/eu/basicairdata/graziano/gpslogger/TrackAdapter.java b/app/src/main/java/eu/basicairdata/graziano/gpslogger/TrackAdapter.java index 6d2bc75b..e1370f6f 100644 --- a/app/src/main/java/eu/basicairdata/graziano/gpslogger/TrackAdapter.java +++ b/app/src/main/java/eu/basicairdata/graziano/gpslogger/TrackAdapter.java @@ -1,6 +1,9 @@ -/** +/* * TrackAdapter - Java Class for Android - * Created by G.Capelli (BasicAirData) on 19/6/2016 + * Created by G.Capelli on 19/6/2016 + * This file is part of BasicAirData GPS Logger + * + * Copyright (C) 2011 BasicAirData * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,35 +42,38 @@ import static eu.basicairdata.graziano.gpslogger.GPSApplication.NOT_AVAILABLE; - +/** + * The Adapter for the Card View of the Tracklist. + */ class TrackAdapter extends RecyclerView.Adapter { - boolean isLightTheme = false; - - private final List dataSet; - - private static final int[] trackType = { - R.drawable.ic_tracktype_place_24dp, - R.drawable.ic_tracktype_walk_24dp, - R.drawable.ic_tracktype_mountain_24dp, - R.drawable.ic_tracktype_run_24dp, - R.drawable.ic_tracktype_bike_24dp, - R.drawable.ic_tracktype_car_24dp, - R.drawable.ic_tracktype_flight_24dp + private static final int[] TRACK_TYPE = { // The indexes must match the Track Types: + R.drawable.ic_tracktype_place_24dp, // Track.TRACK_TYPE_STEADY = 0; + R.drawable.ic_tracktype_walk_24dp, // Track.TRACK_TYPE_WALK = 1; + R.drawable.ic_tracktype_mountain_24dp, // Track.TRACK_TYPE_MOUNTAIN = 2; + R.drawable.ic_tracktype_run_24dp, // Track.TRACK_TYPE_RUN = 3; + R.drawable.ic_tracktype_bike_24dp, // Track.TRACK_TYPE_BICYCLE = 4; + R.drawable.ic_tracktype_car_24dp, // Track.TRACK_TYPE_CAR = 5; + R.drawable.ic_tracktype_flight_24dp // Track.TRACK_TYPE_FLIGHT = 6; }; - private static final Bitmap bmpCurrentTrackRecording = BitmapFactory.decodeResource(GPSApplication.getInstance().getResources(), R.mipmap.ic_recording_48dp); - private static final Bitmap bmpCurrentTrackPaused = BitmapFactory.decodeResource(GPSApplication.getInstance().getResources(), R.mipmap.ic_paused_white_48dp); + private static final Bitmap BMP_CURRENT_TRACK_RECORDING = BitmapFactory.decodeResource(GPSApplication.getInstance().getResources(), R.mipmap.ic_recording_48dp); + private static final Bitmap BMP_CURRENT_TRACK_PAUSED = BitmapFactory.decodeResource(GPSApplication.getInstance().getResources(), R.mipmap.ic_paused_white_48dp); - private long StartAnimationTime = 0; - private long PointsCount = GPSApplication.getInstance().getCurrentTrack().getNumberOfLocations() + GPSApplication.getInstance().getCurrentTrack().getNumberOfPlacemarks(); + private final List dataSet; + boolean isLightTheme; + private long startAnimationTime = 0; + private long pointsCount = GPSApplication.getInstance().getCurrentTrack().getNumberOfLocations() + GPSApplication.getInstance().getCurrentTrack().getNumberOfPlacemarks(); + /** + * The ViewHolder for the TrackAdapter. + */ class TrackHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { private final PhysicalDataFormatter phdformatter = new PhysicalDataFormatter(); private PhysicalData phd; private Track track; - private int TT; + private int tt; private final CardView card; private final TextView textViewTrackName; @@ -83,23 +89,19 @@ class TrackHolder extends RecyclerView.ViewHolder implements View.OnClickListene private final ImageView imageViewPulse; private final ImageView imageViewIcon; - @Override public void onClick(View v) { if (GPSApplication.getInstance().getJobsPending() == 0) { track.setSelected(!track.isSelected()); card.setSelected(track.isSelected()); - GPSApplication.getInstance().setLastClickId(track.getId()); GPSApplication.getInstance().setLastClickState(track.isSelected()); //Log.w("myApp", "[#] TrackAdapter.java - " + (track.isSelected() ? "Selected" : "Deselected") + " id = " + GPSApplication.getInstance().getLastClickId()); - EventBus.getDefault().post(new EventBusMSGNormal(track.isSelected() ? EventBusMSG.TRACKLIST_SELECT : EventBusMSG.TRACKLIST_DESELECT, track.getId())); //Log.w("myApp", "[#] TrackAdapter.java - Selected track id = " + track.getId()); } } - @Override public boolean onLongClick(View view) { if ((GPSApplication.getInstance().getJobsPending() == 0) @@ -111,16 +113,12 @@ public boolean onLongClick(View view) { return false; } - TrackHolder(View itemView) { super(itemView); - itemView.setOnClickListener(this); itemView.setOnLongClickListener(this); - // CardView card = itemView.findViewById(R.id.card_view); - // TextViews textViewTrackName = itemView.findViewById(R.id.id_textView_card_TrackName); textViewTrackDescription = itemView.findViewById(R.id.id_textView_card_TrackDesc); @@ -131,12 +129,10 @@ public boolean onLongClick(View view) { textViewTrackAverageSpeed = itemView.findViewById(R.id.id_textView_card_averagespeed); textViewTrackGeopoints = itemView.findViewById(R.id.id_textView_card_geopoints); textViewTrackPlacemarks = itemView.findViewById(R.id.id_textView_card_placemarks); - // ImageViews imageViewThumbnail = itemView.findViewById(R.id.id_imageView_card_minimap); imageViewPulse = itemView.findViewById(R.id.id_imageView_card_pulse); imageViewIcon = itemView.findViewById(R.id.id_imageView_card_tracktype); - if (isLightTheme) { imageViewThumbnail.setColorFilter(GPSApplication.colorMatrixColorFilter); imageViewPulse.setColorFilter(GPSApplication.colorMatrixColorFilter); @@ -144,21 +140,24 @@ public boolean onLongClick(View view) { } } - + /** + * Updates the statistics of the current track's card, using the given data. + * + * @param trk the track containing the data + */ void UpdateTrackStats(Track trk) { //textViewTrackName.setText(trk.getName()); - if (trk.getNumberOfLocations() >= 1) { phd = phdformatter.format(trk.getEstimatedDistance(),PhysicalDataFormatter.FORMAT_DISTANCE); - textViewTrackLength.setText(phd.Value + " " + phd.UM); + textViewTrackLength.setText(phd.value + " " + phd.um); phd = phdformatter.format(trk.getPrefTime(),PhysicalDataFormatter.FORMAT_DURATION); - textViewTrackDuration.setText(phd.Value); + textViewTrackDuration.setText(phd.value); phd = phdformatter.format(trk.getEstimatedAltitudeGap(GPSApplication.getInstance().getPrefEGM96AltitudeCorrection()),PhysicalDataFormatter.FORMAT_ALTITUDE); - textViewTrackAltitudeGap.setText(phd.Value + " " + phd.UM); + textViewTrackAltitudeGap.setText(phd.value + " " + phd.um); phd = phdformatter.format(trk.getSpeedMax(),PhysicalDataFormatter.FORMAT_SPEED); - textViewTrackMaxSpeed.setText(phd.Value + " " + phd.UM); + textViewTrackMaxSpeed.setText(phd.value + " " + phd.um); phd = phdformatter.format(trk.getPrefSpeedAverage(),PhysicalDataFormatter.FORMAT_SPEED_AVG); - textViewTrackAverageSpeed.setText(phd.Value + " " + phd.UM); + textViewTrackAverageSpeed.setText(phd.value + " " + phd.um); } else { textViewTrackLength.setText(""); textViewTrackDuration.setText(""); @@ -169,46 +168,49 @@ void UpdateTrackStats(Track trk) { textViewTrackGeopoints.setText(String.valueOf(trk.getNumberOfLocations())); textViewTrackPlacemarks.setText(String.valueOf(trk.getNumberOfPlacemarks())); - TT = trk.getTrackType(); - if (TT != NOT_AVAILABLE) imageViewIcon.setImageResource(trackType[TT]); + tt = trk.getEstimatedTrackType(); + if (tt != NOT_AVAILABLE) imageViewIcon.setImageResource(TRACK_TYPE[tt]); else imageViewIcon.setImageBitmap(null); - if (GPSApplication.getInstance().getRecording()) { - imageViewThumbnail.setImageBitmap(bmpCurrentTrackRecording); + if (GPSApplication.getInstance().isRecording()) { + imageViewThumbnail.setImageBitmap(BMP_CURRENT_TRACK_RECORDING); imageViewPulse.setVisibility(View.VISIBLE); - if ((PointsCount != trk.getNumberOfLocations()+trk.getNumberOfPlacemarks()) && (System.currentTimeMillis() - StartAnimationTime >= 700L)) { - PointsCount = trk.getNumberOfLocations()+trk.getNumberOfPlacemarks(); + if ((pointsCount != trk.getNumberOfLocations()+trk.getNumberOfPlacemarks()) && (System.currentTimeMillis() - startAnimationTime >= 700L)) { + pointsCount = trk.getNumberOfLocations()+trk.getNumberOfPlacemarks(); Animation sunRise = AnimationUtils.loadAnimation(GPSApplication.getInstance().getApplicationContext(), R.anim.record_pulse); imageViewPulse.startAnimation(sunRise); - StartAnimationTime = System.currentTimeMillis(); + startAnimationTime = System.currentTimeMillis(); } } else { imageViewPulse.setVisibility(View.INVISIBLE); - imageViewThumbnail.setImageBitmap(bmpCurrentTrackPaused); + imageViewThumbnail.setImageBitmap(BMP_CURRENT_TRACK_PAUSED); } } - + /** + * Binds a card using the given data. + * + * @param trk the track containing the data + */ void BindTrack(Track trk) { track = trk; - card.setSelected(track.isSelected()); - imageViewPulse.setVisibility(View.INVISIBLE); textViewTrackName.setText(track.getName()); - textViewTrackDescription.setText(GPSApplication.getInstance().getString(R.string.track_id) + " " + track.getId()); - + if (track.getDescription().isEmpty()) + textViewTrackDescription.setText(GPSApplication.getInstance().getString(R.string.track_id) + " " + track.getId()); + else textViewTrackDescription.setText(track.getDescription()); if (trk.getNumberOfLocations() >= 1) { phd = phdformatter.format(track.getEstimatedDistance(),PhysicalDataFormatter.FORMAT_DISTANCE); - textViewTrackLength.setText(phd.Value + " " + phd.UM); + textViewTrackLength.setText(phd.value + " " + phd.um); phd = phdformatter.format(track.getPrefTime(),PhysicalDataFormatter.FORMAT_DURATION); - textViewTrackDuration.setText(phd.Value); + textViewTrackDuration.setText(phd.value); phd = phdformatter.format(track.getEstimatedAltitudeGap(GPSApplication.getInstance().getPrefEGM96AltitudeCorrection()),PhysicalDataFormatter.FORMAT_ALTITUDE); - textViewTrackAltitudeGap.setText(phd.Value + " " + phd.UM); + textViewTrackAltitudeGap.setText(phd.value + " " + phd.um); phd = phdformatter.format(track.getSpeedMax(),PhysicalDataFormatter.FORMAT_SPEED); - textViewTrackMaxSpeed.setText(phd.Value + " " + phd.UM); + textViewTrackMaxSpeed.setText(phd.value + " " + phd.um); phd = phdformatter.format(track.getPrefSpeedAverage(),PhysicalDataFormatter.FORMAT_SPEED_AVG); - textViewTrackAverageSpeed.setText(phd.Value + " " + phd.UM); + textViewTrackAverageSpeed.setText(phd.value + " " + phd.um); } else { textViewTrackLength.setText(""); textViewTrackDuration.setText(""); @@ -219,12 +221,12 @@ void BindTrack(Track trk) { textViewTrackGeopoints.setText(String.valueOf(track.getNumberOfLocations())); textViewTrackPlacemarks.setText(String.valueOf(track.getNumberOfPlacemarks())); - TT = trk.getTrackType(); - if (TT != NOT_AVAILABLE) imageViewIcon.setImageResource(trackType[TT]); + tt = trk.getEstimatedTrackType(); + if (tt != NOT_AVAILABLE) imageViewIcon.setImageResource(TRACK_TYPE[tt]); else imageViewIcon.setImageBitmap(null); if (GPSApplication.getInstance().getCurrentTrack().getId() == track.getId()) { - imageViewThumbnail.setImageBitmap (GPSApplication.getInstance().getRecording() ? bmpCurrentTrackRecording : bmpCurrentTrackPaused); + imageViewThumbnail.setImageBitmap (GPSApplication.getInstance().isRecording() ? BMP_CURRENT_TRACK_RECORDING : BMP_CURRENT_TRACK_PAUSED); } else { Glide.clear(imageViewThumbnail); @@ -240,27 +242,22 @@ void BindTrack(Track trk) { } } - TrackAdapter(List data) { - synchronized(data) { this.dataSet = data; } } - @Override public TrackHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new TrackHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.card_trackinfo, parent, false)); } - @Override public void onBindViewHolder(TrackHolder holder, int listPosition) { holder.BindTrack(dataSet.get(listPosition)); } - @Override public int getItemCount() { return dataSet.size(); diff --git a/app/src/main/res/anim/record_pulse.xml b/app/src/main/res/anim/record_pulse.xml index feac42bd..0cd178a2 100644 --- a/app/src/main/res/anim/record_pulse.xml +++ b/app/src/main/res/anim/record_pulse.xml @@ -1,4 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_lock_24.xml b/app/src/main/res/drawable/ic_lock_24.xml new file mode 100644 index 00000000..d6191026 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_done_24dp.xml b/app/src/main/res/drawable/ic_pause_24.xml similarity index 78% rename from app/src/main/res/drawable/ic_done_24dp.xml rename to app/src/main/res/drawable/ic_pause_24.xml index 899cbb68..13d6d2ec 100644 --- a/app/src/main/res/drawable/ic_done_24dp.xml +++ b/app/src/main/res/drawable/ic_pause_24.xml @@ -6,5 +6,5 @@ android:tint="?attr/colorControlNormal"> + android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/> diff --git a/app/src/main/res/drawable/ic_record_24.xml b/app/src/main/res/drawable/ic_record_24.xml new file mode 100644 index 00000000..d1918301 --- /dev/null +++ b/app/src/main/res/drawable/ic_record_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_stop_24.xml b/app/src/main/res/drawable/ic_stop_24.xml new file mode 100644 index 00000000..625ac706 --- /dev/null +++ b/app/src/main/res/drawable/ic_stop_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_unlock_24.xml b/app/src/main/res/drawable/ic_unlock_24.xml new file mode 100644 index 00000000..be9bd087 --- /dev/null +++ b/app/src/main/res/drawable/ic_unlock_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/launch_screen.xml b/app/src/main/res/drawable/launch_screen.xml index 5dc63e70..85d5494d 100644 --- a/app/src/main/res/drawable/launch_screen.xml +++ b/app/src/main/res/drawable/launch_screen.xml @@ -2,7 +2,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-land/fragment_track.xml b/app/src/main/res/layout-land/fragment_track.xml index 8ae7ba1a..125336c3 100644 --- a/app/src/main/res/layout-land/fragment_track.xml +++ b/app/src/main/res/layout-land/fragment_track.xml @@ -2,7 +2,10 @@ + + + + @@ -106,11 +118,10 @@ @@ -122,7 +133,12 @@ android:background="@color/colorCardBackground_Data" android:baselineAligned="false" android:weightSum="2" - android:padding="5dp" + android:paddingTop="2dp" + android:paddingBottom="6dp" + android:paddingLeft="8dp" + android:paddingStart="8dp" + android:paddingRight="5dp" + android:paddingEnd="5dp" android:layout_toRightOf="@+id/id_imageView_card_minimap" android:layout_toEndOf="@+id/id_imageView_card_minimap" android:layout_below="@+id/id_card_separator"> @@ -262,7 +278,7 @@ android:textAppearance="@style/TextAppearanceCardSmall" android:id="@+id/id_textView_card_placemarksLabel" android:layout_marginTop="9dp" - android:text="@string/card_trackinfo_placemarks" /> + android:text="@string/card_trackinfo_annotations" /> + android:textColor="@color/colorPrimaryLight"/> - + + android:textAppearance="@style/TextAppearanceLargeNumbers" /> + android:textAppearance="@style/TextAppearanceLargeNumbers" /> + + android:textAppearance="@style/TextAppearanceLargeNumbers" /> + android:textAppearance="@style/TextAppearanceLargeNumbers" /> + android:textAppearance="@style/TextAppearanceLargeNumbers" /> + android:textAppearance="@style/TextAppearanceLargeNumbers" /> + android:hint="@string/dlg_enter_the_description" > diff --git a/app/src/main/res/layout/fragment_recording_controls.xml b/app/src/main/res/layout/fragment_recording_controls.xml index 1bc99caf..4dd6aa4c 100644 --- a/app/src/main/res/layout/fragment_recording_controls.xml +++ b/app/src/main/res/layout/fragment_recording_controls.xml @@ -2,7 +2,10 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_tracklist.xml b/app/src/main/res/layout/fragment_tracklist.xml index 29280f3d..4887aca1 100644 --- a/app/src/main/res/layout/fragment_tracklist.xml +++ b/app/src/main/res/layout/fragment_tracklist.xml @@ -2,7 +2,10 @@ - Neuer Track\n\nAufnahme von Wegpunkten\noder Hinzufügen von Ortsmarkern.\nVerwenden Sie eine der Schaltflächen unten + Neuer Track + Beginne mit der Aufzeichnung von Trackpoints\noder füge eine Anmerkung\nmit einer der folgenden Tasten hinzu - Ortsmarker + Aufzeichnen + Pause + Anmerkung + Stopp + Sperren + Entsperren + Track abschließen + Symbolleiste gesperrt + Nichts zum Speichern + Die Aufzeichnung beginnt, sobald das GPS-Signal gefunden wurde + Die Anmerkung wird hinzugefügt, sobald das GPS-Signal gefunden wurde + OK + Anmerkungen Wegpunkte Einstellungen - Aufzeichnung beendet - Doppelklick, um Aufzeichnung zu beenden Track in Trackliste gespeichert Tracks exportiert in /GPSLogger Ordner Über + Online-Hilfe Beenden - - Ortsmarker hinzufügen - Beschreibung eingeben + + Neue Anmerkung + Beschreibung hinzufügen Hinzufügen - Abbrechen Trackliste leer Länge @@ -90,13 +103,14 @@ M Geschw Ø Geschw Punkte - Orte + Anmerk Exportieren Anzeigen in ... In %1$s anzeigen Löschen Teilen mit ... + Details bearbeiten Möchten Sie auch alle exportierten Dateien der ausgewählten Tracks löschen? Möchten Sie die ausgewählten Tracks wirklich löschen? @@ -177,10 +191,11 @@ Download abgeschlossen Download ist fehlgeschlagen. Versuchen Sie es später noch einmal Der aktive Track ist nicht leer + Kein Internetbrowser installiert ver. - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger zeichnet Tracks mit Hilfe von GPS auf. Sie können sie in Ihren bevorzugten Viewer ansehen, teilen und exportieren Sie sie im Ordner \"GPSLogger\" Ihres Geräts in KML, GPX und TXT-Format.\n\nDieses Programm ist freie Software, veröffentlicht unter den Bedingungen von der GNU General Public License.\nFür mehr Details siehe: http://www.gnu.org/licenses/gpl.txt\n\nFür EGM96 Geoid Daten Haftungsausschluss siehe: http://earth-info.nga.mil/GandG/disclaim.html - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger zeichnet Tracks mit Hilfe von GPS auf. Sie können sie in Ihren bevorzugten Viewer ansehen, teilen und exportieren Sie sie im Ordner \"GPSLogger\" Ihres Geräts in KML, GPX und TXT-Format.\n\nDieses Programm ist freie Software, veröffentlicht unter den Bedingungen von der GNU General Public License.\nFür mehr Details siehe: http://www.gnu.org/licenses/gpl.txt\n\nFür EGM96 Geoid Daten Haftungsausschluss siehe: http://earth-info.nga.mil/GandG/disclaim.html\n\nWenn Ihnen diese App gefällt, nehmen Sie sich bitte einen Moment Zeit, um GPS-Logger im Google Store zu bewerten. + Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger zeichnet Tracks mit Hilfe von GPS auf. Sie können sie in Ihren bevorzugten Viewer ansehen, teilen und exportieren Sie sie im Ordner \"GPSLogger\" Ihres Geräts in KML, GPX und TXT-Format.\n\nDieses Programm ist freie Software, veröffentlicht unter den Bedingungen von der GNU General Public License.\nFür mehr Details siehe: http://www.gnu.org/licenses/gpl.txt + Wenn Ihnen diese App gefällt, nehmen Sie sich bitte einen Moment Zeit, um GPS-Logger im Google Store zu bewerten. Schließen App bewerten Der Store ist nicht erreichbar diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 9464b51e..2d5d3e1a 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1,7 +1,10 @@ - Nuevo itinerario\n\nEmpezar la grabación\no añadir una marca de posición\nseleccionando uno de los botones de la barra inferior + Nuevo itinerario + Empezar a grabar puntos de seguimiento\no añadir una anotación\nusando uno de los botones de abajo - Marcadores + Grabar + Pausar + Anotar + Detener + Bloquear + Desbloquear + Finalizar itinerario + La barra inferior está bloqueada + Nada que guardar + La grabación empezará cuando se encuentre la señal de GPS + La anotación se tomará cuando se encuentre la señal de GPS + OK + Anotaciones Pos. GPS Ajustes - Itinerario finalizado - Doble click para finalizar este itinerario Itinerario archivado Itinerarios exportados en la carpeta /GPSLogger Acerca de + Ayuda en línea Salir - - Añadir marcador - Introduce la descripción + + Añadir anotación + Ingresar la descripción Añadir - Cancelar Archivo vacío Distancia @@ -90,13 +103,14 @@ Vel max Vel med Puntos - Marcad. + Anot. Exportar Visualizar en ... Visualizar en %1$s Eliminar Compartir con ... + Editar detalles ¿También desea eliminar todos los archivos exportados de los itinerarios seleccionados? ¿Está seguro/a que desea eliminar los itinerarios selecionados? @@ -130,7 +144,7 @@ Mostrar velocidades en Visor de itinerarios Ningún visor GPX/KML instalado - Seleccionar cada vez + Preguntar cada vez SEGUIMIENTO Densidad espacial Máxima @@ -177,10 +191,11 @@ Descarga completada No se pudo descargar. Inténtalo más tarde El itinerario actual no está vacío + No hay un navegador de internet instalado ver. - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger registra itinerarios usando la posición GPS. El itinerario se puede visualizar en su app preferida, compartir y exportar a una carpeta de su dispositivo en formatos KML, GPX y TXT.\n\nEs un software libre, publicado bajo los términos de la licencia GNU General Public.\nMás detalles en el enlace: http://www.gnu.org/licenses/gpl.txt\n\nEl aviso legal del archivo EGM96: http://earth-info.nga.mil/GandG/disclaim.html - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger registra itinerarios usando la posición GPS. El itinerario se puede visualizar en su app preferida, compartir y exportar a una carpeta de su dispositivo en formatos KML, GPX y TXT.\n\nEs un software libre, publicado bajo los términos de la licencia GNU General Public.\nMás detalles en el enlace: http://www.gnu.org/licenses/gpl.txt\n\nEl aviso legal del archivo EGM96: http://earth-info.nga.mil/GandG/disclaim.html\n\n Disfrute de esta aplicación, por favor, tome un momento para calificar GPS Logger en la Play Store. + Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger registra itinerarios usando la posición GPS. El itinerario se puede visualizar en su app preferida, compartir y exportar a una carpeta de su dispositivo en formatos KML, GPX y TXT.\n\nEs un software libre, publicado bajo los términos de la licencia GNU General Public.\nMás detalles en el enlace: http://www.gnu.org/licenses/gpl.txt + Disfrute de esta aplicación, por favor, tome un momento para calificar GPS Logger en la Play Store. Cerrar Valorar app Imposible conectar con el Store diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a7e55e5b..56de9c7d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1,7 +1,10 @@ - Nouveau Tracé\n\nLancer l\'enregistrement des points\nou ajouter un repère\nà l\'aide des bouton ci-dessous + Nouveau Tracé + Lancer l\'enregistrement des points\nou ajouter une annotation\nà l\'aide des bouton ci-dessous - Repères + Enregistrer + Pause + Annoter + Stop + Verrouiller + Déverrouiller + Finaliser le tracé + La barre inférieure est verrouillée + Rien à enregistrer + L\'enregistrement commencera quand le signal GPS sera trouvé + L\'annotation sera prise en compte quand le signal GPS sera trouvé + OK + Annotations Points de tracé Paramètres - Tracé finalisé - Double-cliquer pour finaliser ce tracé Tracé archivé Tracés exportés dans le dossier /GPSLogger À propos + Aide en ligne Éteindre - - Ajouter un repère - Saisissez la description + + Ajouter une annotation + Entrer une description Ajouter - Annuler Archives vides Longueur @@ -90,13 +103,14 @@ Vit max Vit moy Points - Repères + Annot Exporter Ouvrir avec... Ouvrir avec %1$s Supprimer Partager avec ... + Modifier les infos Voulez-vous également supprimer tous les fichiers exportés des tracés sélectionnés ? Voulez-vous vraiment supprimer les tracés sélectionnés ? @@ -128,9 +142,9 @@ Impérial Aérien / Nautique Afficher les vitesses en - Visionneuse des Tracés + Visionneuse de Tracés Aucune visionneuse GPX/KML installée - Sélectionner à chaque fois + Demander à chaque fois TRAÇAGE Densité de Collecte Maximale @@ -177,10 +191,11 @@ Téléchargement terminé Échec du téléchargement. Veuillez réessayer plus tard Le tracé actif n\'est pas vide + Aucun navigateur internet installé ver. - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger enregistre vos parcours en utilisant le GPS. Vous pouvez les afficher dans votre visualiseur préféré, les partager et les exporter dans le dossier GPSLogger de votre appareil au format KML, GPX et TXT.\n\nCe programme est un logiciel libre, publié sous les termes de la GNU General Public License.\nPour plus de détails, voir : http://www.gnu.org/licenses/gpl.txt\n\nPour la mise en garde sur les fichiers EGM96 geoid, voir : http://earth-info.nga.mil/GandG/disclaim.html - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger enregistre vos parcours en utilisant le GPS. Vous pouvez les afficher dans votre visualiseur préféré, les partager et les exporter dans le dossier GPSLogger de votre appareil au format KML, GPX et TXT.\n\nCe programme est un logiciel libre, publié sous les termes de la GNU General Public License.\nPour plus de détails, voir : http://www.gnu.org/licenses/gpl.txt\n\nPour la mise en garde sur les fichiers EGM96 geoid, voir : http://earth-info.nga.mil/GandG/disclaim.html\n\nSi vous aimez cette application, vous pouvez prendre un instant pour noter GPS Logger sur le Google Play Store. + Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger enregistre vos parcours en utilisant le GPS. Vous pouvez les afficher dans votre visualiseur préféré, les partager et les exporter dans le dossier GPSLogger de votre appareil au format KML, GPX et TXT.\n\nCe programme est un logiciel libre, publié sous les termes de la GNU General Public License.\nPour plus de détails, voir : http://www.gnu.org/licenses/gpl.txt + Si vous aimez cette application, vous pouvez prendre un instant pour noter GPS Logger sur le Google Play Store. Fermer Noter l\'Appli Impossible d\'accéder au Store diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 0067d2c8..ceaeb2f7 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1,7 +1,10 @@ Útvonal Időtartam Max Sebesség - Átl Sebesség + Átl. Sebesség Távolság - Átfogó Irány + Irány Szintkülönbség - Új Útvonal\n\nÚtvonal pontok rögzítéséhez\nvagy hely jelölő hozzáadásához\nhasználja a lenti gombokat + Új Útvonal + Indítsd el az útvonalpontok Rögzítését\nvagy adj hozzá Jegyzetet\naz alábbi gombok egyikével - Hely jelölők + Rögzítés + Szünet + Jegyzet + Leállítás + Zárolás + Feloldás + Útvonal véglegesítés + Gombsor lezárva + Nincs mit menteni + Rögzítés akkor indul el, ha lesz GPS jel + Jegyzet akkor készül el, ha lesz GPS jel + OK + Jegyzetek Útpontok Beállítások - Útvonal rögzítés vége - Dupla kattintásra Útvonal rögzítés vége - Útvonal mentve az Útvonal listába - Útvonal exportálva a /GPSLogger mappába + Útvonal mentve az Útvonalak listába + Útvonal exportálva /GPSLogger mappába Névjegy + Online súgó Kikapcsolás - - Hely jelölő hozzáadása - Leírás - Hozzáadás - Mégse + + Jegyzet hozzáadása + Leírás + Hozzáad - Útvonal lista üres + Útvonalak lista üres Hossz Időtartam Szint Δ Max Seb Átl Seb Pontok - Helyek + Jegyzet Exportálás Nézd meg... Nézd meg %1$s Törlés Megosztás... + Részletek szerkesztése - A kiválasztott útvonal összes exportált fájlját törölni szeretné? - Valóban szeretné törölni a kiválasztott útvonalat? + Kiválasztott útvonal összes exportált fájlját törölni szeretné? + Valóban szeretné törölni a kiválasztott útvonalakat? Igen Nem Mégse Fájl nem írható Funkció eléréséhez kérjük engedélyezd a tárolóhelyhez való hozzáférést - Háttérben kikapcsolták az alkalmazást. + Az alkalmazás leállt miközben a háttérben rögzített Kérjük, ellenőrizze az Android beállításokat, hogy az alkalmazás nincs-e optimalizálva az akkumulátorhoz és nem korlátozott-e a háttérben futás. Beállítások megnyitása @@ -118,8 +132,8 @@ Világos Decimális koordináta formátum Koordináta megjelenítése Decimális számrendszerben - A GPS idő megjelenítése a helyi időzónában - a globális GPS idő helyett + Helyi idő megjelenítése + globális GPS idő helyett Irány Megjelenítése Fok Égtáj @@ -128,9 +142,9 @@ Angolszász Légi / Tengeri Sebesség mértékegység - Útvonal néző - Nincs GPX/KML néző telepítve - Választás mindig + Útvonal megjelenítő + Nincs GPX/KML megjelenítő telepítve + Kiválasztás minden egyes alkalommal Útvonal rögzítés Rögzítési sűrűség Maximum @@ -146,7 +160,7 @@ 66 láb GPS Frissítési idő Elérhető legrövidebb - 1 másodperc (legjobb pontosság) + 1 másodperc (legnagyobb pontosság) 2 másodperc 3 másodperc (kevésbé pontos) MAGASSÁG KORREKCIÓ @@ -157,18 +171,18 @@ Eltolás Nincs eltolás definiálva EXPORTÁLÁS - Útvonal exportálása TXT-be - Egyszerű szöveg, vesszővel tagolt értékek - Útvonal exportálása KML-be + Útvonal exportálás TXT-be + Egyszerű szöveg, vesszővel tagolt értékekkel + Útvonal exportálás KML-be Keyhole Markup nyelv (v.2.2) - Útvonal exportálása GPX-be + Útvonal exportálás GPX-be Szabványos GPS eXchange formátum - Szabványos GPX verzió + GPX Standard verzió GPX 1.0 GPX 1.1 KML Magasság mód - Abszolút - Földre vetített + Tengerszinttől + Föld felszínre Útvonal statisztika alapja Teljes idő Mozgásban töltött idő @@ -176,11 +190,12 @@ Letöltési hiba Letöltés kész Sikertelen letöltés. Kérjük, próbálja újra később - Az aktív útvonal nem üres + Aktív útvonal nem üres + Nincs Internet böngésző telepítve ver. - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger a GPS segítségével rögzíti az útvonalat. Megtekintheti őket az Ön által preferált viewer programban, megoszthatja és exportálhatja azokat a készülék GPSLogger mappájából KML, GPX és TXT formátumban.\n\nEz a program szabad felhasználású szoftver, a GNU General Public License feltételei szerint kiadva.\nTovábbi részletek: http://www.gnu.org/licenses/gpl.txt\n\nAz EGM96 geoid fájl felelősség vállalásáért lásd: http://earth-info.nga.mil/GandG/disclaim.html - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger a GPS segítségével rögzíti az útvonalat. Megtekintheti őket az Ön által preferált viewer programban, megoszthatja és exportálhatja azokat a készülék GPSLogger mappájából KML, GPX és TXT formátumban.\n\nEz a program szabad felhasználású szoftver, a GNU General Public License feltételei szerint kiadva.\nTovábbi részletek: http://www.gnu.org/licenses/gpl.txt\n\nAz EGM96 geoid fájl felelősség vállalásáért lásd: http://earth-info.nga.mil/GandG/disclaim.html\n\nHa szereti ezt az alkalmazást, kérjük, szánjon egy pillanatot arra, hogy értékelje a GPS Loggert a Google Áruházban. + Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger GPS segítségével rögzíti az útvonalakat. Megtekintheti őket az Ön által preferált megjelenítő programban, megoszthatja vagy exportálhatja készüléke GPSLogger mappájába KML, GPX és TXT formátumban.\n\nEz a program ingyenes szoftver, amelyet a GNU General Public License feltételei szerint adtak ki.\nTovábbi információ: http://www.gnu.org/licenses/gpl.txt + Ha szereti ezt az alkalmazást, kérjük, szánjon egy pillanatot arra, hogy értékelje a GPS Loggert a Google Play Áruházban. Bezárás Értékelés Nem érhető el a Store diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 84ce4e44..8c131939 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,7 +1,10 @@ - Nuovo itinerario\n\nInizia a registrare punti\no aggiungi un\'annotazione\nusando uno dei pulsanti qui sotto + Nuovo itinerario + Inizia a registrare punti\no aggiungi un\'annotazione\nusando uno dei pulsanti qui sotto - Annotazioni + Registra + Pausa + Annota + Stop + Blocca + Sblocca + Finalizza Itinerario + La barra inferiore è bloccata + Niente da salvare + La registrazione inizierà quando il segnale GPS verrà trovato + L\'annotazione verrà presa quando il segnale GPS verrà trovato + OK + Annotazioni Punti GPS Impostazioni - Itinerario finalizzato - Doppio clic per finalizzare questo itinerario Itinerario archiviato Itinerari esportati nella cartella /GPSLogger Info + Guida in linea Spegni - - Aggiungi Annotazione - Inserisci una descrizione + + Aggiungi annotazione + Inserisci una descrizione Aggiungi - Annulla Archivio vuoto Lungh @@ -90,13 +103,14 @@ Vel max Vel med Punti - Annotaz + Annot Esporta Visualizza in ... Visualizza in %1$s Elimina Condividi con ... + Modifica dettagli Vuoi anche eliminare tutti i file esportati degli itinerari selezionati? Vuoi veramente eliminare gli itinerari selezionati? @@ -177,10 +191,11 @@ Download completato Download non riuscito. Riprova più tardi L\'itinerario attivo non è vuoto + Nessun browser web installato ver. - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger registra i tuoi percorsi utilizzando il GPS. È possibile visualizzarli nel tuo visualizzatore preferito, condividerli ed esportarli nella cartella /GPSLogger del dispositivo in formato KML, GPX e TXT.\n\nQuesto programma è software libero, rilasciato sotto i termini della GNU General Public license.\nPer maggiori dettagli si veda: http://www.gnu.org/licenses/gpl.txt\n\nPer l\'EGM96 geoid file disclaimer vedi: http://earth-info.nga.mil/GandG/disclaim.html - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger registra i tuoi percorsi utilizzando il GPS. È possibile visualizzarli nel tuo visualizzatore preferito, condividerli ed esportarli nella cartella /GPSLogger del dispositivo in formato KML, GPX e TXT.\n\nQuesto programma è software libero, rilasciato sotto i termini della GNU General Public license.\nPer maggiori dettagli si veda: http://www.gnu.org/licenses/gpl.txt\n\nPer l\'EGM96 geoid file disclaimer vedi: http://earth-info.nga.mil/GandG/disclaim.html\n\nSe ti piace utilizzare questa applicazione, prenditi un momento per valutare GPS Logger nel Google Store. + Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger registra i tuoi percorsi utilizzando il GPS. È possibile visualizzarli nel tuo visualizzatore preferito, condividerli ed esportarli nella cartella /GPSLogger del dispositivo in formato KML, GPX e TXT.\n\nQuesto programma è software libero, rilasciato sotto i termini della GNU General Public license.\nPer maggiori dettagli si veda: http://www.gnu.org/licenses/gpl.txt + Se ti piace utilizzare questa applicazione, prenditi un momento per valutare GPS Logger nel Google Play Store. Chiudi Valuta questa app Impossibile accedere allo Store diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 45d0e9e9..cbbdeb65 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1,7 +1,10 @@ - 新しい追跡\n\n下のボタンを使用して\n追跡ポイントの記録を開始\nまたは目印を追加します + 新しい追跡 - 目印 追跡ポイント 設定 - 追跡完了 - ダブルクリックするとこの追跡を終了します 追跡リストに追跡を保存しました /GPSLogger フォルダーに追跡をエクスポートしました アプリについて シャットダウン - - 目印を追加 - 説明を入力してください + 追加 - キャンセル 追跡リストは空です 長さ @@ -90,7 +86,6 @@ 最高速度 平均速度 ポイント - 場所 エクスポート 表示 ... @@ -179,8 +174,8 @@ 有効な追跡が空ではありません ver. - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger は、GPS を使用して、追跡を記録します。 それをお好みのビューアで表示したり、KML、GPX、TXT 形式で共有、およびお使いのデバイスの GPSLogger フォルダーにエクスポートすることができます。\n\nこのプログラムは GNU 一般公衆ライセンスの条件の下でリリースされるフリー ソフトウェアです。\n詳細は、こちらを参照してください: http://www.gnu.org/licenses/gpl.txt\n\nEGM96 ジオイド ファイルの免責事項はこちらを参照してください: http://earth-info.nga.mil/GandG/disclaim.html - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger は、GPS を使用して、追跡を記録します。 それをお好みのビューアで表示したり、KML、GPX、TXT 形式で共有、およびお使いのデバイスの GPSLogger フォルダーにエクスポートすることができます。\n\nこのプログラムは GNU 一般公衆ライセンスの条件の下でリリースされるフリー ソフトウェアです。\n詳細は、こちらを参照してください: http://www.gnu.org/licenses/gpl.txt\n\nEGM96 ジオイド ファイルの免責事項はこちらを参照してください: http://earth-info.nga.mil/GandG/disclaim.html\n\nこのアプリを使って楽しんでいただけたら、少し時間をいただき Google ストアで GPS Logger を評価してください。 + Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger は、GPS を使用して、追跡を記録します。 それをお好みのビューアで表示したり、KML、GPX、TXT 形式で共有、およびお使いのデバイスの GPSLogger フォルダーにエクスポートすることができます。\n\nこのプログラムは GNU 一般公衆ライセンスの条件の下でリリースされるフリー ソフトウェアです。\n詳細は、こちらを参照してください: http://www.gnu.org/licenses/gpl.txt + このアプリを使って楽しんでいただけたら、少し時間をいただき Google ストアで GPS Logger を評価してください。 閉じる このアプリを評価 ストアにアクセスできません diff --git a/app/src/main/res/values-large/dimens.xml b/app/src/main/res/values-large/dimens.xml index 0664e5e3..d8f5c474 100644 --- a/app/src/main/res/values-large/dimens.xml +++ b/app/src/main/res/values-large/dimens.xml @@ -2,7 +2,10 @@ - Novo Trajeto\n\nInicie gravação dos pontos\nou adicione um marcador\nusando um dos botões abaixo + Novo Trajeto + Inicie a gravação dos Pontos\nou adicione uma Anotação\nusando um dos botões abaixo - Marcadores + Gravar + Pausar + Anotar + Parar + Bloquear + Desbloquear + Finalizar trajeto + A barra inferior está bloqueada + Nada para salvar + A gravação começará quando o sinal de GPS for encontrado + A anotação será adicionada quando o sinal GPS for encontrado + OK + Anotações Pontos Preferências - Trajeto finalizado - Clique duas vezes para terminar este trajeto Trajeto salvo no Arquivo Trajetos exportados na pasta /GPSLogger Sobre + Ajuda on-line Desligar - - Adicionar marcador - Entre com a descrição + + Adicionar anotação + Entre com a descrição Adicionar - Cancelar Arquivo vazio Distância @@ -90,13 +103,14 @@ Vel Máx Vel méd Pontos - Locais + Anot. Exportar Ver em ... Ver em %1$s Deletar Compartilhe com ... + Editar detalhes Você também deseja excluir todos os arquivos exportados dos trajetos selecionados? Você realmente deseja excluir os trajetos selecionados? @@ -177,10 +191,11 @@ Download completo Falha no download. Tente novamente mais tarde O trajeto ativo não está vazio + Nenhum navegador de internet instalado ver. - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger grava seus percursos utilizando o GPS. Você pode visualizar eles no seu visualizador preferido, compartilhar e exportar eles na pasta GPSLogger do seu dispositivo nos formatos KML, GPX e TXT.\n\nEste programa é um software livre, publicado sob os termos da GNU General Public License.\nPara mais detalhes ver: http://www.gnu.org/licenses/gpl.txt\n\nPara o arquivo de contrato do EGM96 geoid ver: http://earth-info.nga.mil/GandG/disclaim.html - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger grava seus percursos utilizando o GPS. Você pode visualizar eles no seu visualizador preferido, compartilhar e exportar eles na pasta GPSLogger do seu dispositivo nos formatos KML, GPX e TXT.\n\nEste programa é um software livre, publicado sob os termos da GNU General Public License.\nPara mais detalhes ver: http://www.gnu.org/licenses/gpl.txt\n\nPara o arquivo de contrato do EGM96 geoid ver: http://earth-info.nga.mil/GandG/disclaim.html\n\nSe gostar de usar este app, por favor, dedique um momento para avaliar o GPS Logger na Google Store. + Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger grava seus percursos utilizando o GPS. Você pode visualizar eles no seu visualizador preferido, compartilhar e exportar eles na pasta GPSLogger do seu dispositivo nos formatos KML, GPX e TXT.\n\nEste programa é um software livre, publicado sob os termos da GNU General Public License.\nPara mais detalhes ver: http://www.gnu.org/licenses/gpl.txt + Se gostar de usar este app, por favor, dedique um momento para avaliar o GPS Logger na Google Store. Fechar Avalia este App Incapaz de acessar a Loja diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index f0a66453..f85f2e0c 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1,7 +1,10 @@ - Novo Percurso\n\nComeçar a gravação de Pontos\nou adicionar um Lugar\nusando um dos botões abaixo + Nova gravação de percurso + Começar a gravação de Pontos\nou adicionar uma Anotação\nusando um dos botões abaixo - Lugares + Gravar + Pausa + Anotar + Parar + Bloquear + Desbloquear + Finalizar Percurso + A barra inferior está bloqueada + Nada para guardar + A gravação começará quando o sinal de GPS for encontrado + A anotação será efetuada quando o sinal de GPS for encontrado + OK + Anotações Pontos Definições - Rastreio acabado - Clique duplo para acabar de seguir - Percurso salvo na tracklist + Percurso arquivado Trajetos exportados para a pasta /GPSLogger Sobre + Ajuda online Sair - - Adicionar lugar - Descrição + + Adicionar anotação + Introduza a descrição Adicionar - Cancelar Arquivo vazio Distancia @@ -90,13 +103,14 @@ Vel Máx Vel Méd Pontos - Lugares + Anot Exportar Ver em... Ver em %1$s Apagar Partilhar com... + Editar detalhes Também pretende eliminar todos os ficheiros exportados dos percursos selecionados? Pretende eliminar os percursos selecionados? @@ -126,14 +140,14 @@ Sistema de medição Métrico Imperial - Aérea / Náutica + Aéreo / Náutico Mostrar velocidades em Visualizador de Percursos Nenhum Visualizador de GPX/KML instalado Selecionar cada vez RASTREAMENTO - Coletando a densidade - Máximo + Densidade de coleta + Máxima 1 metro 2 metros 5 metros @@ -177,10 +191,11 @@ Transferência concluída Falha na transferência. Tente novamente. O percurso ativo não está vazio + Nenhum navegador instalado ver. - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nO BasicAirData GPS Logger regista os seus percursos utilizando GPS. Pode consultá-los no seu visualizador preferido, partilhá-los e exportá-los para a pasta GPSLogger no seu dispositivo nos formatos KML, GPX e TXT.\n\nEste software é gratuito, sendo editado nos termos da GNU General Public License.\nPara mais informações, consulte: http://www.gnu.org/licenses/gpl.txt\n\nConsulte a exclusão de responsabilidade do ficheiro EGM96 geoid em: http://earth-info.nga.mil/GandG/disclaim.html - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nO BasicAirData GPS Logger regista os seus percursos utilizando GPS. Pode consultá-los no seu visualizador preferido, partilhá-los e exportá-los para a pasta GPSLogger no seu dispositivo nos formatos KML, GPX e TXT.\n\nEste software é gratuito, sendo editado nos termos da GNU General Public License.\nPara mais informações, consulte: http://www.gnu.org/licenses/gpl.txt\n\nConsulte a exclusão de responsabilidade do ficheiro EGM96 geoid em: http://earth-info.nga.mil/GandG/disclaim.html\n\nSe aprecia a aplicação, dedique um momento a classificar o GPS Logger na Google Store. + Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nO BasicAirData GPS Logger regista os seus percursos utilizando GPS. Pode consultá-los no seu visualizador preferido, partilhá-los e exportá-los para a pasta GPSLogger no seu dispositivo nos formatos KML, GPX e TXT.\n\nEste software é gratuito, sendo editado nos termos da GNU General Public License.\nPara mais informações, consulte: http://www.gnu.org/licenses/gpl.txt + Se aprecia a aplicação, dedique um momento a classificar o GPS Logger na Google Play Store. Fechar Classificar este app Não é possível aceder à Loja diff --git a/app/src/main/res/values-small/dimens.xml b/app/src/main/res/values-small/dimens.xml index 65539fc3..db4a954f 100644 --- a/app/src/main/res/values-small/dimens.xml +++ b/app/src/main/res/values-small/dimens.xml @@ -2,7 +2,10 @@ - 新轨迹\n\n开始记录路点\n或者使用下方其中一个按钮\n添加位置标记 + 新轨迹 + 开始记录路点\n或者使用下方其中一个按钮\n添加批注 - 位置标记 + 记录 + 暂停 + 批注 + 停止 + 锁定 + 解锁 + 完成轨迹 + 底部栏已锁定 + 没有要保存的数据 + 当找到 GPS 信号时,记录将开始 + 当找到 GPS 信号时,将添加批注 + 确定 + 批注 路点 设置 - 已结束轨迹记录 - 双击结束轨迹记录 此轨迹已保存至轨迹列表中 已把轨迹导出至/GPSLogger目录 关于 + 在线帮助 关闭 - - 添加位置标记 - 输入描述 + + 添加批注 + 输入描述 添加 - 取消 轨迹列表为空 长度 @@ -90,13 +103,14 @@ 最高速 均速 路点 - 位置标记 + 批注 导出 查看 使用%1$s查看 删除 分享 + 编辑详细信息 您是否还想删除所选轨道的所有导出文件? 您确定要删除已选择的轨迹? @@ -177,10 +191,11 @@ 下载已完成 下载失败。请稍后再试 当前活动轨迹非空 + 未安装互联网浏览器 版本 - 版权所有(C)2016 BasicAirData \nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger使用GPS记录您的轨迹。您可以在喜欢的查看器中查看它们,并以KML,GPX和TXT格式将它们分享,导出到设备的GPSLogger文件夹中。\n\n此程序是免费软件,根据GNU通用公共许可证条款发布。\n有关详细信息,请参阅:http://www.gnu.org/licenses/gpl.txt\n\n对于EGM96大地水准面文件免责声明请参阅:http://earth-info.nga.mil/GandG/disclaim.html - 版权所有(C)2016 BasicAirData \nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger使用GPS记录您的轨迹。您可以在喜欢的查看器中查看它们,并以KML,GPX和TXT格式将它们分享,导出到设备的GPSLogger文件夹中。\n\n此程序是免费软件,根据GNU通用公共许可证条款发布。\n有关详细信息,请参阅:http://www.gnu.org/licenses/gpl.txt\n\n对于EGM96大地水准面文件免责声明请参阅:http://earth-info.nga.mil/GandG/disclaim.html\n \n如果您喜欢使用此应用程序,请花点时间在Google Store中为GPS Logger评分。 + 版权所有(C)2016 BasicAirData \nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger使用GPS记录您的轨迹。您可以在喜欢的查看器中查看它们,并以KML,GPX和TXT格式将它们分享,导出到设备的GPSLogger文件夹中。\n\n此程序是免费软件,根据GNU通用公共许可证条款发布。\n有关详细信息,请参阅:http://www.gnu.org/licenses/gpl.txt + 如果您喜欢使用此应用程序,请花点时间在Google Store中为GPS Logger评分。 关闭 评价此应用 无法访问Google应用商店 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 396e4e51..28109485 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -2,7 +2,10 @@ diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index eb407ed4..7c7402e2 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,7 +2,10 @@ - New Track\n\nStart recording Trackpoints\nor add a Placemark\nusing one of the buttons below + New Track + Start recording Trackpoints\nor add an Annotation\nusing one of the buttons below - Placemarks + Record + Pause + Annotate + Stop + Lock + Unlock + + Finalize Track + The bottom bar is locked + Nothing to save + The recording will begin when the GPS signal is found + The annotation will be taken when the GPS signal is found + + OK + + Annotations Trackpoints Settings - Track finalized - Double click to finalize this track Track saved into the Tracklist Tracks exported in /GPSLogger folder About + Online help Shutdown - - Add Placemark - Enter the description + + Add annotation + Enter the description Add - Cancel Tracklist Empty @@ -106,7 +122,7 @@ Max spd Avg spd Points - Places + Annot Export @@ -114,6 +130,7 @@ View in %1$s Delete Share with ... + Edit details Do you also want to delete all exported files of the selected tracks? @@ -151,7 +168,7 @@ Track Viewer No GPX/KML Viewer installed - Select every Time + Ask every Time TRACKING Collecting Density @@ -203,11 +220,12 @@ Download completed Download failed. Try again later The active track is not empty + No Internet browser installed ver. - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger records your tracks using GPS. You can view them in your preferred viewer, share, and export them in GPSLogger folder of your device in KML, GPX, and TXT format.\n\nThis program is free software, released under the terms of the GNU General Public License.\nFor more details see: http://www.gnu.org/licenses/gpl.txt\n\nFor EGM96 geoid file disclaimer see: http://earth-info.nga.mil/GandG/disclaim.html - Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger records your tracks using GPS. You can view them in your preferred viewer, share, and export them in GPSLogger folder of your device in KML, GPX, and TXT format.\n\nThis program is free software, released under the terms of the GNU General Public License.\nFor more details see: http://www.gnu.org/licenses/gpl.txt\n\nFor EGM96 geoid file disclaimer see: http://earth-info.nga.mil/GandG/disclaim.html\n\nIf You enjoy using this app, please take a moment to rate GPS Logger in the Google Store. + Copyright (C) 2016 BasicAirData\nhttp://www.basicairdata.eu\n\nBasicAirData GPS Logger records your tracks using GPS. You can view them in your preferred viewer, share, and export them in GPSLogger folder of your device in KML, GPX, and TXT format.\n\nThis program is free software, released under the terms of the GNU General Public License.\nFor more details see: http://www.gnu.org/licenses/gpl.txt + If You enjoy using this app, please take a moment to rate GPS Logger in the Google Play Store. Close Rate this App Unable to reach the Store diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 69adadec..387ef978 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -2,7 +2,9 @@ - - - - + + + + + + + + diff --git a/app/src/main/res/xml/my_backup_rules.xml b/app/src/main/res/xml/my_backup_rules.xml index 910dc613..8a090353 100644 --- a/app/src/main/res/xml/my_backup_rules.xml +++ b/app/src/main/res/xml/my_backup_rules.xml @@ -2,7 +2,10 @@