wip
This commit is contained in:
parent
77cfcf202f
commit
0c510d4a11
15 changed files with 214 additions and 311 deletions
|
@ -275,10 +275,16 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlayHelpe
|
||||||
|
|
||||||
|
|
||||||
/* Saves track - shows dialog, if recording is still empty */
|
/* Saves track - shows dialog, if recording is still empty */
|
||||||
private fun saveTrack() {
|
private fun saveTrack()
|
||||||
|
{
|
||||||
if (track.wayPoints.isEmpty())
|
if (track.wayPoints.isEmpty())
|
||||||
{
|
{
|
||||||
YesNoDialog(this as YesNoDialog.YesNoDialogListener).show(context = activity as Context, type = Keys.DIALOG_RESUME_EMPTY_RECORDING, message = R.string.dialog_error_empty_recording_message, yesButton = R.string.dialog_error_empty_recording_button_resume)
|
YesNoDialog(this as YesNoDialog.YesNoDialogListener).show(
|
||||||
|
context = activity as Context,
|
||||||
|
type = Keys.DIALOG_RESUME_EMPTY_RECORDING,
|
||||||
|
message = R.string.dialog_error_empty_recording_message,
|
||||||
|
yesButton = R.string.dialog_error_empty_recording_button_resume
|
||||||
|
)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -288,8 +294,6 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlayHelpe
|
||||||
track.gpxUriString = FileHelper.getGpxFileUri(activity as Context, track).toString()
|
track.gpxUriString = FileHelper.getGpxFileUri(activity as Context, track).toString()
|
||||||
// step 2: save track
|
// step 2: save track
|
||||||
FileHelper.saveTrackSuspended(track, saveGpxToo = true)
|
FileHelper.saveTrackSuspended(track, saveGpxToo = true)
|
||||||
// step 3: save tracklist - suspended
|
|
||||||
FileHelper.addTrackAndSaveTracklistSuspended(activity as Context, track)
|
|
||||||
// step 3: clear track
|
// step 3: clear track
|
||||||
trackerService.clearTrack()
|
trackerService.clearTrack()
|
||||||
// step 4: open track in TrackFragement
|
// step 4: open track in TrackFragement
|
||||||
|
@ -307,7 +311,7 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlayHelpe
|
||||||
bundle.putString(Keys.ARG_TRACK_TITLE, tracklistElement.name)
|
bundle.putString(Keys.ARG_TRACK_TITLE, tracklistElement.name)
|
||||||
bundle.putString(Keys.ARG_TRACK_FILE_URI, tracklistElement.trackUriString)
|
bundle.putString(Keys.ARG_TRACK_FILE_URI, tracklistElement.trackUriString)
|
||||||
bundle.putString(Keys.ARG_GPX_FILE_URI, tracklistElement.gpxUriString)
|
bundle.putString(Keys.ARG_GPX_FILE_URI, tracklistElement.gpxUriString)
|
||||||
bundle.putLong(Keys.ARG_TRACK_ID, TrackHelper.getTrackId(tracklistElement))
|
bundle.putLong(Keys.ARG_TRACK_ID, tracklistElement.id)
|
||||||
findNavController().navigate(R.id.fragment_track, bundle)
|
findNavController().navigate(R.id.fragment_track, bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,7 +376,7 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlayHelpe
|
||||||
// update location and track
|
// update location and track
|
||||||
layout.markCurrentPosition(currentBestLocation, trackingState)
|
layout.markCurrentPosition(currentBestLocation, trackingState)
|
||||||
layout.overlayCurrentTrack(track, trackingState)
|
layout.overlayCurrentTrack(track, trackingState)
|
||||||
layout.updateLiveStatics(length = track.length, duration = track.duration, trackingState = trackingState)
|
layout.updateLiveStatics(distance = track.distance, duration = track.duration, trackingState = trackingState)
|
||||||
// center map, if it had not been dragged/zoomed before
|
// center map, if it had not been dragged/zoomed before
|
||||||
if (!layout.userInteraction) { layout.centerMap(currentBestLocation, true)}
|
if (!layout.userInteraction) { layout.centerMap(currentBestLocation, true)}
|
||||||
// show error snackbar if necessary
|
// show error snackbar if necessary
|
||||||
|
|
|
@ -90,7 +90,12 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
// set up delete button
|
// set up delete button
|
||||||
layout.deleteButton.setOnClickListener {
|
layout.deleteButton.setOnClickListener {
|
||||||
val dialogMessage: String = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${layout.trackNameView.text}"
|
val dialogMessage: String = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${layout.trackNameView.text}"
|
||||||
YesNoDialog(this@TrackFragment as YesNoDialog.YesNoDialogListener).show(context = activity as Context, type = Keys.DIALOG_DELETE_TRACK, messageString = dialogMessage, yesButton = R.string.dialog_yes_no_positive_button_delete_recording)
|
YesNoDialog(this@TrackFragment as YesNoDialog.YesNoDialogListener).show(
|
||||||
|
context = activity as Context,
|
||||||
|
type = Keys.DIALOG_DELETE_TRACK,
|
||||||
|
messageString = dialogMessage,
|
||||||
|
yesButton = R.string.dialog_yes_no_positive_button_delete_recording
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// set up rename button
|
// set up rename button
|
||||||
layout.editButton.setOnClickListener {
|
layout.editButton.setOnClickListener {
|
||||||
|
@ -154,7 +159,7 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
// user tapped remove track
|
// user tapped remove track
|
||||||
true -> {
|
true -> {
|
||||||
// switch to TracklistFragment and remove track there
|
// switch to TracklistFragment and remove track there
|
||||||
val bundle: Bundle = bundleOf(Keys.ARG_TRACK_ID to layout.track.getTrackId())
|
val bundle: Bundle = bundleOf(Keys.ARG_TRACK_ID to layout.track.id)
|
||||||
findNavController().navigate(R.id.tracklist_fragment, bundle)
|
findNavController().navigate(R.id.tracklist_fragment, bundle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,17 +14,15 @@
|
||||||
* https://github.com/osmdroid/osmdroid
|
* https://github.com/osmdroid/osmdroid
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package org.y20k.trackbook
|
package org.y20k.trackbook
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.hardware.Sensor
|
import android.hardware.Sensor
|
||||||
import android.hardware.SensorEvent
|
import android.hardware.SensorEvent
|
||||||
import android.hardware.SensorEventListener
|
import android.hardware.SensorEventListener
|
||||||
|
@ -32,16 +30,18 @@ import android.hardware.SensorManager
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.location.LocationListener
|
import android.location.LocationListener
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
|
import android.Manifest
|
||||||
import android.os.*
|
import android.os.*
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import java.util.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.Runnable
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.Runnable
|
||||||
import org.y20k.trackbook.core.Track
|
import org.y20k.trackbook.core.Track
|
||||||
import org.y20k.trackbook.helpers.*
|
import org.y20k.trackbook.helpers.*
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TrackerService class
|
* TrackerService class
|
||||||
|
@ -51,7 +51,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TrackerService::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TrackerService::class.java)
|
||||||
|
|
||||||
|
|
||||||
/* Main class variables */
|
/* Main class variables */
|
||||||
var trackingState: Int = Keys.STATE_TRACKING_NOT_STARTED
|
var trackingState: Int = Keys.STATE_TRACKING_NOT_STARTED
|
||||||
var gpsProviderActive: Boolean = false
|
var gpsProviderActive: Boolean = false
|
||||||
|
@ -77,7 +76,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
private lateinit var gpsLocationListener: LocationListener
|
private lateinit var gpsLocationListener: LocationListener
|
||||||
private lateinit var networkLocationListener: LocationListener
|
private lateinit var networkLocationListener: LocationListener
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onCreate from Service */
|
/* Overrides onCreate from Service */
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
@ -100,7 +98,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onStartCommand from Service */
|
/* Overrides onStartCommand from Service */
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
|
||||||
|
@ -128,7 +125,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onBind from Service */
|
/* Overrides onBind from Service */
|
||||||
override fun onBind(p0: Intent?): IBinder? {
|
override fun onBind(p0: Intent?): IBinder? {
|
||||||
bound = true
|
bound = true
|
||||||
|
@ -139,7 +135,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
return binder
|
return binder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onRebind from Service */
|
/* Overrides onRebind from Service */
|
||||||
override fun onRebind(intent: Intent?) {
|
override fun onRebind(intent: Intent?) {
|
||||||
bound = true
|
bound = true
|
||||||
|
@ -148,7 +143,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
addNetworkLocationListener()
|
addNetworkLocationListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onUnbind from Service */
|
/* Overrides onUnbind from Service */
|
||||||
override fun onUnbind(intent: Intent?): Boolean {
|
override fun onUnbind(intent: Intent?): Boolean {
|
||||||
bound = false
|
bound = false
|
||||||
|
@ -161,7 +155,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onDestroy from Service */
|
/* Overrides onDestroy from Service */
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
@ -180,13 +173,11 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
removeNetworkLocationListener()
|
removeNetworkLocationListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onAccuracyChanged from SensorEventListener */
|
/* Overrides onAccuracyChanged from SensorEventListener */
|
||||||
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
|
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
|
||||||
LogHelper.v(TAG, "Accuracy changed: $accuracy")
|
LogHelper.v(TAG, "Accuracy changed: $accuracy")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onSensorChanged from SensorEventListener */
|
/* Overrides onSensorChanged from SensorEventListener */
|
||||||
override fun onSensorChanged(sensorEvent: SensorEvent?) {
|
override fun onSensorChanged(sensorEvent: SensorEvent?) {
|
||||||
var steps: Float = 0f
|
var steps: Float = 0f
|
||||||
|
@ -202,7 +193,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
track.stepCount = steps
|
track.stepCount = steps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Resume tracking after stop/pause */
|
/* Resume tracking after stop/pause */
|
||||||
fun resumeTracking() {
|
fun resumeTracking() {
|
||||||
// load temp track - returns an empty track if not available
|
// load temp track - returns an empty track if not available
|
||||||
|
@ -220,7 +210,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
startTracking(newTrack = false)
|
startTracking(newTrack = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Start tracking location */
|
/* Start tracking location */
|
||||||
fun startTracking(newTrack: Boolean = true) {
|
fun startTracking(newTrack: Boolean = true) {
|
||||||
// start receiving location updates
|
// start receiving location updates
|
||||||
|
@ -229,8 +218,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
// set up new track
|
// set up new track
|
||||||
if (newTrack) {
|
if (newTrack) {
|
||||||
track = Track()
|
track = Track()
|
||||||
track.recordingStart = GregorianCalendar.getInstance().time
|
|
||||||
track.recordingStop = track.recordingStart
|
|
||||||
track.name = DateTimeHelper.convertToReadableDate(track.recordingStart)
|
track.name = DateTimeHelper.convertToReadableDate(track.recordingStart)
|
||||||
stepCountOffset = 0f
|
stepCountOffset = 0f
|
||||||
}
|
}
|
||||||
|
@ -244,7 +231,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
startForeground(Keys.TRACKER_SERVICE_NOTIFICATION_ID, displayNotification())
|
startForeground(Keys.TRACKER_SERVICE_NOTIFICATION_ID, displayNotification())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Stop tracking location */
|
/* Stop tracking location */
|
||||||
fun stopTracking() {
|
fun stopTracking() {
|
||||||
// save temp track
|
// save temp track
|
||||||
|
@ -263,7 +249,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
stopForeground(false)
|
stopForeground(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Clear track recording */
|
/* Clear track recording */
|
||||||
fun clearTrack() {
|
fun clearTrack() {
|
||||||
track = Track()
|
track = Track()
|
||||||
|
@ -274,24 +259,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
notificationManager.cancel(Keys.TRACKER_SERVICE_NOTIFICATION_ID) // this call was not necessary prior to Android 12
|
notificationManager.cancel(Keys.TRACKER_SERVICE_NOTIFICATION_ID) // this call was not necessary prior to Android 12
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// /* Saves track recording to storage */ // todo remove
|
|
||||||
// fun saveTrack() {
|
|
||||||
// // save track using "deferred await"
|
|
||||||
// launch {
|
|
||||||
// // step 1: create and store filenames for json and gpx files
|
|
||||||
// track.trackUriString = FileHelper.getTrackFileUri(this@TrackerService, track).toString()
|
|
||||||
// track.gpxUriString = FileHelper.getGpxFileUri(this@TrackerService, track).toString()
|
|
||||||
// // step 2: save track
|
|
||||||
// FileHelper.saveTrackSuspended(track, saveGpxToo = true)
|
|
||||||
// // step 3: save tracklist
|
|
||||||
// FileHelper.addTrackAndSaveTracklistSuspended(this@TrackerService, track)
|
|
||||||
// // step 3: clear track
|
|
||||||
// clearTrack()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
/* Creates location listener */
|
/* Creates location listener */
|
||||||
private fun createLocationListener(): LocationListener {
|
private fun createLocationListener(): LocationListener {
|
||||||
return object : LocationListener {
|
return object : LocationListener {
|
||||||
|
@ -331,7 +298,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Adds a GPS location listener to location manager */
|
/* Adds a GPS location listener to location manager */
|
||||||
private fun addGpsLocationListener() {
|
private fun addGpsLocationListener() {
|
||||||
// check if already registered
|
// check if already registered
|
||||||
|
@ -367,45 +333,43 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Adds a Network location listener to location manager */
|
/* Adds a Network location listener to location manager */
|
||||||
private fun addNetworkLocationListener() {
|
private fun addNetworkLocationListener() {
|
||||||
// check if already registered
|
if (gpsOnly)
|
||||||
if (!networkLocationListenerRegistered) {
|
{
|
||||||
// check if Network provider is available
|
LogHelper.v(TAG, "User prefers GPS-only.")
|
||||||
networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
return;
|
||||||
if (networkProviderActive && !gpsOnly) {
|
|
||||||
// check for location permission
|
|
||||||
if (ContextCompat.checkSelfPermission(
|
|
||||||
this,
|
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION
|
|
||||||
) == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
// adds Network location listener
|
|
||||||
locationManager.requestLocationUpdates(
|
|
||||||
LocationManager.NETWORK_PROVIDER,
|
|
||||||
0,
|
|
||||||
0f,
|
|
||||||
networkLocationListener
|
|
||||||
)
|
|
||||||
networkLocationListenerRegistered = true
|
|
||||||
LogHelper.v(TAG, "Added Network location listener.")
|
|
||||||
} else {
|
|
||||||
LogHelper.w(
|
|
||||||
TAG,
|
|
||||||
"Unable to add Network location listener. Location permission is not granted."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LogHelper.w(TAG, "Unable to add Network location listener.")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LogHelper.v(
|
|
||||||
TAG,
|
|
||||||
"Skipping registration. Network location listener has already been added."
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (networkLocationListenerRegistered)
|
||||||
|
{
|
||||||
|
LogHelper.v(TAG, "Network location listener has already been added.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
||||||
|
if (!networkProviderActive)
|
||||||
|
{
|
||||||
|
LogHelper.w(TAG, "Unable to add Network location listener.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val has_permission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||||
|
if (! has_permission)
|
||||||
|
{
|
||||||
|
LogHelper.w(TAG, "Unable to add Network location listener. Location permission is not granted.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
locationManager.requestLocationUpdates(
|
||||||
|
LocationManager.NETWORK_PROVIDER,
|
||||||
|
0,
|
||||||
|
0f,
|
||||||
|
networkLocationListener
|
||||||
|
)
|
||||||
|
networkLocationListenerRegistered = true
|
||||||
|
LogHelper.v(TAG, "Added Network location listener.")
|
||||||
|
}
|
||||||
|
|
||||||
/* Adds location listeners to location manager */
|
/* Adds location listeners to location manager */
|
||||||
fun removeGpsLocationListener() {
|
fun removeGpsLocationListener() {
|
||||||
|
@ -421,7 +385,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Adds location listeners to location manager */
|
/* Adds location listeners to location manager */
|
||||||
fun removeNetworkLocationListener() {
|
fun removeNetworkLocationListener() {
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
@ -436,7 +399,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Registers a step counter listener */
|
/* Registers a step counter listener */
|
||||||
private fun startStepCounter() {
|
private fun startStepCounter() {
|
||||||
val stepCounterAvailable = sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER), SensorManager.SENSOR_DELAY_UI)
|
val stepCounterAvailable = sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER), SensorManager.SENSOR_DELAY_UI)
|
||||||
|
@ -446,12 +408,11 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Displays or updates notification */
|
||||||
/* Displays / updates notification */
|
|
||||||
private fun displayNotification(): Notification {
|
private fun displayNotification(): Notification {
|
||||||
val notification: Notification = notificationHelper.createNotification(
|
val notification: Notification = notificationHelper.createNotification(
|
||||||
trackingState,
|
trackingState,
|
||||||
track.length,
|
track.distance,
|
||||||
track.duration,
|
track.duration,
|
||||||
useImperial
|
useImperial
|
||||||
)
|
)
|
||||||
|
@ -459,7 +420,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Defines the listener for changes in shared preferences
|
* Defines the listener for changes in shared preferences
|
||||||
*/
|
*/
|
||||||
|
@ -487,7 +447,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
* End of declaration
|
* End of declaration
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inner class: Local Binder that returns this service
|
* Inner class: Local Binder that returns this service
|
||||||
*/
|
*/
|
||||||
|
@ -498,7 +457,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
* End of inner class
|
* End of inner class
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Runnable: Periodically track updates (if recording active)
|
* Runnable: Periodically track updates (if recording active)
|
||||||
*/
|
*/
|
||||||
|
@ -554,7 +512,6 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
* End of declaration
|
* End of declaration
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/* Simple queue that evicts older elements and holds an average */
|
/* Simple queue that evicts older elements and holds an average */
|
||||||
/* Credit: CircularQueue https://stackoverflow.com/a/51923797 */
|
/* Credit: CircularQueue https://stackoverflow.com/a/51923797 */
|
||||||
class SimpleMovingAverageQueue(var capacity: Int) : LinkedList<Double>() {
|
class SimpleMovingAverageQueue(var capacity: Int) : LinkedList<Double>() {
|
||||||
|
@ -576,18 +533,4 @@ class TrackerService: Service(), SensorEventListener {
|
||||||
sum = 0.0
|
sum = 0.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// // TODO remove
|
|
||||||
// val testAltitudes: Array<Double> = arrayOf(352.4349365234375, 358.883544921875, 358.6827392578125, 357.31396484375, 354.27459716796875, 354.573486328125, 354.388916015625, 354.6697998046875, 356.534912109375, 355.2772216796875, 356.21246337890625, 352.3499755859375, 350.37646484375, 351.2098388671875, 350.5213623046875, 350.5145263671875, 350.1728515625, 350.9075927734375, 351.5965576171875, 349.55767822265625, 351.548583984375, 357.1195068359375, 362.18634033203125, 366.3153076171875, 366.2218017578125, 362.1046142578125, 357.48291015625, 356.78570556640625, 353.7734375, 352.53936767578125, 351.8125, 353.1099853515625, 354.93035888671875, 355.4337158203125, 354.83270263671875, 352.9859619140625, 352.3006591796875, 351.63470458984375, 350.2501220703125, 351.75726318359375, 350.87664794921875, 350.4185791015625, 350.51568603515625, 349.5537109375, 345.2874755859375, 345.57196044921875, 349.99658203125, 353.3822021484375, 355.19061279296875, 359.1099853515625, 361.74365234375, 363.313232421875, 362.026611328125, 363.20703125, 363.2508544921875, 362.5870361328125, 362.521240234375)
|
|
||||||
// var testCounter: Int = 0
|
|
||||||
// fun getTestAltitude(): Double {
|
|
||||||
// if (testCounter >= testAltitudes.size) testCounter = 0
|
|
||||||
// val testAltitude: Double = testAltitudes[testCounter]
|
|
||||||
// testCounter ++
|
|
||||||
// return testAltitude
|
|
||||||
// }
|
|
||||||
// // TODO remove
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
Keys.ARG_TRACK_TITLE to tracklistElement.name,
|
Keys.ARG_TRACK_TITLE to tracklistElement.name,
|
||||||
Keys.ARG_TRACK_FILE_URI to tracklistElement.trackUriString,
|
Keys.ARG_TRACK_FILE_URI to tracklistElement.trackUriString,
|
||||||
Keys.ARG_GPX_FILE_URI to tracklistElement.gpxUriString,
|
Keys.ARG_GPX_FILE_URI to tracklistElement.gpxUriString,
|
||||||
Keys.ARG_TRACK_ID to TrackHelper.getTrackId(tracklistElement)
|
Keys.ARG_TRACK_ID to tracklistElement.id
|
||||||
)
|
)
|
||||||
findNavController().navigate(R.id.fragment_track, bundle)
|
findNavController().navigate(R.id.fragment_track, bundle)
|
||||||
}
|
}
|
||||||
|
@ -159,11 +159,13 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
// handle delete request from TrackFragment - after layout calculations are complete
|
// handle delete request from TrackFragment - after layout calculations are complete
|
||||||
val deleteTrackId: Long = arguments?.getLong(Keys.ARG_TRACK_ID, -1L) ?: -1L
|
val deleteTrackId: Long = arguments?.getLong(Keys.ARG_TRACK_ID, -1L) ?: -1L
|
||||||
arguments?.putLong(Keys.ARG_TRACK_ID, -1L)
|
arguments?.putLong(Keys.ARG_TRACK_ID, -1L)
|
||||||
if (deleteTrackId != -1L) {
|
if (deleteTrackId == -1L)
|
||||||
CoroutineScope(Main). launch {
|
{
|
||||||
tracklistAdapter.removeTrackById(this@TracklistFragment.activity as Context, deleteTrackId)
|
return;
|
||||||
toggleOnboardingLayout()
|
}
|
||||||
}
|
CoroutineScope(Main). launch {
|
||||||
|
tracklistAdapter.removeTrackById(this@TracklistFragment.activity as Context, deleteTrackId)
|
||||||
|
toggleOnboardingLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,27 +14,27 @@
|
||||||
* https://github.com/osmdroid/osmdroid
|
* https://github.com/osmdroid/osmdroid
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package org.y20k.trackbook.core
|
package org.y20k.trackbook.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import com.google.gson.annotations.Expose
|
import com.google.gson.annotations.Expose
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.random.Random
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.y20k.trackbook.Keys
|
import org.y20k.trackbook.Keys
|
||||||
import org.y20k.trackbook.helpers.DateTimeHelper
|
import org.y20k.trackbook.helpers.DateTimeHelper
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Track data class
|
* Track data class
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Track (@Expose var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMAT_VERSION,
|
data class Track (@Expose val id: Long = make_random_id(),
|
||||||
|
@Expose var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMAT_VERSION,
|
||||||
@Expose val wayPoints: MutableList<WayPoint> = mutableListOf<WayPoint>(),
|
@Expose val wayPoints: MutableList<WayPoint> = mutableListOf<WayPoint>(),
|
||||||
@Expose var length: Float = 0f,
|
@Expose var distance: Float = 0f,
|
||||||
@Expose var duration: Long = 0L,
|
@Expose var duration: Long = 0L,
|
||||||
@Expose var recordingPaused: Long = 0L,
|
@Expose var recordingPaused: Long = 0L,
|
||||||
@Expose var stepCount: Float = 0f,
|
@Expose var stepCount: Float = 0f,
|
||||||
|
@ -49,30 +49,27 @@ data class Track (@Expose var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMA
|
||||||
@Expose var latitude: Double = Keys.DEFAULT_LATITUDE,
|
@Expose var latitude: Double = Keys.DEFAULT_LATITUDE,
|
||||||
@Expose var longitude: Double = Keys.DEFAULT_LONGITUDE,
|
@Expose var longitude: Double = Keys.DEFAULT_LONGITUDE,
|
||||||
@Expose var zoomLevel: Double = Keys.DEFAULT_ZOOM_LEVEL,
|
@Expose var zoomLevel: Double = Keys.DEFAULT_ZOOM_LEVEL,
|
||||||
@Expose var name: String = String()): Parcelable {
|
@Expose var name: String = String()): Parcelable
|
||||||
|
{
|
||||||
|
|
||||||
/* Creates a TracklistElement */
|
/* Creates a TracklistElement */
|
||||||
fun toTracklistElement(context: Context): TracklistElement {
|
fun toTracklistElement(context: Context): TracklistElement {
|
||||||
val readableDateString: String = DateTimeHelper.convertToReadableDate(recordingStart)
|
val readableDateString: String = DateTimeHelper.convertToReadableDate(recordingStart)
|
||||||
val readableDurationString: String = DateTimeHelper.convertToReadableTime(context, duration)
|
val readableDurationString: String = DateTimeHelper.convertToReadableTime(context, duration)
|
||||||
return TracklistElement(
|
return TracklistElement(
|
||||||
|
id = id,
|
||||||
name = name,
|
name = name,
|
||||||
date = recordingStart,
|
date = recordingStart,
|
||||||
dateString = readableDateString,
|
dateString = readableDateString,
|
||||||
length = length,
|
distance = distance,
|
||||||
durationString = readableDurationString,
|
duration = duration,
|
||||||
trackUriString = trackUriString,
|
trackUriString = trackUriString,
|
||||||
gpxUriString = gpxUriString,
|
gpxUriString = gpxUriString,
|
||||||
starred = false
|
starred = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns unique ID for Track - currently the start date */
|
fun make_random_id(): Long
|
||||||
fun getTrackId(): Long {
|
{
|
||||||
return recordingStart.time
|
return (Random.nextBits(31).toLong() shl 32) + Random.nextBits(32)
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,26 +32,39 @@ import java.util.*
|
||||||
@Keep
|
@Keep
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Tracklist (@Expose val tracklistFormatVersion: Int = Keys.CURRENT_TRACKLIST_FORMAT_VERSION,
|
data class Tracklist (@Expose val tracklistFormatVersion: Int = Keys.CURRENT_TRACKLIST_FORMAT_VERSION,
|
||||||
@Expose val tracklistElements: MutableList<TracklistElement> = mutableListOf<TracklistElement>(),
|
@Expose val tracklistElements: MutableList<TracklistElement> = mutableListOf<TracklistElement>()): Parcelable {
|
||||||
@Expose var modificationDate: Date = Date(),
|
|
||||||
@Expose var totalDistanceAll: Float = 0f,
|
|
||||||
@Expose var totalDurationAll: Long = 0L,
|
|
||||||
@Expose var totalRecordingPausedAll: Long = 0L,
|
|
||||||
@Expose var totalStepCountAll: Float = 0f): Parcelable {
|
|
||||||
|
|
||||||
/* Return trackelement for given track id */
|
/* Return trackelement for given track id */
|
||||||
fun getTrackElement(trackId: Long): TracklistElement? {
|
fun getTrackElement(trackId: Long): TracklistElement? {
|
||||||
tracklistElements.forEach { tracklistElement ->
|
tracklistElements.forEach { tracklistElement ->
|
||||||
if (TrackHelper.getTrackId(tracklistElement) == trackId) {
|
if (tracklistElement.id == trackId) {
|
||||||
return tracklistElement
|
return tracklistElement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun get_total_distance(): Float
|
||||||
|
{
|
||||||
|
var total: Float = 0F
|
||||||
|
tracklistElements.forEach { tracklist_element ->
|
||||||
|
total += tracklist_element.distance
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get_total_duration(): Long
|
||||||
|
{
|
||||||
|
var total: Long = 0L
|
||||||
|
tracklistElements.forEach { tracklist_element ->
|
||||||
|
total += tracklist_element.duration
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
/* Create a deep copy */
|
/* Create a deep copy */
|
||||||
fun deepCopy(): Tracklist {
|
fun deepCopy(): Tracklist {
|
||||||
return Tracklist(tracklistFormatVersion, mutableListOf<TracklistElement>().apply { addAll(tracklistElements) }, modificationDate)
|
return Tracklist(tracklistFormatVersion, mutableListOf<TracklistElement>().apply { addAll(tracklistElements) })
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,18 +29,15 @@ import java.util.*
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class TracklistElement(@Expose var name: String,
|
data class TracklistElement(
|
||||||
@Expose val date: Date,
|
@Expose val id: Long,
|
||||||
@Expose val dateString: String,
|
@Expose var name: String,
|
||||||
@Expose val durationString: String,
|
@Expose val date: Date,
|
||||||
@Expose val length: Float,
|
@Expose val dateString: String,
|
||||||
@Expose val trackUriString: String,
|
@Expose val duration: Long,
|
||||||
@Expose val gpxUriString: String,
|
@Expose val distance: Float,
|
||||||
@Expose var starred: Boolean = false): Parcelable {
|
@Expose val trackUriString: String,
|
||||||
|
@Expose val gpxUriString: String,
|
||||||
/* Returns unique ID for TracklistElement - currently the start date */
|
@Expose var starred: Boolean = false
|
||||||
fun getTrackId(): Long {
|
) : Parcelable {
|
||||||
return date.time
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ object DateTimeHelper {
|
||||||
|
|
||||||
/* Create sortable string for date - used for filenames */
|
/* Create sortable string for date - used for filenames */
|
||||||
fun convertToSortableDateString(date: Date): String {
|
fun convertToSortableDateString(date: Date): String {
|
||||||
val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US)
|
val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US)
|
||||||
return dateFormat.format(date)
|
return dateFormat.format(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.database.Cursor
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
@ -89,7 +90,6 @@ object FileHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Clears given folder - keeps given number of files */
|
/* Clears given folder - keeps given number of files */
|
||||||
fun clearFolder(folder: File?, keep: Int, deleteFolder: Boolean = false) {
|
fun clearFolder(folder: File?, keep: Int, deleteFolder: Boolean = false) {
|
||||||
if (folder != null && folder.exists()) {
|
if (folder != null && folder.exists()) {
|
||||||
|
@ -111,15 +111,17 @@ object FileHelper {
|
||||||
/* Reads tracklist from storage using GSON */
|
/* Reads tracklist from storage using GSON */
|
||||||
fun readTracklist(context: Context): Tracklist {
|
fun readTracklist(context: Context): Tracklist {
|
||||||
LogHelper.v(TAG, "Reading Tracklist - Thread: ${Thread.currentThread().name}")
|
LogHelper.v(TAG, "Reading Tracklist - Thread: ${Thread.currentThread().name}")
|
||||||
// get JSON from text file
|
var folder = context.getExternalFilesDir("tracks")
|
||||||
val json: String = readTextFile(context, getTracklistFileUri(context))
|
|
||||||
var tracklist: Tracklist = Tracklist()
|
var tracklist: Tracklist = Tracklist()
|
||||||
when (json.isNotBlank()) {
|
Log.i(TAG, folder.toString())
|
||||||
// convert JSON and return as tracklist
|
if (folder != null)
|
||||||
true -> try {
|
{
|
||||||
tracklist = getCustomGson().fromJson(json, Tracklist::class.java)
|
folder.walk().filter{ f: File -> f.isFile }.forEach{ track_file ->
|
||||||
} catch (e: Exception) {
|
val track_json = readTextFile(context, track_file.toUri())
|
||||||
e.printStackTrace()
|
Log.i("VOUSSOIR", track_json)
|
||||||
|
val track = getCustomGson().fromJson(track_json, Track::class.java)
|
||||||
|
val tracklist_element = track.toTracklistElement(context)
|
||||||
|
tracklist.tracklistElements.add(tracklist_element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tracklist
|
return tracklist
|
||||||
|
@ -165,7 +167,7 @@ object FileHelper {
|
||||||
|
|
||||||
/* Creates Uri for json track file */
|
/* Creates Uri for json track file */
|
||||||
fun getTrackFileUri(context: Context, track: Track): Uri {
|
fun getTrackFileUri(context: Context, track: Track): Uri {
|
||||||
val fileName: String = DateTimeHelper.convertToSortableDateString(track.recordingStart) + Keys.TRACKBOOK_FILE_EXTENSION
|
val fileName: String = track.id.toString() + Keys.TRACKBOOK_FILE_EXTENSION
|
||||||
return File(context.getExternalFilesDir(Keys.FOLDER_TRACKS), fileName).toUri()
|
return File(context.getExternalFilesDir(Keys.FOLDER_TRACKS), fileName).toUri()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,21 +177,6 @@ object FileHelper {
|
||||||
return File(context.getExternalFilesDir(Keys.FOLDER_TEMP), Keys.TEMP_FILE).toUri()
|
return File(context.getExternalFilesDir(Keys.FOLDER_TEMP), Keys.TEMP_FILE).toUri()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for saveTracklist */
|
|
||||||
suspend fun addTrackAndSaveTracklistSuspended(context: Context, track: Track, modificationDate: Date = track.recordingStop) {
|
|
||||||
return suspendCoroutine { cont ->
|
|
||||||
val tracklist: Tracklist = readTracklist(context)
|
|
||||||
tracklist.tracklistElements.add(track.toTracklistElement(context))
|
|
||||||
tracklist.totalDistanceAll += track.length
|
|
||||||
// tracklist.totalDurationAll += track.duration // note: TracklistElement does not contain duration
|
|
||||||
// tracklist.totalRecordingPausedAll += track.recordingPaused // note: TracklistElement does not contain recordingPaused
|
|
||||||
// tracklist.totalStepCountAll += track.stepCount // note: TracklistElement does not contain stepCount
|
|
||||||
cont.resume(saveTracklist(context, tracklist, modificationDate))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for renameTrack */
|
/* Suspend function: Wrapper for renameTrack */
|
||||||
suspend fun renameTrackSuspended(context: Context, track: Track, newName: String) {
|
suspend fun renameTrackSuspended(context: Context, track: Track, newName: String) {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
|
@ -198,14 +185,6 @@ object FileHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for saveTracklist */
|
|
||||||
suspend fun saveTracklistSuspended(context: Context, tracklist: Tracklist, modificationDate: Date) {
|
|
||||||
return suspendCoroutine { cont ->
|
|
||||||
cont.resume(saveTracklist(context, tracklist, modificationDate))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for saveTrack */
|
/* Suspend function: Wrapper for saveTrack */
|
||||||
suspend fun saveTrackSuspended(track: Track, saveGpxToo: Boolean) {
|
suspend fun saveTrackSuspended(track: Track, saveGpxToo: Boolean) {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
|
@ -223,13 +202,12 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for deleteTrack */
|
/* Suspend function: Wrapper for deleteTrack */
|
||||||
suspend fun deleteTrackSuspended(context: Context, position: Int, tracklist: Tracklist): Tracklist {
|
suspend fun deleteTrackSuspended(context: Context, tracklist_element: TracklistElement, tracklist: Tracklist): Tracklist {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
cont.resume(deleteTrack(context, position, tracklist))
|
cont.resume(deleteTrack(context, tracklist_element, tracklist))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Deletes tracks that are not starred using deleteTracks */
|
/* Suspend function: Deletes tracks that are not starred using deleteTracks */
|
||||||
suspend fun deleteNonStarredSuspended(context: Context, tracklist: Tracklist): Tracklist {
|
suspend fun deleteNonStarredSuspended(context: Context, tracklist: Tracklist): Tracklist {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
|
@ -243,7 +221,6 @@ object FileHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for readTracklist */
|
/* Suspend function: Wrapper for readTracklist */
|
||||||
suspend fun readTracklistSuspended(context: Context): Tracklist {
|
suspend fun readTracklistSuspended(context: Context): Tracklist {
|
||||||
return suspendCoroutine {cont ->
|
return suspendCoroutine {cont ->
|
||||||
|
@ -251,7 +228,6 @@ object FileHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for copyFile */
|
/* Suspend function: Wrapper for copyFile */
|
||||||
suspend fun saveCopyOfFileSuspended(context: Context, originalFileUri: Uri, targetFileUri: Uri, deleteOriginal: Boolean = false) {
|
suspend fun saveCopyOfFileSuspended(context: Context, originalFileUri: Uri, targetFileUri: Uri, deleteOriginal: Boolean = false) {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
|
@ -259,7 +235,6 @@ object FileHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Save Track as JSON to storage */
|
/* Save Track as JSON to storage */
|
||||||
private fun saveTrack(track: Track, saveGpxToo: Boolean) {
|
private fun saveTrack(track: Track, saveGpxToo: Boolean) {
|
||||||
val jsonString: String = getTrackJsonString(track)
|
val jsonString: String = getTrackJsonString(track)
|
||||||
|
@ -276,7 +251,6 @@ object FileHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Save Temp Track as JSON to storage */
|
/* Save Temp Track as JSON to storage */
|
||||||
private fun saveTempTrack(context: Context, track: Track) {
|
private fun saveTempTrack(context: Context, track: Track) {
|
||||||
val json: String = getTrackJsonString(track)
|
val json: String = getTrackJsonString(track)
|
||||||
|
@ -285,47 +259,24 @@ object FileHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Saves track tracklist as JSON text file */
|
|
||||||
private fun saveTracklist(context: Context, tracklist: Tracklist, modificationDate: Date) {
|
|
||||||
tracklist.modificationDate = modificationDate
|
|
||||||
// convert to JSON
|
|
||||||
val gson: Gson = getCustomGson()
|
|
||||||
var json: String = String()
|
|
||||||
try {
|
|
||||||
json = gson.toJson(tracklist)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
if (json.isNotBlank()) {
|
|
||||||
// write text file
|
|
||||||
writeTextFile(json, getTracklistFileUri(context))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Creates Uri for tracklist file */
|
/* Creates Uri for tracklist file */
|
||||||
private fun getTracklistFileUri(context: Context): Uri {
|
private fun getTracklistFileUri(context: Context): Uri {
|
||||||
return File(context.getExternalFilesDir(""), Keys.TRACKLIST_FILE).toUri()
|
return File(context.getExternalFilesDir(""), Keys.TRACKLIST_FILE).toUri()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Renames track */
|
/* Renames track */
|
||||||
private fun renameTrack(context: Context, track: Track, newName: String) {
|
private fun renameTrack(context: Context, track: Track, newName: String) {
|
||||||
// search track in tracklist
|
// search track in tracklist
|
||||||
val tracklist: Tracklist = readTracklist(context)
|
val tracklist: Tracklist = readTracklist(context)
|
||||||
var trackUriString: String = String()
|
var trackUriString: String = String()
|
||||||
tracklist.tracklistElements.forEach { tracklistElement ->
|
tracklist.tracklistElements.forEach { tracklistElement ->
|
||||||
if (tracklistElement.getTrackId() == track.getTrackId()) {
|
if (tracklistElement.id == track.id) {
|
||||||
// rename tracklist element
|
// rename tracklist element
|
||||||
tracklistElement.name = newName
|
tracklistElement.name = newName
|
||||||
trackUriString = tracklistElement.trackUriString
|
trackUriString = tracklistElement.trackUriString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (trackUriString.isNotEmpty()) {
|
if (trackUriString.isNotEmpty()) {
|
||||||
// save tracklist
|
|
||||||
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
|
||||||
// rename track
|
// rename track
|
||||||
track.name = newName
|
track.name = newName
|
||||||
// save track
|
// save track
|
||||||
|
@ -337,29 +288,27 @@ object FileHelper {
|
||||||
/* Deletes multiple tracks */
|
/* Deletes multiple tracks */
|
||||||
private fun deleteTracks(context: Context, tracklistElements: MutableList<TracklistElement>, tracklist: Tracklist): Tracklist {
|
private fun deleteTracks(context: Context, tracklistElements: MutableList<TracklistElement>, tracklist: Tracklist): Tracklist {
|
||||||
tracklistElements.forEach { tracklistElement ->
|
tracklistElements.forEach { tracklistElement ->
|
||||||
// delete track files
|
deleteTrack(context, tracklistElement, tracklist)
|
||||||
tracklistElement.trackUriString.toUri().toFile().delete()
|
|
||||||
tracklistElement.gpxUriString.toUri().toFile().delete()
|
|
||||||
// subtract track length from total distance
|
|
||||||
tracklist.totalDistanceAll -= tracklistElement.length
|
|
||||||
}
|
}
|
||||||
tracklist.tracklistElements.removeAll{ tracklistElements.contains(it) }
|
|
||||||
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
|
||||||
return tracklist
|
return tracklist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Deletes one track */
|
/* Deletes one track */
|
||||||
private fun deleteTrack(context: Context, position: Int, tracklist: Tracklist): Tracklist {
|
private fun deleteTrack(context: Context, tracklist_element: TracklistElement, tracklist: Tracklist): Tracklist {
|
||||||
val tracklistElement: TracklistElement = tracklist.tracklistElements[position]
|
|
||||||
// delete track files
|
// delete track files
|
||||||
tracklistElement.trackUriString.toUri().toFile().delete()
|
val json_file: File = tracklist_element.trackUriString.toUri().toFile()
|
||||||
tracklistElement.gpxUriString.toUri().toFile().delete()
|
if (json_file.isFile)
|
||||||
// subtract track length from total distance
|
{
|
||||||
tracklist.totalDistanceAll -= tracklistElement.length
|
json_file.delete()
|
||||||
|
}
|
||||||
|
val gpx_file: File = tracklist_element.gpxUriString.toUri().toFile()
|
||||||
|
if (gpx_file.isFile)
|
||||||
|
{
|
||||||
|
gpx_file.delete()
|
||||||
|
}
|
||||||
// remove track element from list
|
// remove track element from list
|
||||||
tracklist.tracklistElements.removeIf { TrackHelper.getTrackId(it) == TrackHelper.getTrackId(tracklistElement) }
|
tracklist.tracklistElements.removeIf {it.id == tracklist_element.id}
|
||||||
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
|
||||||
return tracklist
|
return tracklist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,17 +338,14 @@ object FileHelper {
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Creates a Gson object */
|
/* Creates a Gson object */
|
||||||
private fun getCustomGson(): Gson {
|
private fun getCustomGson(): Gson {
|
||||||
val gsonBuilder = GsonBuilder()
|
val gsonBuilder = GsonBuilder()
|
||||||
gsonBuilder.setDateFormat("M/d/yy hh:mm a")
|
gsonBuilder.setDateFormat("yyyy-MM-dd-HH-mm-ss")
|
||||||
gsonBuilder.excludeFieldsWithoutExposeAnnotation()
|
gsonBuilder.excludeFieldsWithoutExposeAnnotation()
|
||||||
return gsonBuilder.create()
|
return gsonBuilder.create()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Converts byte value into a human readable format */
|
/* Converts byte value into a human readable format */
|
||||||
// Source: https://programming.guide/java/formatting-byte-size-to-human-readable-format.html
|
// Source: https://programming.guide/java/formatting-byte-size-to-human-readable-format.html
|
||||||
fun getReadableByteCount(bytes: Long, si: Boolean = true): String {
|
fun getReadableByteCount(bytes: Long, si: Boolean = true): String {
|
||||||
|
|
|
@ -14,13 +14,14 @@
|
||||||
* https://github.com/osmdroid/osmdroid
|
* https://github.com/osmdroid/osmdroid
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package org.y20k.trackbook.helpers
|
package org.y20k.trackbook.helpers
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -30,9 +31,6 @@ import org.y20k.trackbook.core.Track
|
||||||
import org.y20k.trackbook.core.Tracklist
|
import org.y20k.trackbook.core.Tracklist
|
||||||
import org.y20k.trackbook.core.TracklistElement
|
import org.y20k.trackbook.core.TracklistElement
|
||||||
import org.y20k.trackbook.core.WayPoint
|
import org.y20k.trackbook.core.WayPoint
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TrackHelper object
|
* TrackHelper object
|
||||||
|
@ -42,15 +40,6 @@ object TrackHelper {
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TrackHelper::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TrackHelper::class.java)
|
||||||
|
|
||||||
|
|
||||||
/* Returns unique ID for Track - currently the start date */
|
|
||||||
fun getTrackId(track: Track): Long = track.recordingStart.time
|
|
||||||
|
|
||||||
|
|
||||||
/* Returns unique ID for TracklistElement - currently the start date */
|
|
||||||
fun getTrackId(tracklistElement: TracklistElement): Long = tracklistElement.date.time
|
|
||||||
|
|
||||||
|
|
||||||
/* Adds given locatiom as waypoint to track */
|
/* Adds given locatiom as waypoint to track */
|
||||||
fun addWayPointToTrack(track: Track, location: Location, accuracyMultiplier: Int, resumed: Boolean): Pair<Boolean, Track> {
|
fun addWayPointToTrack(track: Track, location: Location, accuracyMultiplier: Int, resumed: Boolean): Pair<Boolean, Track> {
|
||||||
// Step 1: Get previous location
|
// Step 1: Get previous location
|
||||||
|
@ -85,7 +74,7 @@ object TrackHelper {
|
||||||
if (shouldBeAdded) {
|
if (shouldBeAdded) {
|
||||||
// Step 3.1: Update distance (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
// Step 3.1: Update distance (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
||||||
if (!resumed) {
|
if (!resumed) {
|
||||||
track.length = track.length + LocationHelper.calculateDistance(previousLocation, location)
|
track.distance = track.distance + LocationHelper.calculateDistance(previousLocation, location)
|
||||||
}
|
}
|
||||||
// Step 3.2: Update altitude values
|
// Step 3.2: Update altitude values
|
||||||
val altitude: Double = location.altitude
|
val altitude: Double = location.altitude
|
||||||
|
@ -109,17 +98,15 @@ object TrackHelper {
|
||||||
track.longitude = location.longitude
|
track.longitude = location.longitude
|
||||||
|
|
||||||
// Step 3.5: Add location as new waypoint
|
// Step 3.5: Add location as new waypoint
|
||||||
track.wayPoints.add(WayPoint(location = location, distanceToStartingPoint = track.length))
|
track.wayPoints.add(WayPoint(location = location, distanceToStartingPoint = track.distance))
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair(shouldBeAdded, track)
|
return Pair(shouldBeAdded, track)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Calculates time passed since last stop of recording */
|
/* Calculates time passed since last stop of recording */
|
||||||
fun calculateDurationOfPause(recordingStop: Date): Long = GregorianCalendar.getInstance().time.time - recordingStop.time
|
fun calculateDurationOfPause(recordingStop: Date): Long = GregorianCalendar.getInstance().time.time - recordingStop.time
|
||||||
|
|
||||||
|
|
||||||
/* Creates GPX string for given track */
|
/* Creates GPX string for given track */
|
||||||
fun createGpxString(track: Track): String {
|
fun createGpxString(track: Track): String {
|
||||||
var gpxString: String
|
var gpxString: String
|
||||||
|
@ -236,7 +223,6 @@ object TrackHelper {
|
||||||
return gpxTrack.toString()
|
return gpxTrack.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Toggles starred flag for given position */
|
/* Toggles starred flag for given position */
|
||||||
fun toggleStarred(context: Context, track: Track, latitude: Double, longitude: Double): Track {
|
fun toggleStarred(context: Context, track: Track, latitude: Double, longitude: Double): Track {
|
||||||
track.wayPoints.forEach { waypoint ->
|
track.wayPoints.forEach { waypoint ->
|
||||||
|
@ -250,28 +236,4 @@ object TrackHelper {
|
||||||
}
|
}
|
||||||
return track
|
return track
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Calculates total distance, duration and pause */
|
|
||||||
fun calculateAndSaveTrackTotals(context: Context, tracklist: Tracklist) {
|
|
||||||
CoroutineScope(IO).launch {
|
|
||||||
var totalDistanceAll: Float = 0f
|
|
||||||
// var totalDurationAll: Long = 0L
|
|
||||||
// var totalRecordingPausedAll: Long = 0L
|
|
||||||
// var totalStepCountAll: Float = 0f
|
|
||||||
tracklist.tracklistElements.forEach { tracklistElement ->
|
|
||||||
val track: Track = FileHelper.readTrack(context, tracklistElement.trackUriString.toUri())
|
|
||||||
totalDistanceAll += track.length
|
|
||||||
// totalDurationAll += track.duration
|
|
||||||
// totalRecordingPausedAll += track.recordingPaused
|
|
||||||
// totalStepCountAll += track.stepCount
|
|
||||||
}
|
|
||||||
tracklist.totalDistanceAll = totalDistanceAll
|
|
||||||
// tracklist.totalDurationAll = totalDurationAll
|
|
||||||
// tracklist.totalRecordingPausedAll = totalRecordingPausedAll
|
|
||||||
// tracklist.totalStepCountAll = totalStepCountAll
|
|
||||||
FileHelper.saveTracklistSuspended(context, tracklist, GregorianCalendar.getInstance().time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.y20k.trackbook.tracklist
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -29,6 +30,7 @@ import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import java.util.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
|
@ -37,7 +39,6 @@ import org.y20k.trackbook.R
|
||||||
import org.y20k.trackbook.core.Tracklist
|
import org.y20k.trackbook.core.Tracklist
|
||||||
import org.y20k.trackbook.core.TracklistElement
|
import org.y20k.trackbook.core.TracklistElement
|
||||||
import org.y20k.trackbook.helpers.*
|
import org.y20k.trackbook.helpers.*
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -70,10 +71,6 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
// load tracklist
|
// load tracklist
|
||||||
tracklist = FileHelper.readTracklist(context)
|
tracklist = FileHelper.readTracklist(context)
|
||||||
tracklist.tracklistElements.sortByDescending { tracklistElement -> tracklistElement.date }
|
tracklist.tracklistElements.sortByDescending { tracklistElement -> tracklistElement.date }
|
||||||
// calculate total duration and distance, if necessary
|
|
||||||
if (tracklist.tracklistElements.isNotEmpty() && tracklist.totalDurationAll == 0L) {
|
|
||||||
TrackHelper.calculateAndSaveTrackTotals(context, tracklist)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,12 +115,12 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
// CASE STATISTICS ELEMENT
|
// CASE STATISTICS ELEMENT
|
||||||
is ElementStatisticsViewHolder -> {
|
is ElementStatisticsViewHolder -> {
|
||||||
val elementStatisticsViewHolder: ElementStatisticsViewHolder = holder as ElementStatisticsViewHolder
|
val elementStatisticsViewHolder: ElementStatisticsViewHolder = holder as ElementStatisticsViewHolder
|
||||||
elementStatisticsViewHolder.totalDistanceView.text = LengthUnitHelper.convertDistanceToString(tracklist.totalDistanceAll, useImperial)
|
elementStatisticsViewHolder.totalDistanceView.text = LengthUnitHelper.convertDistanceToString(tracklist.get_total_distance(), useImperial)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CASE TRACK ELEMENT
|
// CASE TRACK ELEMENT
|
||||||
is ElementTrackViewHolder -> {
|
is ElementTrackViewHolder -> {
|
||||||
val positionInTracklist: Int = position -1
|
val positionInTracklist: Int = position - 1 // Element 0 is the statistics element.
|
||||||
val elementTrackViewHolder: ElementTrackViewHolder = holder as ElementTrackViewHolder
|
val elementTrackViewHolder: ElementTrackViewHolder = holder as ElementTrackViewHolder
|
||||||
elementTrackViewHolder.trackNameView.text = tracklist.tracklistElements[positionInTracklist].name
|
elementTrackViewHolder.trackNameView.text = tracklist.tracklistElements[positionInTracklist].name
|
||||||
elementTrackViewHolder.trackDataView.text = createTrackDataString(positionInTracklist)
|
elementTrackViewHolder.trackDataView.text = createTrackDataString(positionInTracklist)
|
||||||
|
@ -154,8 +151,9 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
/* Removes track and track files for given position - used by TracklistFragment */
|
/* Removes track and track files for given position - used by TracklistFragment */
|
||||||
fun removeTrackAtPosition(context: Context, position: Int) {
|
fun removeTrackAtPosition(context: Context, position: Int) {
|
||||||
CoroutineScope(IO).launch {
|
CoroutineScope(IO).launch {
|
||||||
val positionInTracklist = position - 1
|
val index = position - 1 // position 0 is the statistics element
|
||||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, positionInTracklist, tracklist) }
|
val tracklist_element = tracklist.tracklistElements[index]
|
||||||
|
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, tracklist_element, tracklist) }
|
||||||
// wait for result and store in tracklist
|
// wait for result and store in tracklist
|
||||||
withContext(Main) {
|
withContext(Main) {
|
||||||
tracklist = deferred.await()
|
tracklist = deferred.await()
|
||||||
|
@ -166,20 +164,23 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Removes track and track files for given track id - used by TracklistFragment */
|
/* Removes track and track files for given track id - used by TracklistFragment */
|
||||||
fun removeTrackById(context: Context, trackId: Long) {
|
fun removeTrackById(context: Context, trackId: Long) {
|
||||||
CoroutineScope(IO).launch {
|
CoroutineScope(IO).launch {
|
||||||
// reload tracklist //todo check if necessary
|
// reload tracklist //todo check if necessary
|
||||||
// tracklist = FileHelper.readTracklist(context)
|
tracklist = FileHelper.readTracklist(context)
|
||||||
val positionInTracklist: Int = findPosition(trackId)
|
val index: Int = tracklist.tracklistElements.indexOfFirst {it.id == trackId}
|
||||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, positionInTracklist, tracklist) }
|
if (index == -1) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
val tracklist_element = tracklist.tracklistElements[index]
|
||||||
|
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, tracklist_element, tracklist) }
|
||||||
// wait for result and store in tracklist
|
// wait for result and store in tracklist
|
||||||
|
val position = index + 1 // position 0 is the statistics element
|
||||||
withContext(Main) {
|
withContext(Main) {
|
||||||
tracklist = deferred.await()
|
tracklist = deferred.await()
|
||||||
val positionInRecyclerView: Int = positionInTracklist + 1 // position 0 is the statistics element
|
|
||||||
notifyItemChanged(0)
|
notifyItemChanged(0)
|
||||||
notifyItemRemoved(positionInRecyclerView)
|
notifyItemRemoved(position)
|
||||||
notifyItemRangeChanged(position, tracklist.tracklistElements.size)
|
notifyItemRangeChanged(position, tracklist.tracklistElements.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +196,10 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
/* Finds current position of track element in adapter list */
|
/* Finds current position of track element in adapter list */
|
||||||
private fun findPosition(trackId: Long): Int {
|
private fun findPosition(trackId: Long): Int {
|
||||||
tracklist.tracklistElements.forEachIndexed {index, tracklistElement ->
|
tracklist.tracklistElements.forEachIndexed {index, tracklistElement ->
|
||||||
if (tracklistElement.getTrackId() == trackId) return index
|
if (tracklistElement.id == trackId)
|
||||||
|
{
|
||||||
|
return index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
@ -214,21 +218,19 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
tracklist.tracklistElements[position].starred = true
|
tracklist.tracklistElements[position].starred = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
FileHelper.saveTracklistSuspended(context, tracklist, GregorianCalendar.getInstance().time)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Creates the track data string */
|
/* Creates the track data string */
|
||||||
private fun createTrackDataString(position: Int): String {
|
private fun createTrackDataString(position: Int): String {
|
||||||
val tracklistElement: TracklistElement = tracklist.tracklistElements[position]
|
val tracklistElement: TracklistElement = tracklist.tracklistElements[position]
|
||||||
|
val track_duration_string = DateTimeHelper.convertToReadableTime(context, tracklistElement.duration)
|
||||||
val trackDataString: String
|
val trackDataString: String
|
||||||
when (tracklistElement.name == tracklistElement.dateString) {
|
when (tracklistElement.name == tracklistElement.dateString) {
|
||||||
// CASE: no individual name set - exclude date
|
// CASE: no individual name set - exclude date
|
||||||
true -> trackDataString = "${LengthUnitHelper.convertDistanceToString(tracklistElement.length, useImperial)} • ${tracklistElement.durationString}"
|
true -> trackDataString = "${LengthUnitHelper.convertDistanceToString(tracklistElement.distance, useImperial)} • ${track_duration_string}"
|
||||||
// CASE: no individual name set - include date
|
// CASE: no individual name set - include date
|
||||||
false -> trackDataString = "${tracklistElement.dateString} • ${LengthUnitHelper.convertDistanceToString(tracklistElement.length, useImperial)} • ${tracklistElement.durationString}"
|
false -> trackDataString = "${tracklistElement.dateString} • ${LengthUnitHelper.convertDistanceToString(tracklistElement.distance, useImperial)} • ${track_duration_string}"
|
||||||
}
|
}
|
||||||
return trackDataString
|
return trackDataString
|
||||||
}
|
}
|
||||||
|
@ -242,7 +244,7 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||||
val oldItem = oldList.tracklistElements[oldItemPosition]
|
val oldItem = oldList.tracklistElements[oldItemPosition]
|
||||||
val newItem = newList.tracklistElements[newItemPosition]
|
val newItem = newList.tracklistElements[newItemPosition]
|
||||||
return TrackHelper.getTrackId(oldItem) == TrackHelper.getTrackId(newItem)
|
return oldItem.id == newItem.id
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getOldListSize(): Int {
|
override fun getOldListSize(): Int {
|
||||||
|
@ -256,7 +258,7 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||||
val oldItem = oldList.tracklistElements[oldItemPosition]
|
val oldItem = oldList.tracklistElements[oldItemPosition]
|
||||||
val newItem = newList.tracklistElements[newItemPosition]
|
val newItem = newList.tracklistElements[newItemPosition]
|
||||||
return TrackHelper.getTrackId(oldItem) == TrackHelper.getTrackId(newItem) && oldItem.length == newItem.length
|
return (oldItem.id == newItem.id) && (oldItem.distance == newItem.distance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -203,13 +203,13 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
||||||
|
|
||||||
|
|
||||||
/* Update live statics */
|
/* Update live statics */
|
||||||
fun updateLiveStatics(length: Float, duration: Long, trackingState: Int) {
|
fun updateLiveStatics(distance: Float, duration: Long, trackingState: Int) {
|
||||||
// toggle visibility
|
// toggle visibility
|
||||||
val trackingActive: Boolean = trackingState != Keys.STATE_TRACKING_NOT_STARTED
|
val trackingActive: Boolean = trackingState != Keys.STATE_TRACKING_NOT_STARTED
|
||||||
liveStatisticsDistanceView.isVisible = trackingActive
|
liveStatisticsDistanceView.isVisible = trackingActive
|
||||||
liveStatisticsDurationView.isVisible = trackingActive
|
liveStatisticsDurationView.isVisible = trackingActive
|
||||||
// update distance and duration (and add outline)
|
// update distance and duration (and add outline)
|
||||||
val distanceString: String = LengthUnitHelper.convertDistanceToString(length, useImperial)
|
val distanceString: String = LengthUnitHelper.convertDistanceToString(distance, useImperial)
|
||||||
liveStatisticsDistanceView.text = distanceString
|
liveStatisticsDistanceView.text = distanceString
|
||||||
liveStatisticsDistanceOutlineView.text = distanceString
|
liveStatisticsDistanceOutlineView.text = distanceString
|
||||||
liveStatisticsDistanceOutlineView.paint.strokeWidth = 5f
|
liveStatisticsDistanceOutlineView.paint.strokeWidth = 5f
|
||||||
|
|
|
@ -78,6 +78,7 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
private val statisticsSheetBehavior: BottomSheetBehavior<View>
|
private val statisticsSheetBehavior: BottomSheetBehavior<View>
|
||||||
private val statisticsSheet: NestedScrollView
|
private val statisticsSheet: NestedScrollView
|
||||||
private val statisticsView: View
|
private val statisticsView: View
|
||||||
|
private val trackidView: MaterialTextView
|
||||||
private val distanceView: MaterialTextView
|
private val distanceView: MaterialTextView
|
||||||
private val stepsTitleView: MaterialTextView
|
private val stepsTitleView: MaterialTextView
|
||||||
private val stepsView: MaterialTextView
|
private val stepsView: MaterialTextView
|
||||||
|
@ -120,6 +121,7 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
// get views for statistics sheet
|
// get views for statistics sheet
|
||||||
statisticsSheet = rootView.findViewById(R.id.statistics_sheet)
|
statisticsSheet = rootView.findViewById(R.id.statistics_sheet)
|
||||||
statisticsView = rootView.findViewById(R.id.statistics_view)
|
statisticsView = rootView.findViewById(R.id.statistics_view)
|
||||||
|
trackidView = rootView.findViewById(R.id.statistics_data_trackid)
|
||||||
distanceView = rootView.findViewById(R.id.statistics_data_distance)
|
distanceView = rootView.findViewById(R.id.statistics_data_distance)
|
||||||
stepsTitleView = rootView.findViewById(R.id.statistics_p_steps)
|
stepsTitleView = rootView.findViewById(R.id.statistics_p_steps)
|
||||||
stepsView = rootView.findViewById(R.id.statistics_data_steps)
|
stepsView = rootView.findViewById(R.id.statistics_data_steps)
|
||||||
|
@ -190,8 +192,10 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
|
|
||||||
|
|
||||||
/* Saves zoom level and center of this map */
|
/* Saves zoom level and center of this map */
|
||||||
fun saveViewStateToTrack() {
|
fun saveViewStateToTrack()
|
||||||
if (track.latitude != 0.0 && track.longitude != 0.0) {
|
{
|
||||||
|
if (track.latitude != 0.0 && track.longitude != 0.0)
|
||||||
|
{
|
||||||
CoroutineScope(Dispatchers.IO).launch { FileHelper.saveTrackSuspended(track, false) }
|
CoroutineScope(Dispatchers.IO).launch { FileHelper.saveTrackSuspended(track, false) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,12 +206,14 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
|
|
||||||
// get step count string - hide step count if not available
|
// get step count string - hide step count if not available
|
||||||
val steps: String
|
val steps: String
|
||||||
if (track.stepCount == -1f) {
|
if (track.stepCount == -1f)
|
||||||
|
{
|
||||||
steps = context.getString(R.string.statistics_sheet_p_steps_no_pedometer)
|
steps = context.getString(R.string.statistics_sheet_p_steps_no_pedometer)
|
||||||
stepsTitleView.isGone = true
|
stepsTitleView.isGone = true
|
||||||
stepsView.isGone = true
|
stepsView.isGone = true
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
steps = track.stepCount.roundToInt().toString()
|
steps = track.stepCount.roundToInt().toString()
|
||||||
stepsTitleView.isVisible = true
|
stepsTitleView.isVisible = true
|
||||||
stepsView.isVisible = true
|
stepsView.isVisible = true
|
||||||
|
@ -215,11 +221,12 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
|
|
||||||
// populate views
|
// populate views
|
||||||
trackNameView.text = track.name
|
trackNameView.text = track.name
|
||||||
distanceView.text = LengthUnitHelper.convertDistanceToString(track.length, useImperialUnits)
|
trackidView.text = track.id.toString()
|
||||||
|
distanceView.text = LengthUnitHelper.convertDistanceToString(track.distance, useImperialUnits)
|
||||||
stepsView.text = steps
|
stepsView.text = steps
|
||||||
waypointsView.text = track.wayPoints.size.toString()
|
waypointsView.text = track.wayPoints.size.toString()
|
||||||
durationView.text = DateTimeHelper.convertToReadableTime(context, track.duration)
|
durationView.text = DateTimeHelper.convertToReadableTime(context, track.duration)
|
||||||
velocityView.text = LengthUnitHelper.convertToVelocityString(track.duration, track.recordingPaused, track.length, useImperialUnits)
|
velocityView.text = LengthUnitHelper.convertToVelocityString(track.duration, track.recordingPaused, track.distance, useImperialUnits)
|
||||||
recordingStartView.text = DateTimeHelper.convertToReadableDateAndTime(track.recordingStart)
|
recordingStartView.text = DateTimeHelper.convertToReadableDateAndTime(track.recordingStart)
|
||||||
recordingStopView.text = DateTimeHelper.convertToReadableDateAndTime(track.recordingStop)
|
recordingStopView.text = DateTimeHelper.convertToReadableDateAndTime(track.recordingStop)
|
||||||
maxAltitudeView.text = LengthUnitHelper.convertDistanceToString(track.maxAltitude, useImperialUnits)
|
maxAltitudeView.text = LengthUnitHelper.convertDistanceToString(track.maxAltitude, useImperialUnits)
|
||||||
|
|
|
@ -75,17 +75,41 @@
|
||||||
app:srcCompat="@drawable/ic_save_to_storage_24dp" />
|
app:srcCompat="@drawable/ic_save_to_storage_24dp" />
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/statistics_p_distance"
|
android:id="@+id/statistics_p_trackid"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
android:text="@string/statistics_sheet_p_distance"
|
android:text="@string/statistics_sheet_p_trackid"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
||||||
android:textColor="@color/text_lightweight"
|
android:textColor="@color/text_lightweight"
|
||||||
app:layout_constraintStart_toStartOf="@+id/statistics_track_name_headline"
|
app:layout_constraintStart_toStartOf="@+id/statistics_track_name_headline"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/statistics_track_name_headline" />
|
app:layout_constraintTop_toBottomOf="@+id/statistics_track_name_headline" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/statistics_data_trackid"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
|
||||||
|
android:textColor="@color/text_default"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/statistics_p_trackid"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/statistics_p_trackid"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/statistics_p_trackid"
|
||||||
|
tools:text="@string/sample_text_default_data" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/statistics_p_distance"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/statistics_sheet_p_distance"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:textColor="@color/text_lightweight"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/statistics_track_name_headline"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/statistics_p_trackid" />
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/statistics_data_distance"
|
android:id="@+id/statistics_data_distance"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
<string name="marker_description_time">Time</string>
|
<string name="marker_description_time">Time</string>
|
||||||
<string name="marker_description_accuracy">Accuracy</string>
|
<string name="marker_description_accuracy">Accuracy</string>
|
||||||
<!-- Statistics Sheet -->
|
<!-- Statistics Sheet -->
|
||||||
|
<string name="statistics_sheet_p_trackid">Track ID:</string>
|
||||||
<string name="statistics_sheet_p_distance">Total distance:</string>
|
<string name="statistics_sheet_p_distance">Total distance:</string>
|
||||||
<string name="statistics_sheet_p_steps">Steps taken:</string>
|
<string name="statistics_sheet_p_steps">Steps taken:</string>
|
||||||
<string name="statistics_sheet_p_steps_no_pedometer">pedometer not available</string>
|
<string name="statistics_sheet_p_steps_no_pedometer">pedometer not available</string>
|
||||||
|
|
Loading…
Reference in a new issue