checkpoint

This commit is contained in:
voussoir 2023-03-14 22:14:25 -07:00
parent b0edefcb4b
commit 0133524fa2
7 changed files with 226 additions and 45 deletions

View file

@ -30,8 +30,8 @@ import java.util.*
data class Track ( data class Track (
val database: Database, val database: Database,
val device_id: String, val device_id: String,
val start_time: Date, var start_time: Date,
val stop_time: Date, var end_time: Date,
var name: String = "", var name: String = "",
val trkpts: ArrayDeque<Trkpt> = ArrayDeque<Trkpt>(), val trkpts: ArrayDeque<Trkpt> = ArrayDeque<Trkpt>(),
var view_latitude: Double = Keys.DEFAULT_LATITUDE, var view_latitude: Double = Keys.DEFAULT_LATITUDE,
@ -135,7 +135,6 @@ data class Track (
continue continue
} }
stats.distance += previous.toLocation().distanceTo(trkpt.toLocation()) stats.distance += previous.toLocation().distanceTo(trkpt.toLocation())
Log.i("VOUSSOIR", previous.toLocation().distanceTo(trkpt.toLocation()).toString())
val ascentdiff = trkpt.altitude - previous.altitude val ascentdiff = trkpt.altitude - previous.altitude
if (ascentdiff > 0) if (ascentdiff > 0)
{ {
@ -169,8 +168,9 @@ data class Track (
{ {
val cursor: Cursor = database.connection.rawQuery( val 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", "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(), stop_time.time.toString()) arrayOf(device_id, start_time.time.toString(), end_time.time.toString())
) )
Log.i("VOUSSOIR", "Querying points between ${start_time} -- ${end_time}")
val COLUMN_LAT = cursor.getColumnIndex("lat") val COLUMN_LAT = cursor.getColumnIndex("lat")
val COLUMN_LON = cursor.getColumnIndex("lon") val COLUMN_LON = cursor.getColumnIndex("lon")
val COLUMN_ELE = cursor.getColumnIndex("ele") val COLUMN_ELE = cursor.getColumnIndex("ele")

View file

@ -49,9 +49,8 @@ class TrackFragment : Fragment(), YesNoDialog.YesNoDialogListener
name=this.requireArguments().getString(Keys.ARG_TRACK_TITLE, ""), name=this.requireArguments().getString(Keys.ARG_TRACK_TITLE, ""),
device_id= this.requireArguments().getString(Keys.ARG_TRACK_DEVICE_ID, ""), device_id= this.requireArguments().getString(Keys.ARG_TRACK_DEVICE_ID, ""),
start_time= iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_START_TIME)!!), start_time= iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_START_TIME)!!),
stop_time=iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_STOP_TIME)!!), end_time=iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_STOP_TIME)!!),
) )
track.load_trkpts()
layout = TrackFragmentLayoutHolder(activity as Context, inflater, container, track) layout = TrackFragmentLayoutHolder(activity as Context, inflater, container, track)
// set up share button // set up share button

View file

@ -91,7 +91,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
Keys.ARG_TRACK_TITLE to track.name, Keys.ARG_TRACK_TITLE to track.name,
Keys.ARG_TRACK_DEVICE_ID to track.device_id, Keys.ARG_TRACK_DEVICE_ID to track.device_id,
Keys.ARG_TRACK_START_TIME to iso8601_format.format(track.start_time), Keys.ARG_TRACK_START_TIME to iso8601_format.format(track.start_time),
Keys.ARG_TRACK_STOP_TIME to iso8601_format.format(track.stop_time), Keys.ARG_TRACK_STOP_TIME to iso8601_format.format(track.end_time),
) )
findNavController().navigate(R.id.fragment_track, bundle) findNavController().navigate(R.id.fragment_track, bundle)
} }

View file

