Add "when was I here?" button.
This commit is contained in:
parent
d8d9bc23ff
commit
ebfbf05006
10 changed files with 238 additions and 181 deletions
10
README.md
10
README.md
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
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 ->
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue