Add "when was I here?" button.

This commit is contained in:
voussoir 2023-03-24 22:37:16 -07:00
parent d8d9bc23ff
commit ebfbf05006
10 changed files with 238 additions and 181 deletions

View file

@ -7,7 +7,7 @@ The goal of this fork is to make 24/7 recording easier. I want to be able to run
1. trkpt stores points in an SQLite database instead of json files.
• Instead of storing the database in the app's private area (`/Android/data/...`), you can put the database in a folder that you sync to your PC with [Syncthing](https://f-droid.org/en/packages/com.nutomic.syncthingandroid/).
• You can put the database in a folder that you sync to your PC with [Syncthing](https://f-droid.org/en/packages/com.nutomic.syncthingandroid/).
2. trkpt does not store "tracks" as objects. Instead, tracks are rendered and exported on the fly by querying the database of trackpoints.
@ -16,3 +16,11 @@ The goal of this fork is to make 24/7 recording easier. I want to be able to run
• Although Trackbook has a feature to omit points that are close together, natural GPS inaccuracy and drift is large enough to create points that are far apart, leading to clouds over time.
4. trkpt removes the feature of "starring" waypoints. I recommend using [OsmAnd](https://f-droid.org/en/packages/net.osmand.plus/) to store your favorite places.
## Mirrors
https://github.com/voussoir/trkpt
https://gitlab.com/voussoir/trkpt
https://codeberg.org/voussoir/trkpt

View file

@ -60,6 +60,14 @@ class Database(val trackbook: net.voussoir.trkpt.Trackbook)
commit()
}
fun delete_trkpt_start_end(device_id: String, start_time: Long, end_time: Long)
{
Log.i("VOUSSOIR", "Track.delete ${device_id} ${start_time} -- ${end_time}.")
this.begin_transaction()
this.connection.delete("trkpt", "device_id = ? AND time > ? AND time < ?", arrayOf(device_id, start_time.toString(), end_time.toString()))
this.commit()
}
fun insert_trkpt(trkpt: net.voussoir.trkpt.Trkpt)
{
Log.i("VOUSSOIR", "Database.insert_trkpt")
@ -76,6 +84,56 @@ class Database(val trackbook: net.voussoir.trkpt.Trackbook)
connection.insert("trkpt", null, values)
}
fun select_trkpt_start_end(device_id: String, start_time: Long, end_time: Long): Iterator<Trkpt>
{
Log.i("VOUSSOIR", "Track.trkpt_generator: Querying points between ${start_time} -- ${end_time}.")
return _trkpt_generator(this.connection.rawQuery(
"SELECT device_id, lat, lon, time, ele, accuracy, sat FROM trkpt WHERE device_id = ? AND time > ? AND time < ? ORDER BY time ASC",
arrayOf(device_id, start_time.toString(), end_time.toString())
))
}
fun select_trkpt_bounding_box(device_id: String, north: Double, south: Double, east: Double, west: Double): Iterator<Trkpt>
{
Log.i("VOUSSOIR", "Track.trkpt_generator: Querying points between $north, $south, $east, $west.")
return _trkpt_generator(this.connection.rawQuery(
"SELECT device_id, lat, lon, time, ele, accuracy, sat FROM trkpt WHERE device_id = ? AND lat >= ? AND lat <= ? AND lon >= ? AND lon <= ? ORDER BY time ASC",
arrayOf(device_id, south.toString(), north.toString(), west.toString(), east.toString())
))
}
fun _trkpt_generator(cursor: Cursor) = iterator<Trkpt>
{
val COLUMN_DEVICE = cursor.getColumnIndex("device_id")
val COLUMN_LAT = cursor.getColumnIndex("lat")
val COLUMN_LON = cursor.getColumnIndex("lon")
val COLUMN_ELE = cursor.getColumnIndex("ele")
val COLUMN_SAT = cursor.getColumnIndex("sat")
val COLUMN_ACCURACY = cursor.getColumnIndex("accuracy")
val COLUMN_TIME = cursor.getColumnIndex("time")
try
{
while (cursor.moveToNext())
{
val trkpt = Trkpt(
device_id=cursor.getString(COLUMN_DEVICE),
provider="",
latitude=cursor.getDouble(COLUMN_LAT),
longitude=cursor.getDouble(COLUMN_LON),
altitude=cursor.getDouble(COLUMN_ELE),
accuracy=cursor.getFloat(COLUMN_ACCURACY),
time=cursor.getLong(COLUMN_TIME),
numberSatellites=cursor.getInt(COLUMN_SAT),
)
yield(trkpt)
}
}
finally
{
cursor.close()
}
}
fun delete_homepoint(id: Long)
{
Log.i("VOUSSOIR", "Database.delete_homepoint")

View file

@ -21,7 +21,6 @@
package net.voussoir.trkpt
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Handler
import android.os.Looper
@ -34,23 +33,13 @@ import java.util.*
data class Track (
val database: net.voussoir.trkpt.Database,
val device_id: String,
var start_time: Date,
var end_time: Date,
var name: String = "",
var _start_time: Long = 0L,
var _end_time: Long = 0L,
val trkpts: ArrayList<Trkpt> = ArrayList<Trkpt>(),
var view_latitude: Double = Keys.DEFAULT_LATITUDE,
var view_longitude: Double = Keys.DEFAULT_LONGITUDE,
var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMAT_VERSION,
)
{
fun delete()
{
Log.i("VOUSSOIR", "Track.delete ${device_id} ${start_time} -- ${end_time}.")
database.begin_transaction()
database.connection.delete("trkpt", "device_id = ? AND time > ? AND time < ?", arrayOf(device_id, start_time.time.toString(), end_time.time.toString()))
database.commit()
}
fun export_gpx(context: Context, fileuri: Uri): Uri?
{
if (! database.ready)
@ -88,7 +77,7 @@ data class Track (
write("\t\t<trkseg>")
var previous: Trkpt? = null
for (trkpt in trkpt_generator())
for (trkpt in this.trkpts)
{
if (previous != null && (trkpt.time - previous.time) > (Keys.STOP_OVER_THRESHOLD))
{
@ -96,9 +85,10 @@ data class Track (
write("\t\t<trkseg>")
}
write("\t\t\t<trkpt lat=\"${trkpt.latitude}\" lon=\"${trkpt.longitude}\">")
write("\t\t\t\t<ele>${trkpt.altitude}</ele>")
write("\t\t\t\t<time>${iso8601(trkpt.time)}</time>")
write("\t\t\t\t<unix>${trkpt.time}</unix>")
write("\t\t\t\t<ele>${trkpt.altitude}</ele>")
write("\t\t\t\t<accuracy>${trkpt.accuracy}</accuracy>")
write("\t\t\t\t<sat>${trkpt.numberSatellites}</sat>")
write("\t\t\t</trkpt>")
previous = trkpt
@ -114,107 +104,65 @@ data class Track (
return fileuri
}
fun load_trkpts()
fun load_trkpts(points: Iterator<Trkpt>)
{
this.trkpts.clear()
trkpt_generator().forEach { trkpt -> this.trkpts.add(trkpt) }
if (this.trkpts.size > 0)
{
this.view_latitude = this.trkpts.first().latitude
this.view_longitude = this.trkpts.first().longitude
}
}
fun statistics(): TrackStatistics
{
Log.i("VOUSSOIR", "Track.statistics")
var first: Trkpt? = null
var last: Trkpt? = null
var previous: Trkpt? = null
val stats = TrackStatistics()
for (trkpt in trkpt_generator())
{
if (previous == null)
{
first = trkpt
previous = trkpt
stats.max_altitude = trkpt.altitude
stats.min_altitude = trkpt.altitude
continue
}
stats.distance += previous.toLocation().distanceTo(trkpt.toLocation())
val ascentdiff = trkpt.altitude - previous.altitude
if (ascentdiff > 0)
{
stats.total_ascent += ascentdiff
}
else
{
stats.total_descent += ascentdiff
}
if (trkpt.altitude > stats.max_altitude)
{
stats.max_altitude = trkpt.altitude
}
if (trkpt.altitude < stats.min_altitude)
{
stats.min_altitude = trkpt.altitude
}
previous = trkpt
last = trkpt
}
if (first == null || last == null)
{
return stats
}
stats.duration = last.time - first.time
stats.velocity = stats.distance / (stats.duration / 1000)
return stats
}
fun trkpt_generator() = iterator<Trkpt>
{
var cursor: Cursor = database.connection.rawQuery(
"SELECT lat, lon, time, ele, accuracy, sat FROM trkpt WHERE device_id = ? AND time > ? AND time < ? ORDER BY time ASC",
arrayOf(device_id, start_time.time.toString(), end_time.time.toString())
)
Log.i("VOUSSOIR", "Track.trkpt_generator: Querying points between ${start_time} -- ${end_time}, ${cursor.count} results")
val COLUMN_LAT = cursor.getColumnIndex("lat")
val COLUMN_LON = cursor.getColumnIndex("lon")
val COLUMN_ELE = cursor.getColumnIndex("ele")
val COLUMN_SAT = cursor.getColumnIndex("sat")
val COLUMN_ACCURACY = cursor.getColumnIndex("accuracy")
val COLUMN_TIME = cursor.getColumnIndex("time")
try
{
while (cursor.moveToNext())
{
val trkpt = Trkpt(
device_id=device_id,
provider="",
latitude=cursor.getDouble(COLUMN_LAT),
longitude=cursor.getDouble(COLUMN_LON),
altitude=cursor.getDouble(COLUMN_ELE),
accuracy=cursor.getFloat(COLUMN_ACCURACY),
time=cursor.getLong(COLUMN_TIME),
numberSatellites=cursor.getInt(COLUMN_SAT),
)
yield(trkpt)
}
}
finally
{
cursor.close()
}
points.forEach { trkpt -> this.trkpts.add(trkpt) }
}
}
data class TrackStatistics(
val trkpts: ArrayList<Trkpt>,
var distance: Double = 0.0,
var duration: Long = 0,
var velocity: Double = 0.0,
var total_ascent: Double = 0.0,
var total_descent: Double = 0.0,
var max_altitude: Double = 0.0,
var min_altitude: Double = 0.0,
var min_altitude: Double = 0.0
)
{
init
{
Log.i("VOUSSOIR", "Track.statistics")
var first: Trkpt? = null
var last: Trkpt? = null
var previous: Trkpt? = null
for (trkpt in trkpts)
{
if (previous == null)
{
first = trkpt
previous = trkpt
max_altitude = trkpt.altitude
min_altitude = trkpt.altitude
continue
}
distance += previous.toLocation().distanceTo(trkpt.toLocation())
val ascentdiff = trkpt.altitude - previous.altitude
if (ascentdiff > 0)
{
total_ascent += ascentdiff
}
else
{
total_descent += ascentdiff
}
if (trkpt.altitude > max_altitude)
{
max_altitude = trkpt.altitude
}
if (trkpt.altitude < min_altitude)
{
min_altitude = trkpt.altitude
}
previous = trkpt
last = trkpt
}
if (first != null && last != null)
{
duration = last.time - first.time
velocity = distance / (duration / 1000)
}
}
}

View file

@ -64,8 +64,7 @@ import net.voussoir.trkpt.helpers.LengthUnitHelper
import net.voussoir.trkpt.helpers.PreferencesHelper
import net.voussoir.trkpt.helpers.UiHelper
import net.voussoir.trkpt.helpers.create_start_end_markers
import net.voussoir.trkpt.helpers.iso8601
import net.voussoir.trkpt.helpers.iso8601_parse
import net.voussoir.trkpt.helpers.iso8601_local
import java.text.SimpleDateFormat
import java.util.*
@ -85,6 +84,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
lateinit var track_query_end_date: DatePicker
lateinit var track_query_end_time: TimePicker
lateinit var delete_selected_trkpt_button: ImageButton
lateinit var when_was_i_here_button: ImageButton
var track_query_start_time_previous: Int = 0
var track_query_end_time_previous: Int = 0
private lateinit var mapView: MapView
@ -118,15 +118,18 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
{
this.trackbook = (requireContext().applicationContext as Trackbook)
val database: net.voussoir.trkpt.Database = (requireActivity().applicationContext as Trackbook).database
val requested_start_time = this.requireArguments().getString(Keys.ARG_TRACK_START_TIME)!!.toLong()
val requested_end_time = this.requireArguments().getString(Keys.ARG_TRACK_STOP_TIME)!!.toLong()
track = Track(
database=database,
name=this.requireArguments().getString(Keys.ARG_TRACK_TITLE, ""),
database=this.trackbook.database,
device_id= this.requireArguments().getString(Keys.ARG_TRACK_DEVICE_ID, ""),
start_time=iso8601_parse(this.requireArguments().getString(Keys.ARG_TRACK_START_TIME)!!),
end_time=iso8601_parse(this.requireArguments().getString(Keys.ARG_TRACK_STOP_TIME)!!),
name=this.requireArguments().getString(Keys.ARG_TRACK_TITLE, ""),
)
track.load_trkpts()
track.load_trkpts(this.trackbook.database.select_trkpt_start_end(
device_id= this.requireArguments().getString(Keys.ARG_TRACK_DEVICE_ID, ""),
start_time=requested_start_time,
end_time=requested_end_time,
))
rootView = inflater.inflate(R.layout.fragment_track, container, false)
mapView = rootView.findViewById(R.id.map)
save_track_button = rootView.findViewById(R.id.save_button)
@ -142,7 +145,11 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
mapView.isVerticalMapRepetitionEnabled = false
mapView.setMultiTouchControls(true)
mapView.zoomController.setVisibility(org.osmdroid.views.CustomZoomButtonsController.Visibility.NEVER)
controller.setCenter(GeoPoint(track.view_latitude, track.view_longitude))
if (track.trkpts.size > 0)
{
val first = track.trkpts.first()
controller.setCenter(GeoPoint(first.latitude, first.longitude))
}
controller.setZoom(Keys.DEFAULT_ZOOM_LEVEL)
// trkpt_infowindow = MarkerInfoWindow(R.layout.trkpt_infowindow, mapView)
@ -170,8 +177,8 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
mapView.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS)
}
val actual_start_time: Date = if (track.trkpts.isEmpty()) track.start_time else Date(track.trkpts.first().time)
val actual_end_time: Date = if (track.trkpts.isEmpty()) track.end_time else Date(track.trkpts.last().time)
val actual_start_time: Date = if (track.trkpts.isEmpty()) Date(requested_start_time) else Date(track.trkpts.first().time)
val actual_end_time: Date = if (track.trkpts.isEmpty()) Date(requested_end_time) else Date(track.trkpts.last().time)
track_query_start_date = rootView.findViewById(R.id.track_query_start_date)
val start_cal = GregorianCalendar()
@ -263,6 +270,18 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
mapView.invalidate()
}
}
when_was_i_here_button = rootView.findViewById(R.id.when_was_i_here_button)
when_was_i_here_button.setOnClickListener {
Log.i("VOUSSOIR", "when_was_i_here_button.")
track.load_trkpts(trackbook.database.select_trkpt_bounding_box(
device_id=track.device_id,
north=mapView.boundingBox.actualNorth,
south=mapView.boundingBox.actualSouth,
east=mapView.boundingBox.lonEast,
west=mapView.boundingBox.lonWest,
))
render_track()
}
save_track_button.setOnClickListener {
openSaveGpxDialog()
@ -303,6 +322,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
fun render_track()
{
Log.i("VOUSSOIR", "TrackFragment.render_track")
mapView.invalidate()
mapView.overlays.clear()
track_segment_overlays.clear()
delete_selected_trkpt_button.visibility = View.GONE
@ -362,7 +382,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
}
val trkpt = (points[point]) as Trkpt
Log.i("VOUSSOIR", "Clicked ${trkpt.device_id} ${trkpt.time}")
selected_trkpt_info.text = "${trkpt.time}\n${iso8601(trkpt.time)}\n${trkpt.latitude}\n${trkpt.longitude}"
selected_trkpt_info.text = "${trkpt.time}\n${iso8601_local(trkpt.time)}\n${trkpt.latitude}\n${trkpt.longitude}"
delete_selected_trkpt_button.visibility = View.VISIBLE
return
}
@ -406,14 +426,17 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
private fun setupStatisticsViews()
{
val stats: TrackStatistics = track.statistics()
val stats: TrackStatistics = TrackStatistics(track.trkpts)
trackNameView.text = track.name
distanceView.text = LengthUnitHelper.convertDistanceToString(stats.distance, useImperialUnits)
waypointsView.text = track.trkpts.size.toString()
durationView.text = DateTimeHelper.convertToReadableTime(requireContext(), stats.duration)
velocityView.text = LengthUnitHelper.convertToVelocityString(stats.velocity, useImperialUnits)
recordingStartView.text = DateTimeHelper.convertToReadableDateAndTime(track.start_time)
recordingStopView.text = DateTimeHelper.convertToReadableDateAndTime(track.end_time)
if (track.trkpts.isNotEmpty())
{
recordingStartView.text = DateTimeHelper.convertToReadableDateAndTime(Date(track.trkpts.first().time))
recordingStopView.text = DateTimeHelper.convertToReadableDateAndTime(Date(track.trkpts.last().time))
}
maxAltitudeView.text = LengthUnitHelper.convertDistanceToString(stats.max_altitude, useImperialUnits)
minAltitudeView.text = LengthUnitHelper.convertDistanceToString(stats.min_altitude, useImperialUnits)
positiveElevationView.text = LengthUnitHelper.convertDistanceToString(stats.total_ascent, useImperialUnits)
@ -464,12 +487,13 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
override fun run()
{
Log.i("VOUSSOIR", "TrackFragment.requery_and_render")
track.start_time = get_datetime(track_query_start_date, track_query_start_time, seconds=0)
track.end_time = get_datetime(track_query_end_date, track_query_end_time, seconds=59)
track.load_trkpts()
track.load_trkpts(trackbook.database.select_trkpt_start_end(
track.device_id,
start_time=get_datetime(track_query_start_date, track_query_start_time, seconds=0).time,
end_time=get_datetime(track_query_end_date, track_query_end_time, seconds=59).time,
))
Log.i("VOUSSOIR", "TrackFragment.requery_and_render: Reloaded ${track.trkpts.size} trkpts.")
render_track()
mapView.invalidate()
}
}
@ -499,33 +523,22 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
/* Overrides onYesNoDialog from YesNoDialogListener */
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String)
{
when (type)
if (type == Keys.DIALOG_DELETE_TRACK && dialogResult && track.trkpts.isNotEmpty())
{
Keys.DIALOG_DELETE_TRACK -> {
when (dialogResult)
{
// user tapped remove track
true -> {
track.delete()
trackbook.database.delete_trkpt_start_end(track.device_id, track.trkpts.first().time, track.trkpts.last().time)
handler.removeCallbacks(requery_and_render)
handler.postDelayed(requery_and_render, RERENDER_DELAY)
// switch to TracklistFragment and remove track there
// val bundle: Bundle = bundleOf(Keys.ARG_TRACK_ID to layout.track.id)
// findNavController().navigate(R.id.tracklist_fragment, bundle)
}
else ->
{
;
}
}
}
}
}
/* Opens up a file picker to select the save location */
private fun openSaveGpxDialog()
{
val export_name: String = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(track.start_time) + " " + track.device_id + Keys.GPX_FILE_EXTENSION
if (track.trkpts.isEmpty())
{
return
}
val export_name: String = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(track.trkpts.first().time) + " " + track.device_id + Keys.GPX_FILE_EXTENSION
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = Keys.MIME_TYPE_GPX

View file

@ -20,30 +20,44 @@
package net.voussoir.trkpt
import android.Manifest
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.app.TaskStackBuilder
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.Manifest
import android.app.NotificationChannel
import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.os.*
import android.media.AudioManager
import android.media.ToneGenerator
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import net.voussoir.trkpt.helpers.PreferencesHelper
import net.voussoir.trkpt.helpers.getDefaultLocation
import net.voussoir.trkpt.helpers.getLastKnownLocation
import net.voussoir.trkpt.helpers.isAccurateEnough
import net.voussoir.trkpt.helpers.isBetterLocation
import net.voussoir.trkpt.helpers.isDifferentEnough
import net.voussoir.trkpt.helpers.isGpsEnabled
import net.voussoir.trkpt.helpers.isNetworkEnabled
import net.voussoir.trkpt.helpers.isRecentEnough
import net.voussoir.trkpt.helpers.iso8601
import net.voussoir.trkpt.helpers.random_device_id
import org.osmdroid.util.GeoPoint
import java.util.*
import net.voussoir.trkpt.helpers.*
class TrackerService: Service()
{
@ -64,6 +78,7 @@ class TrackerService: Service()
private lateinit var notificationManager: NotificationManager
private lateinit var notification_builder: NotificationCompat.Builder
val beeper = ToneGenerator(AudioManager.STREAM_MUSIC, 100)
private lateinit var locationManager: LocationManager
private lateinit var gpsLocationListener: LocationListener
@ -189,16 +204,18 @@ class TrackerService: Service()
{
Log.i("VOUSSOIR", "Processing point ${location.time} ${location.latitude}, ${location.longitude}.")
// beeper.startTone(ToneGenerator.TONE_PROP_ACK, 150)
if (location.time == currentBestLocation.time)
{
return
}
// if (! isBetterLocation(location, currentBestLocation))
// {
// Log.i("VOUSSOIR", "Not better than previous.")
// return
// }
if (! isBetterLocation(location, currentBestLocation))
{
Log.i("VOUSSOIR", "Not better than previous.")
return
}
currentBestLocation = location

View file

@ -35,7 +35,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.Main
import net.voussoir.trkpt.helpers.iso8601
import net.voussoir.trkpt.tracklist.TracklistAdapter
/*
@ -80,8 +79,8 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
val bundle: Bundle = bundleOf(
Keys.ARG_TRACK_TITLE to track.name,
Keys.ARG_TRACK_DEVICE_ID to track.device_id,
Keys.ARG_TRACK_START_TIME to iso8601(track.start_time),
Keys.ARG_TRACK_STOP_TIME to iso8601(track.end_time),
Keys.ARG_TRACK_START_TIME to track._start_time.toString(),
Keys.ARG_TRACK_STOP_TIME to track._end_time.toString(),
)
findNavController().navigate(R.id.fragment_track, bundle)
}

View file

@ -14,6 +14,12 @@ fun iso8601(timestamp: Long): String
return iso8601_format.format(timestamp)
}
fun iso8601_local(timestamp: Long): String
{
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")
return iso8601_format.format(timestamp)
}
fun iso8601(datetime: Date): String
{
return iso8601(datetime.time)

View file

@ -23,9 +23,6 @@ import java.text.DateFormat
import java.util.*
import java.util.concurrent.TimeUnit
/*
* DateTimeHelper object
*/
object DateTimeHelper {
/* Converts milliseconds to mm:ss or hh:mm:ss */

View file

@ -64,12 +64,12 @@ class TracklistAdapter(val fragment: Fragment, val database: net.voussoir.trkpt.
{
val trackdate = cursor.getString(0)
val device_id = cursor.getString(1)
val start_time: Date? = df.parse(trackdate + "T00:00:00.000")
val stop_time: Date? = df.parse(trackdate + "T23:59:59.999")
val start_time: Long? = df.parse(trackdate + "T00:00:00.000").time
val stop_time: Long? = df.parse(trackdate + "T23:59:59.999").time
Log.i("VOUSSOIR", "TracklistAdapter prep track ${trackdate}")
if (start_time != null && stop_time != null)
{
val track = Track(database=database, device_id=device_id, start_time=start_time, end_time=stop_time)
val track = Track(database=database, device_id=device_id, _start_time=start_time, _end_time=stop_time)
track.name = "$trackdate $device_id"
tracks.add(track)
}
@ -115,7 +115,7 @@ class TracklistAdapter(val fragment: Fragment, val database: net.voussoir.trkpt.
/* Get track name for given position */
fun getTrackName(positionInRecyclerView: Int): String
{
return SimpleDateFormat("yyyy-MM-dd", Locale.US).format(tracks[positionInRecyclerView].start_time)
return SimpleDateFormat("yyyy-MM-dd", Locale.US).format(tracks[positionInRecyclerView]._start_time)
}
fun delete_track_at_position(context: Context, index: Int)

View file

@ -21,22 +21,12 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/selected_trkpt_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:gravity="right"
android:text="time\nlat\nlong"
app:fontFamily="monospace"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/delete_selected_trkpt_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="Delete selected trackpoint"
android:tooltipText="Delete selected trackpoint"
android:src="@drawable/ic_delete_24dp"
android:visibility="gone"
app:backgroundTint="@color/default_transparent"
@ -44,6 +34,18 @@
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_delete_24dp" />
<ImageButton
android:id="@+id/when_was_i_here_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="When was I here?"
android:tooltipText="When was I here?"
android:src="@drawable/ic_gps_24dp"
app:backgroundTint="@color/default_transparent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_gps_24dp" />
<DatePicker
android:id="@+id/track_query_start_date"
@ -106,9 +108,18 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/trackfragment_tools_constraint_layout">
app:layout_constraintTop_toBottomOf="@+id/trackfragment_tools_constraint_layout"/>
</org.osmdroid.views.MapView>
<TextView
android:id="@+id/selected_trkpt_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:gravity="right"
android:text="time\nlat\nlong"
app:fontFamily="monospace"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/trackfragment_tools_constraint_layout"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/zoom_out_button"