@ -69,7 +69,7 @@ class TracklistAdapter(val fragment: Fragment, val database: Database) : Recycle
Log.i("VOUSSOIR", "TracklistAdapter prep track ${trackdate}") Log.i("VOUSSOIR", "TracklistAdapter prep track ${trackdate}")
if (start_time != null && stop_time != null) if (start_time != null && stop_time != null)
{ {
val track = Track(database=database, device_id=device_id, start_time=start_time, stop_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" track.name = "$trackdate $device_id"
tracks.add(track) tracks.add(track)
} }

View file

@ -18,10 +18,15 @@ package org.y20k.trackbook.ui
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.DatePicker
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.TimePicker
import android.widget.Toast import android.widget.Toast
import androidx.constraintlayout.widget.Group import androidx.constraintlayout.widget.Group
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
@ -34,19 +39,17 @@ import org.osmdroid.events.ZoomEvent
import org.osmdroid.tileprovider.tilesource.TileSourceFactory import org.osmdroid.tileprovider.tilesource.TileSourceFactory
import org.osmdroid.util.GeoPoint import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.ItemizedIconOverlay
import org.osmdroid.views.overlay.OverlayItem
import org.osmdroid.views.overlay.TilesOverlay import org.osmdroid.views.overlay.TilesOverlay
import org.osmdroid.views.overlay.compass.CompassOverlay import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.Track import org.y20k.trackbook.Track
import org.y20k.trackbook.TrackStatistics import org.y20k.trackbook.TrackStatistics
import org.y20k.trackbook.helpers.* import org.y20k.trackbook.helpers.*
import java.util.*
/*
* TrackFragmentLayoutHolder class
*/
//data class TrackFragmentLayoutHolder(private var context: Context, private var markerListener: MapOverlayHelper.MarkerListener, private var inflater: LayoutInflater, private var statusBarHeight: Int, private var container: ViewGroup?, var track: Track): MapListener { TODO REMOVE
data class TrackFragmentLayoutHolder( data class TrackFragmentLayoutHolder(
private var context: Context, private var context: Context,
private var inflater: LayoutInflater, private var inflater: LayoutInflater,
@ -54,11 +57,16 @@ data class TrackFragmentLayoutHolder(
var track: Track var track: Track
): MapListener ): MapListener
{ {
/* Main class variables */
val rootView: View val rootView: View
val save_track_button: ImageButton val save_track_button: ImageButton
val deleteButton: ImageButton val deleteButton: ImageButton
val trackNameView: MaterialTextView val trackNameView: MaterialTextView
val track_query_start_date: DatePicker
val track_query_start_time: TimePicker
val track_query_end_date: DatePicker
val track_query_end_time: TimePicker
var track_query_start_time_previous: Int
var track_query_end_time_previous: Int
private val mapView: MapView private val mapView: MapView
private var controller: IMapController private var controller: IMapController
private val statisticsSheetBehavior: BottomSheetBehavior<View> private val statisticsSheetBehavior: BottomSheetBehavior<View>
@ -78,17 +86,93 @@ data class TrackFragmentLayoutHolder(
private val negativeElevationView: MaterialTextView private val negativeElevationView: MaterialTextView
private val elevationDataViews: Group private val elevationDataViews: Group
private val useImperialUnits: Boolean private val useImperialUnits: Boolean
private val handler: Handler = Handler(Looper.getMainLooper())
private var special_points_overlay: ItemizedIconOverlay<OverlayItem>? = null
private var track_overlay: SimpleFastPointOverlay? = null
val RERENDER_DELAY: Long = 1000
init init
{ {
// find views
rootView = inflater.inflate(R.layout.fragment_track, container, false) rootView = inflater.inflate(R.layout.fragment_track, container, false)
mapView = rootView.findViewById(R.id.map) mapView = rootView.findViewById(R.id.map)
save_track_button = rootView.findViewById(R.id.save_button) save_track_button = rootView.findViewById(R.id.save_button)
deleteButton = rootView.findViewById(R.id.delete_button) deleteButton = rootView.findViewById(R.id.delete_button)
trackNameView = rootView.findViewById(R.id.statistics_track_name_headline) trackNameView = rootView.findViewById(R.id.statistics_track_name_headline)
// basic map setup track.load_trkpts()
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)
track_query_start_date = rootView.findViewById(R.id.track_query_start_date)
val start_cal = GregorianCalendar()
start_cal.time = actual_start_time
track_query_start_date.init(start_cal.get(Calendar.YEAR), start_cal.get(Calendar.MONTH), start_cal.get(Calendar.DAY_OF_MONTH), object: DatePicker.OnDateChangedListener {
override fun onDateChanged(p0: DatePicker?, p1: Int, p2: Int, p3: Int)
{
handler.removeCallbacks(requery_and_render)
handler.postDelayed(requery_and_render, RERENDER_DELAY)
}
})
track_query_start_time = rootView.findViewById(R.id.track_query_start_time)
track_query_start_time.setIs24HourView(true)
track_query_start_time.hour = actual_start_time.hours
track_query_start_time.minute = actual_start_time.minutes
track_query_start_time_previous = (actual_start_time.hours * 60) + actual_start_time.minutes
track_query_start_time.setOnTimeChangedListener(object : TimePicker.OnTimeChangedListener{
override fun onTimeChanged(p0: TimePicker?, p1: Int, p2: Int)
{
handler.removeCallbacks(requery_and_render)
val newminute = (p1 * 60) + p2
Log.i("VOUSSOIR", "End time changed $newminute")
if (newminute < track_query_start_time_previous && (track_query_start_time_previous - newminute > 60))
{
increment_datepicker(track_query_start_date)
}
else if (newminute > track_query_start_time_previous && (newminute - track_query_start_time_previous > 60))
{
decrement_datepicker(track_query_start_date)
}
track_query_start_time_previous = newminute
handler.postDelayed(requery_and_render, RERENDER_DELAY)
}
})
track_query_end_date = rootView.findViewById(R.id.track_query_end_date)
val end_cal = GregorianCalendar()
end_cal.time = actual_end_time
track_query_end_date.init(end_cal.get(Calendar.YEAR), end_cal.get(Calendar.MONTH), end_cal.get(Calendar.DAY_OF_MONTH), object: DatePicker.OnDateChangedListener {
override fun onDateChanged(p0: DatePicker?, p1: Int, p2: Int, p3: Int)
{
handler.removeCallbacks(requery_and_render)
handler.postDelayed(requery_and_render, RERENDER_DELAY)
}
})
track_query_end_time = rootView.findViewById(R.id.track_query_end_time)
track_query_end_time.setIs24HourView(true)
track_query_end_time.hour = actual_end_time.hours
track_query_end_time.minute = actual_end_time.minutes
track_query_end_time_previous = (actual_end_time.hours * 60) + actual_end_time.minutes
track_query_end_time.setOnTimeChangedListener(object : TimePicker.OnTimeChangedListener{
override fun onTimeChanged(p0: TimePicker?, p1: Int, p2: Int)
{
handler.removeCallbacks(requery_and_render)
val newminute = (p1 * 60) + p2
Log.i("VOUSSOIR", "End time changed $newminute")
if (newminute < track_query_end_time_previous && (track_query_end_time_previous - newminute > 60))
{
increment_datepicker(track_query_end_date)
}
else if (newminute > track_query_end_time_previous && (newminute - track_query_end_time_previous > 60))
{
decrement_datepicker(track_query_end_date)
}
track_query_end_time_previous = newminute
handler.postDelayed(requery_and_render, RERENDER_DELAY)
}
})
controller = mapView.controller controller = mapView.controller
mapView.addMapListener(this) mapView.addMapListener(this)
mapView.isTilesScaledToDpi = true mapView.isTilesScaledToDpi = true
@ -99,7 +183,6 @@ data class TrackFragmentLayoutHolder(
controller.setCenter(GeoPoint(track.view_latitude, track.view_longitude)) controller.setCenter(GeoPoint(track.view_latitude, track.view_longitude))
controller.setZoom(Keys.DEFAULT_ZOOM_LEVEL) controller.setZoom(Keys.DEFAULT_ZOOM_LEVEL)
// get views for statistics sheet
statisticsSheet = rootView.findViewById(R.id.statistics_sheet) statisticsSheet = rootView.findViewById(R.id.statistics_sheet)
statisticsView = rootView.findViewById(R.id.statistics_view) statisticsView = rootView.findViewById(R.id.statistics_view)
distanceView = rootView.findViewById(R.id.statistics_data_distance) distanceView = rootView.findViewById(R.id.statistics_data_distance)
@ -116,36 +199,59 @@ data class TrackFragmentLayoutHolder(
negativeElevationView = rootView.findViewById(R.id.statistics_data_negative_elevation) negativeElevationView = rootView.findViewById(R.id.statistics_data_negative_elevation)
elevationDataViews = rootView.findViewById(R.id.elevation_data) elevationDataViews = rootView.findViewById(R.id.elevation_data)
// get measurement unit system
useImperialUnits = PreferencesHelper.loadUseImperialUnits() useImperialUnits = PreferencesHelper.loadUseImperialUnits()
// set dark map tiles, if necessary
if (AppThemeHelper.isDarkModeOn(context as Activity)) { if (AppThemeHelper.isDarkModeOn(context as Activity)) {
mapView.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS) mapView.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS)
} }
// add compass to map
val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), mapView)
compassOverlay.enableCompass()
compassOverlay.setCompassCenter(36f, 36f)
// compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / UiHelper.getDensityScalingFactor(context))) TODO REMOVE
mapView.overlays.add(compassOverlay)
if (track.trkpts.isNotEmpty()) {
createSpecialMakersTrackOverlay(context, mapView, track.trkpts, Keys.STATE_TRACKING_STOPPED, displayStartEndMarker = true)
createTrackOverlay(context, mapView, track.trkpts, Keys.STATE_TRACKING_STOPPED)
}
// set up and show statistics sheet
statisticsSheetBehavior = BottomSheetBehavior.from<View>(statisticsSheet) statisticsSheetBehavior = BottomSheetBehavior.from<View>(statisticsSheet)
statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
render_track()
}
fun render_track()
{
if (special_points_overlay != null)
{
mapView.overlays.remove(special_points_overlay)
mapView.overlays.remove(track_overlay)
}
if (track.trkpts.isNotEmpty())
{
special_points_overlay = createSpecialMakersTrackOverlay(context, mapView, track.trkpts, Keys.STATE_TRACKING_STOPPED, displayStartEndMarker = true)
track_overlay = createTrackOverlay(context, mapView, track.trkpts, Keys.STATE_TRACKING_STOPPED)
}
setupStatisticsViews() setupStatisticsViews()
} }
/* Sets up the statistics sheet */ fun get_datetime(datepicker: DatePicker, timepicker: TimePicker, seconds: Int): Date
{
val cal = GregorianCalendar.getInstance()
cal.set(datepicker.year, datepicker.month, datepicker.dayOfMonth, timepicker.hour, timepicker.minute, seconds)
Log.i("VOUSSOIR", cal.time.toString())
return cal.time
}
fun decrement_datepicker(picker: DatePicker)
{
val cal = GregorianCalendar.getInstance()
cal.set(picker.year, picker.month, picker.dayOfMonth)
cal.add(Calendar.DATE, -1)
picker.updateDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH))
}
fun increment_datepicker(picker: DatePicker)
{
val cal = GregorianCalendar.getInstance()
cal.set(picker.year, picker.month, picker.dayOfMonth)
cal.add(Calendar.DATE, 1)
picker.updateDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH))
}
private fun setupStatisticsViews() private fun setupStatisticsViews()
{ {
// populate views
val stats: TrackStatistics = track.statistics() val stats: TrackStatistics = track.statistics()
trackNameView.text = track.name trackNameView.text = track.name
distanceView.text = LengthUnitHelper.convertDistanceToString(stats.distance, useImperialUnits) distanceView.text = LengthUnitHelper.convertDistanceToString(stats.distance, useImperialUnits)
@ -153,7 +259,7 @@ data class TrackFragmentLayoutHolder(
durationView.text = DateTimeHelper.convertToReadableTime(context, stats.duration) durationView.text = DateTimeHelper.convertToReadableTime(context, stats.duration)
velocityView.text = LengthUnitHelper.convertToVelocityString(stats.velocity, useImperialUnits) velocityView.text = LengthUnitHelper.convertToVelocityString(stats.velocity, useImperialUnits)
recordingStartView.text = DateTimeHelper.convertToReadableDateAndTime(track.start_time) recordingStartView.text = DateTimeHelper.convertToReadableDateAndTime(track.start_time)
recordingStopView.text = DateTimeHelper.convertToReadableDateAndTime(track.stop_time) recordingStopView.text = DateTimeHelper.convertToReadableDateAndTime(track.end_time)
maxAltitudeView.text = LengthUnitHelper.convertDistanceToString(stats.max_altitude, useImperialUnits) maxAltitudeView.text = LengthUnitHelper.convertDistanceToString(stats.max_altitude, useImperialUnits)
minAltitudeView.text = LengthUnitHelper.convertDistanceToString(stats.min_altitude, useImperialUnits) minAltitudeView.text = LengthUnitHelper.convertDistanceToString(stats.min_altitude, useImperialUnits)
positiveElevationView.text = LengthUnitHelper.convertDistanceToString(stats.total_ascent, useImperialUnits) positiveElevationView.text = LengthUnitHelper.convertDistanceToString(stats.total_ascent, useImperialUnits)
@ -192,5 +298,17 @@ data class TrackFragmentLayoutHolder(
return (event != null) return (event != null)
} }
private val requery_and_render: Runnable = object : Runnable {
override fun run()
{
Log.i("VOUSSOIR", "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()
Log.i("VOUSSOIR", "Reloaded ${track.trkpts.size} trkpts.")
render_track()
mapView.invalidate()
}
}
} }

View file

@ -7,19 +7,82 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".MainActivity"> tools:context=".MainActivity">
<!-- OSM MAP --> <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<DatePicker
android:id="@+id/track_query_start_date"
android:layout_width="wrap_content"
android:layout_height="130dp"
android:translationX="-45dp"
android:translationY="-30dp"
android:calendarViewShown="false"
android:datePickerMode="spinner"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:scaleX="0.5"
android:scaleY="0.5"
/>
<TimePicker
android:id="@+id/track_query_start_time"
android:layout_width="wrap_content"
android:layout_height="130dp"
android:translationX="-150dp"
android:translationY="-30dp"
android:timePickerMode="spinner"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/track_query_start_date"
android:scaleX="0.5"
android:scaleY="0.5"
/>
<DatePicker
android:id="@+id/track_query_end_date"
android:layout_width="wrap_content"
android:layout_height="130dp"
android:translationX="-45dp"
android:translationY="-100dp"
android:datePickerMode="spinner"
android:calendarViewShown="false"
app:layout_constraintTop_toBottomOf="@+id/track_query_start_date"
app:layout_constraintStart_toStartOf="parent"
android:scaleX="0.5"
android:scaleY="0.5"
/>
<TimePicker
android:id="@+id/track_query_end_time"
android:layout_width="wrap_content"
android:layout_height="130dp"
android:translationX="-150dp"
android:translationY="-100dp"
android:timePickerMode="spinner"
app:layout_constraintTop_toBottomOf="@+id/track_query_start_time"
app:layout_constraintStart_toEndOf="@+id/track_query_start_date"
android:scaleX="0.5"
android:scaleY="0.5"
/>
<org.osmdroid.views.MapView <org.osmdroid.views.MapView
android:id="@+id/map" android:id="@+id/map"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_alignParentStart="true" app:layout_constraintTop_toBottomOf="@+id/track_query_end_time"
android:layout_alignParentTop="true" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:contentDescription="@string/descr_map_last_track" android:contentDescription="@string/descr_map_last_track"
app:layout_anchor="@+id/map" >
app:layout_anchorGravity="center">
</org.osmdroid.views.MapView> </org.osmdroid.views.MapView>
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- BOTTOM SHEET --> <!-- BOTTOM SHEET -->
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/statistics_sheet" android:id="@+id/statistics_sheet"

View file

@ -22,6 +22,7 @@
app:layout_constraintVertical_bias="0" /> app:layout_constraintVertical_bias="0" />
<!-- ONBOARDING --> <!-- ONBOARDING -->
<include <include
layout="@layout/track_onboarding" layout="@layout/track_onboarding"
android:layout_width="0dp" android:layout_width="0dp"