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 */
|
||||
private fun saveTrack() {
|
||||
private fun saveTrack()
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@ -288,8 +294,6 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlayHelpe
|
|||
track.gpxUriString = FileHelper.getGpxFileUri(activity as Context, track).toString()
|
||||
// step 2: save track
|
||||
FileHelper.saveTrackSuspended(track, saveGpxToo = true)
|
||||
// step 3: save tracklist - suspended
|
||||
FileHelper.addTrackAndSaveTracklistSuspended(activity as Context, track)
|
||||
// step 3: clear track
|
||||
trackerService.clearTrack()
|
||||
// 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_FILE_URI, tracklistElement.trackUriString)
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -372,7 +376,7 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlayHelpe
|
|||
// update location and track
|
||||
layout.markCurrentPosition(currentBestLocation, 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
|
||||
if (!layout.userInteraction) { layout.centerMap(currentBestLocation, true)}
|
||||
// show error snackbar if necessary
|
||||
|
|
|
@ -90,7 +90,12 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
|||
// set up delete button
|
||||
layout.deleteButton.setOnClickListener {
|
||||
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
|
||||
layout.editButton.setOnClickListener {
|
||||
|
@ -154,7 +159,7 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
|||
// user tapped remove track
|
||||
true -> {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,17 +14,15 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.SharedPreferences
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
|
@ -32,16 +30,18 @@ import android.hardware.SensorManager
|
|||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import android.Manifest
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Runnable
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.Runnable
|
||||
import org.y20k.trackbook.core.Track
|
||||
import org.y20k.trackbook.helpers.*
|
||||
import java.util.*
|
||||
|
||||
|
||||
/*
|
||||
* TrackerService class
|
||||
|
@ -51,7 +51,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(TrackerService::class.java)
|
||||
|
||||
|
||||
/* Main class variables */
|
||||
var trackingState: Int = Keys.STATE_TRACKING_NOT_STARTED
|
||||
var gpsProviderActive: Boolean = false
|
||||
|
@ -77,7 +76,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
private lateinit var gpsLocationListener: LocationListener
|
||||
private lateinit var networkLocationListener: LocationListener
|
||||
|
||||
|
||||
/* Overrides onCreate from Service */
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
@ -100,7 +98,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onStartCommand from Service */
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
|
||||
|
@ -128,7 +125,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
return START_STICKY
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onBind from Service */
|
||||
override fun onBind(p0: Intent?): IBinder? {
|
||||
bound = true
|
||||
|
@ -139,7 +135,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
return binder
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onRebind from Service */
|
||||
override fun onRebind(intent: Intent?) {
|
||||
bound = true
|
||||
|
@ -148,7 +143,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
addNetworkLocationListener()
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onUnbind from Service */
|
||||
override fun onUnbind(intent: Intent?): Boolean {
|
||||
bound = false
|
||||
|
@ -161,7 +155,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
return true
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onDestroy from Service */
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
@ -180,13 +173,11 @@ class TrackerService: Service(), SensorEventListener {
|
|||
removeNetworkLocationListener()
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onAccuracyChanged from SensorEventListener */
|
||||
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
|
||||
LogHelper.v(TAG, "Accuracy changed: $accuracy")
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onSensorChanged from SensorEventListener */
|
||||
override fun onSensorChanged(sensorEvent: SensorEvent?) {
|
||||
var steps: Float = 0f
|
||||
|
@ -202,7 +193,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
track.stepCount = steps
|
||||
}
|
||||
|
||||
|
||||
/* Resume tracking after stop/pause */
|
||||
fun resumeTracking() {
|
||||
// load temp track - returns an empty track if not available
|
||||
|
@ -220,7 +210,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
startTracking(newTrack = false)
|
||||
}
|
||||
|
||||
|
||||
/* Start tracking location */
|
||||
fun startTracking(newTrack: Boolean = true) {
|
||||
// start receiving location updates
|
||||
|
@ -229,8 +218,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
// set up new track
|
||||
if (newTrack) {
|
||||
track = Track()
|
||||
track.recordingStart = GregorianCalendar.getInstance().time
|
||||
track.recordingStop = track.recordingStart
|
||||
track.name = DateTimeHelper.convertToReadableDate(track.recordingStart)
|
||||
stepCountOffset = 0f
|
||||
}
|
||||
|
@ -244,7 +231,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
startForeground(Keys.TRACKER_SERVICE_NOTIFICATION_ID, displayNotification())
|
||||
}
|
||||
|
||||
|
||||
/* Stop tracking location */
|
||||
fun stopTracking() {
|
||||
// save temp track
|
||||
|
@ -263,7 +249,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
stopForeground(false)
|
||||
}
|
||||
|
||||
|
||||
/* Clear track recording */
|
||||
fun clearTrack() {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
// /* 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 */
|
||||
private fun createLocationListener(): LocationListener {
|
||||
return object : LocationListener {
|
||||
|
@ -331,7 +298,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Adds a GPS location listener to location manager */
|
||||
private fun addGpsLocationListener() {
|
||||
// check if already registered
|
||||
|
@ -367,20 +333,34 @@ class TrackerService: Service(), SensorEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Adds a Network location listener to location manager */
|
||||
private fun addNetworkLocationListener() {
|
||||
// check if already registered
|
||||
if (!networkLocationListenerRegistered) {
|
||||
// check if Network provider is available
|
||||
if (gpsOnly)
|
||||
{
|
||||
LogHelper.v(TAG, "User prefers GPS-only.")
|
||||
return;
|
||||
}
|
||||
|
||||
if (networkLocationListenerRegistered)
|
||||
{
|
||||
LogHelper.v(TAG, "Network location listener has already been added.")
|
||||
return;
|
||||
}
|
||||
|
||||
networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
||||
if (networkProviderActive && !gpsOnly) {
|
||||
// check for location permission
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
) == PackageManager.PERMISSION_GRANTED) {
|
||||
// adds Network location listener
|
||||
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,
|
||||
|
@ -389,23 +369,7 @@ class TrackerService: Service(), SensorEventListener {
|
|||
)
|
||||
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."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Adds location listeners to location manager */
|
||||
fun removeGpsLocationListener() {
|
||||
|
@ -421,7 +385,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Adds location listeners to location manager */
|
||||
fun removeNetworkLocationListener() {
|
||||
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 */
|
||||
private fun startStepCounter() {
|
||||
val stepCounterAvailable = sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER), SensorManager.SENSOR_DELAY_UI)
|
||||
|
@ -446,12 +408,11 @@ class TrackerService: Service(), SensorEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Displays / updates notification */
|
||||
/* Displays or updates notification */
|
||||
private fun displayNotification(): Notification {
|
||||
val notification: Notification = notificationHelper.createNotification(
|
||||
trackingState,
|
||||
track.length,
|
||||
track.distance,
|
||||
track.duration,
|
||||
useImperial
|
||||
)
|
||||
|
@ -459,7 +420,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
return notification
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Defines the listener for changes in shared preferences
|
||||
*/
|
||||
|
@ -487,7 +447,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
* End of declaration
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Inner class: Local Binder that returns this service
|
||||
*/
|
||||
|
@ -498,7 +457,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
* End of inner class
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Runnable: Periodically track updates (if recording active)
|
||||
*/
|
||||
|
@ -554,7 +512,6 @@ class TrackerService: Service(), SensorEventListener {
|
|||
* End of declaration
|
||||
*/
|
||||
|
||||
|
||||
/* Simple queue that evicts older elements and holds an average */
|
||||
/* Credit: CircularQueue https://stackoverflow.com/a/51923797 */
|
||||
class SimpleMovingAverageQueue(var capacity: Int) : LinkedList<Double>() {
|
||||
|
@ -576,18 +533,4 @@ class TrackerService: Service(), SensorEventListener {
|
|||
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_FILE_URI to tracklistElement.trackUriString,
|
||||
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)
|
||||
}
|
||||
|
@ -159,13 +159,15 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
|||
// handle delete request from TrackFragment - after layout calculations are complete
|
||||
val deleteTrackId: Long = arguments?.getLong(Keys.ARG_TRACK_ID, -1L) ?: -1L
|
||||
arguments?.putLong(Keys.ARG_TRACK_ID, -1L)
|
||||
if (deleteTrackId != -1L) {
|
||||
if (deleteTrackId == -1L)
|
||||
{
|
||||
return;
|
||||
}
|
||||
CoroutineScope(Main). launch {
|
||||
tracklistAdapter.removeTrackById(this@TracklistFragment.activity as Context, deleteTrackId)
|
||||
toggleOnboardingLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -14,27 +14,27 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.core
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.Expose
|
||||
import java.util.*
|
||||
import kotlin.random.Random
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.y20k.trackbook.Keys
|
||||
import org.y20k.trackbook.helpers.DateTimeHelper
|
||||
import java.util.*
|
||||
|
||||
|
||||
/*
|
||||
* Track data class
|
||||
*/
|
||||
@Keep
|
||||
@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 var length: Float = 0f,
|
||||
@Expose var distance: Float = 0f,
|
||||
@Expose var duration: Long = 0L,
|
||||
@Expose var recordingPaused: Long = 0L,
|
||||
@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 longitude: Double = Keys.DEFAULT_LONGITUDE,
|
||||
@Expose var zoomLevel: Double = Keys.DEFAULT_ZOOM_LEVEL,
|
||||
@Expose var name: String = String()): Parcelable {
|
||||
|
||||
|
||||
@Expose var name: String = String()): Parcelable
|
||||
{
|
||||
/* Creates a TracklistElement */
|
||||
fun toTracklistElement(context: Context): TracklistElement {
|
||||
val readableDateString: String = DateTimeHelper.convertToReadableDate(recordingStart)
|
||||
val readableDurationString: String = DateTimeHelper.convertToReadableTime(context, duration)
|
||||
return TracklistElement(
|
||||
id = id,
|
||||
name = name,
|
||||
date = recordingStart,
|
||||
dateString = readableDateString,
|
||||
length = length,
|
||||
durationString = readableDurationString,
|
||||
distance = distance,
|
||||
duration = duration,
|
||||
trackUriString = trackUriString,
|
||||
gpxUriString = gpxUriString,
|
||||
starred = false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/* Returns unique ID for Track - currently the start date */
|
||||
fun getTrackId(): Long {
|
||||
return recordingStart.time
|
||||
}
|
||||
|
||||
|
||||
fun make_random_id(): Long
|
||||
{
|
||||
return (Random.nextBits(31).toLong() shl 32) + Random.nextBits(32)
|
||||
}
|
||||
|
|
|
@ -32,26 +32,39 @@ import java.util.*
|
|||
@Keep
|
||||
@Parcelize
|
||||
data class Tracklist (@Expose val tracklistFormatVersion: Int = Keys.CURRENT_TRACKLIST_FORMAT_VERSION,
|
||||
@Expose val tracklistElements: MutableList<TracklistElement> = mutableListOf<TracklistElement>(),
|
||||
@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 {
|
||||
@Expose val tracklistElements: MutableList<TracklistElement> = mutableListOf<TracklistElement>()): Parcelable {
|
||||
|
||||
/* Return trackelement for given track id */
|
||||
fun getTrackElement(trackId: Long): TracklistElement? {
|
||||
tracklistElements.forEach { tracklistElement ->
|
||||
if (TrackHelper.getTrackId(tracklistElement) == trackId) {
|
||||
if (tracklistElement.id == trackId) {
|
||||
return tracklistElement
|
||||
}
|
||||
}
|
||||
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 */
|
||||
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
|
||||
@Parcelize
|
||||
data class TracklistElement(@Expose var name: String,
|
||||
data class TracklistElement(
|
||||
@Expose val id: Long,
|
||||
@Expose var name: String,
|
||||
@Expose val date: Date,
|
||||
@Expose val dateString: String,
|
||||
@Expose val durationString: String,
|
||||
@Expose val length: Float,
|
||||
@Expose val duration: Long,
|
||||
@Expose val distance: Float,
|
||||
@Expose val trackUriString: String,
|
||||
@Expose val gpxUriString: String,
|
||||
@Expose var starred: Boolean = false): Parcelable {
|
||||
|
||||
/* Returns unique ID for TracklistElement - currently the start date */
|
||||
fun getTrackId(): Long {
|
||||
return date.time
|
||||
}
|
||||
|
||||
@Expose var starred: Boolean = false
|
||||
) : Parcelable {
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ object DateTimeHelper {
|
|||
|
||||
/* Create sortable string for date - used for filenames */
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import android.database.Cursor
|
|||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import com.google.gson.Gson
|
||||
|
@ -89,7 +90,6 @@ object FileHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Clears given folder - keeps given number of files */
|
||||
fun clearFolder(folder: File?, keep: Int, deleteFolder: Boolean = false) {
|
||||
if (folder != null && folder.exists()) {
|
||||
|
@ -111,15 +111,17 @@ object FileHelper {
|
|||
/* Reads tracklist from storage using GSON */
|
||||
fun readTracklist(context: Context): Tracklist {
|
||||
LogHelper.v(TAG, "Reading Tracklist - Thread: ${Thread.currentThread().name}")
|
||||
// get JSON from text file
|
||||
val json: String = readTextFile(context, getTracklistFileUri(context))
|
||||
var folder = context.getExternalFilesDir("tracks")
|
||||
var tracklist: Tracklist = Tracklist()
|
||||
when (json.isNotBlank()) {
|
||||
// convert JSON and return as tracklist
|
||||
true -> try {
|
||||
tracklist = getCustomGson().fromJson(json, Tracklist::class.java)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.i(TAG, folder.toString())
|
||||
if (folder != null)
|
||||
{
|
||||
folder.walk().filter{ f: File -> f.isFile }.forEach{ track_file ->
|
||||
val track_json = readTextFile(context, track_file.toUri())
|
||||
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
|
||||
|
@ -165,7 +167,7 @@ object FileHelper {
|
|||
|
||||
/* Creates Uri for json track file */
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -175,21 +177,6 @@ object FileHelper {
|
|||
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 fun renameTrackSuspended(context: Context, track: Track, newName: String) {
|
||||
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 fun saveTrackSuspended(track: Track, saveGpxToo: Boolean) {
|
||||
return suspendCoroutine { cont ->
|
||||
|
@ -223,13 +202,12 @@ object FileHelper {
|
|||
|
||||
|
||||
/* 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 ->
|
||||
cont.resume(deleteTrack(context, position, tracklist))
|
||||
cont.resume(deleteTrack(context, tracklist_element, tracklist))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Suspend function: Deletes tracks that are not starred using deleteTracks */
|
||||
suspend fun deleteNonStarredSuspended(context: Context, tracklist: Tracklist): Tracklist {
|
||||
return suspendCoroutine { cont ->
|
||||
|
@ -243,7 +221,6 @@ object FileHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Suspend function: Wrapper for readTracklist */
|
||||
suspend fun readTracklistSuspended(context: Context): Tracklist {
|
||||
return suspendCoroutine {cont ->
|
||||
|
@ -251,7 +228,6 @@ object FileHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Suspend function: Wrapper for copyFile */
|
||||
suspend fun saveCopyOfFileSuspended(context: Context, originalFileUri: Uri, targetFileUri: Uri, deleteOriginal: Boolean = false) {
|
||||
return suspendCoroutine { cont ->
|
||||
|
@ -259,7 +235,6 @@ object FileHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Save Track as JSON to storage */
|
||||
private fun saveTrack(track: Track, saveGpxToo: Boolean) {
|
||||
val jsonString: String = getTrackJsonString(track)
|
||||
|
@ -276,7 +251,6 @@ object FileHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Save Temp Track as JSON to storage */
|
||||
private fun saveTempTrack(context: Context, track: 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 */
|
||||
private fun getTracklistFileUri(context: Context): Uri {
|
||||
return File(context.getExternalFilesDir(""), Keys.TRACKLIST_FILE).toUri()
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Renames track */
|
||||
private fun renameTrack(context: Context, track: Track, newName: String) {
|
||||
// search track in tracklist
|
||||
val tracklist: Tracklist = readTracklist(context)
|
||||
var trackUriString: String = String()
|
||||
tracklist.tracklistElements.forEach { tracklistElement ->
|
||||
if (tracklistElement.getTrackId() == track.getTrackId()) {
|
||||
if (tracklistElement.id == track.id) {
|
||||
// rename tracklist element
|
||||
tracklistElement.name = newName
|
||||
trackUriString = tracklistElement.trackUriString
|
||||
}
|
||||
}
|
||||
if (trackUriString.isNotEmpty()) {
|
||||
// save tracklist
|
||||
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
||||
// rename track
|
||||
track.name = newName
|
||||
// save track
|
||||
|
@ -337,29 +288,27 @@ object FileHelper {
|
|||
/* Deletes multiple tracks */
|
||||
private fun deleteTracks(context: Context, tracklistElements: MutableList<TracklistElement>, tracklist: Tracklist): Tracklist {
|
||||
tracklistElements.forEach { tracklistElement ->
|
||||
// delete track files
|
||||
tracklistElement.trackUriString.toUri().toFile().delete()
|
||||
tracklistElement.gpxUriString.toUri().toFile().delete()
|
||||
// subtract track length from total distance
|
||||
tracklist.totalDistanceAll -= tracklistElement.length
|
||||
deleteTrack(context, tracklistElement, tracklist)
|
||||
}
|
||||
tracklist.tracklistElements.removeAll{ tracklistElements.contains(it) }
|
||||
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
||||
return tracklist
|
||||
}
|
||||
|
||||
|
||||
/* Deletes one track */
|
||||
private fun deleteTrack(context: Context, position: Int, tracklist: Tracklist): Tracklist {
|
||||
val tracklistElement: TracklistElement = tracklist.tracklistElements[position]
|
||||
private fun deleteTrack(context: Context, tracklist_element: TracklistElement, tracklist: Tracklist): Tracklist {
|
||||
// delete track files
|
||||
tracklistElement.trackUriString.toUri().toFile().delete()
|
||||
tracklistElement.gpxUriString.toUri().toFile().delete()
|
||||
// subtract track length from total distance
|
||||
tracklist.totalDistanceAll -= tracklistElement.length
|
||||
val json_file: File = tracklist_element.trackUriString.toUri().toFile()
|
||||
if (json_file.isFile)
|
||||
{
|
||||
json_file.delete()
|
||||
}
|
||||
val gpx_file: File = tracklist_element.gpxUriString.toUri().toFile()
|
||||
if (gpx_file.isFile)
|
||||
{
|
||||
gpx_file.delete()
|
||||
}
|
||||
// remove track element from list
|
||||
tracklist.tracklistElements.removeIf { TrackHelper.getTrackId(it) == TrackHelper.getTrackId(tracklistElement) }
|
||||
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
||||
tracklist.tracklistElements.removeIf {it.id == tracklist_element.id}
|
||||
return tracklist
|
||||
}
|
||||
|
||||
|
@ -389,17 +338,14 @@ object FileHelper {
|
|||
return json
|
||||
}
|
||||
|
||||
|
||||
/* Creates a Gson object */
|
||||
private fun getCustomGson(): Gson {
|
||||
val gsonBuilder = GsonBuilder()
|
||||
gsonBuilder.setDateFormat("M/d/yy hh:mm a")
|
||||
gsonBuilder.setDateFormat("yyyy-MM-dd-HH-mm-ss")
|
||||
gsonBuilder.excludeFieldsWithoutExposeAnnotation()
|
||||
return gsonBuilder.create()
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Converts byte value into a human readable format */
|
||||
// Source: https://programming.guide/java/formatting-byte-size-to-human-readable-format.html
|
||||
fun getReadableByteCount(bytes: Long, si: Boolean = true): String {
|
||||
|
|
|
@ -14,13 +14,14 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import android.widget.Toast
|
||||
import androidx.core.net.toUri
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
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.TracklistElement
|
||||
import org.y20k.trackbook.core.WayPoint
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
/*
|
||||
* TrackHelper object
|
||||
|
@ -42,15 +40,6 @@ object TrackHelper {
|
|||
/* Define log tag */
|
||||
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 */
|
||||
fun addWayPointToTrack(track: Track, location: Location, accuracyMultiplier: Int, resumed: Boolean): Pair<Boolean, Track> {
|
||||
// Step 1: Get previous location
|
||||
|
@ -85,7 +74,7 @@ object TrackHelper {
|
|||
if (shouldBeAdded) {
|
||||
// Step 3.1: Update distance (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
||||
if (!resumed) {
|
||||
track.length = track.length + LocationHelper.calculateDistance(previousLocation, location)
|
||||
track.distance = track.distance + LocationHelper.calculateDistance(previousLocation, location)
|
||||
}
|
||||
// Step 3.2: Update altitude values
|
||||
val altitude: Double = location.altitude
|
||||
|
@ -109,17 +98,15 @@ object TrackHelper {
|
|||
track.longitude = location.longitude
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
||||
/* Calculates time passed since last stop of recording */
|
||||
fun calculateDurationOfPause(recordingStop: Date): Long = GregorianCalendar.getInstance().time.time - recordingStop.time
|
||||
|
||||
|
||||
/* Creates GPX string for given track */
|
||||
fun createGpxString(track: Track): String {
|
||||
var gpxString: String
|
||||
|
@ -236,7 +223,6 @@ object TrackHelper {
|
|||
return gpxTrack.toString()
|
||||
}
|
||||
|
||||
|
||||
/* Toggles starred flag for given position */
|
||||
fun toggleStarred(context: Context, track: Track, latitude: Double, longitude: Double): Track {
|
||||
track.wayPoints.forEach { waypoint ->
|
||||
|
@ -250,28 +236,4 @@ object TrackHelper {
|
|||
}
|
||||
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.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -29,6 +30,7 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
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.TracklistElement
|
||||
import org.y20k.trackbook.helpers.*
|
||||
import java.util.*
|
||||
|
||||
|
||||
/*
|
||||
|
@ -70,10 +71,6 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
|||
// load tracklist
|
||||
tracklist = FileHelper.readTracklist(context)
|
||||
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
|
||||
is 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
|
||||
is ElementTrackViewHolder -> {
|
||||
val positionInTracklist: Int = position -1
|
||||
val positionInTracklist: Int = position - 1 // Element 0 is the statistics element.
|
||||
val elementTrackViewHolder: ElementTrackViewHolder = holder as ElementTrackViewHolder
|
||||
elementTrackViewHolder.trackNameView.text = tracklist.tracklistElements[positionInTracklist].name
|
||||
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 */
|
||||
fun removeTrackAtPosition(context: Context, position: Int) {
|
||||
CoroutineScope(IO).launch {
|
||||
val positionInTracklist = position - 1
|
||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, positionInTracklist, tracklist) }
|
||||
val index = position - 1 // position 0 is the statistics element
|
||||
val tracklist_element = tracklist.tracklistElements[index]
|
||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, tracklist_element, tracklist) }
|
||||
// wait for result and store in tracklist
|
||||
withContext(Main) {
|
||||
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 */
|
||||
fun removeTrackById(context: Context, trackId: Long) {
|
||||
CoroutineScope(IO).launch {
|
||||
// reload tracklist //todo check if necessary
|
||||
// tracklist = FileHelper.readTracklist(context)
|
||||
val positionInTracklist: Int = findPosition(trackId)
|
||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, positionInTracklist, tracklist) }
|
||||
tracklist = FileHelper.readTracklist(context)
|
||||
val index: Int = tracklist.tracklistElements.indexOfFirst {it.id == trackId}
|
||||
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
|
||||
val position = index + 1 // position 0 is the statistics element
|
||||
withContext(Main) {
|
||||
tracklist = deferred.await()
|
||||
val positionInRecyclerView: Int = positionInTracklist + 1 // position 0 is the statistics element
|
||||
notifyItemChanged(0)
|
||||
notifyItemRemoved(positionInRecyclerView)
|
||||
notifyItemRemoved(position)
|
||||
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 */
|
||||
private fun findPosition(trackId: Long): Int {
|
||||
tracklist.tracklistElements.forEachIndexed {index, tracklistElement ->
|
||||
if (tracklistElement.getTrackId() == trackId) return index
|
||||
if (tracklistElement.id == trackId)
|
||||
{
|
||||
return index
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
@ -214,21 +218,19 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
|||
tracklist.tracklistElements[position].starred = true
|
||||
}
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
FileHelper.saveTracklistSuspended(context, tracklist, GregorianCalendar.getInstance().time)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Creates the track data string */
|
||||
private fun createTrackDataString(position: Int): String {
|
||||
val tracklistElement: TracklistElement = tracklist.tracklistElements[position]
|
||||
val track_duration_string = DateTimeHelper.convertToReadableTime(context, tracklistElement.duration)
|
||||
val trackDataString: String
|
||||
when (tracklistElement.name == tracklistElement.dateString) {
|
||||
// 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
|
||||
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
|
||||
}
|
||||
|
@ -242,7 +244,7 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
|||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldItem = oldList.tracklistElements[oldItemPosition]
|
||||
val newItem = newList.tracklistElements[newItemPosition]
|
||||
return TrackHelper.getTrackId(oldItem) == TrackHelper.getTrackId(newItem)
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
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 {
|
||||
val oldItem = oldList.tracklistElements[oldItemPosition]
|
||||
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 */
|
||||
fun updateLiveStatics(length: Float, duration: Long, trackingState: Int) {
|
||||
fun updateLiveStatics(distance: Float, duration: Long, trackingState: Int) {
|
||||
// toggle visibility
|
||||
val trackingActive: Boolean = trackingState != Keys.STATE_TRACKING_NOT_STARTED
|
||||
liveStatisticsDistanceView.isVisible = trackingActive
|
||||
liveStatisticsDurationView.isVisible = trackingActive
|
||||
// update distance and duration (and add outline)
|
||||
val distanceString: String = LengthUnitHelper.convertDistanceToString(length, useImperial)
|
||||
val distanceString: String = LengthUnitHelper.convertDistanceToString(distance, useImperial)
|
||||
liveStatisticsDistanceView.text = distanceString
|
||||
liveStatisticsDistanceOutlineView.text = distanceString
|
||||
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 statisticsSheet: NestedScrollView
|
||||
private val statisticsView: View
|
||||
private val trackidView: MaterialTextView
|
||||
private val distanceView: MaterialTextView
|
||||
private val stepsTitleView: MaterialTextView
|
||||
private val stepsView: MaterialTextView
|
||||
|
@ -120,6 +121,7 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
|||
// get views for statistics sheet
|
||||
statisticsSheet = rootView.findViewById(R.id.statistics_sheet)
|
||||
statisticsView = rootView.findViewById(R.id.statistics_view)
|
||||
trackidView = rootView.findViewById(R.id.statistics_data_trackid)
|
||||
distanceView = rootView.findViewById(R.id.statistics_data_distance)
|
||||
stepsTitleView = rootView.findViewById(R.id.statistics_p_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 */
|
||||
fun saveViewStateToTrack() {
|
||||
if (track.latitude != 0.0 && track.longitude != 0.0) {
|
||||
fun saveViewStateToTrack()
|
||||
{
|
||||
if (track.latitude != 0.0 && track.longitude != 0.0)
|
||||
{
|
||||
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
|
||||
val steps: String
|
||||
if (track.stepCount == -1f) {
|
||||
if (track.stepCount == -1f)
|
||||
{
|
||||
steps = context.getString(R.string.statistics_sheet_p_steps_no_pedometer)
|
||||
stepsTitleView.isGone = true
|
||||
stepsView.isGone = true
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
steps = track.stepCount.roundToInt().toString()
|
||||
stepsTitleView.isVisible = true
|
||||
stepsView.isVisible = true
|
||||
|
@ -215,11 +221,12 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
|||
|
||||
// populate views
|
||||
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
|
||||
waypointsView.text = track.wayPoints.size.toString()
|
||||
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)
|
||||
recordingStopView.text = DateTimeHelper.convertToReadableDateAndTime(track.recordingStop)
|
||||
maxAltitudeView.text = LengthUnitHelper.convertDistanceToString(track.maxAltitude, useImperialUnits)
|
||||
|
|
|
@ -75,17 +75,41 @@
|
|||
app:srcCompat="@drawable/ic_save_to_storage_24dp" />
|
||||
|
||||
<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_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/statistics_sheet_p_distance"
|
||||
android:text="@string/statistics_sheet_p_trackid"
|
||||
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_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
|
||||
android:id="@+id/statistics_data_distance"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
<string name="marker_description_time">Time</string>
|
||||
<string name="marker_description_accuracy">Accuracy</string>
|
||||
<!-- 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_steps">Steps taken:</string>
|
||||
<string name="statistics_sheet_p_steps_no_pedometer">pedometer not available</string>
|
||||
|
|
Loading…
Reference in a new issue