checkpoint
This commit is contained in:
parent
5b68b33f1d
commit
06ca0fd2b3
12 changed files with 324 additions and 260 deletions
|
@ -53,6 +53,14 @@ class Database(val trackbook: Trackbook)
|
|||
this.connection.endTransaction()
|
||||
}
|
||||
|
||||
fun delete_trkpt(device_id: String, time: Long)
|
||||
{
|
||||
Log.i("VOUSSOIR", "Database.delete_trkpt")
|
||||
begin_transaction()
|
||||
connection.delete("trkpt", "device_id = ? AND time = ?", arrayOf(device_id, time.toString()))
|
||||
commit()
|
||||
}
|
||||
|
||||
fun insert_trkpt(trkpt: Trkpt)
|
||||
{
|
||||
Log.i("VOUSSOIR", "Database.insert_trkpt")
|
||||
|
@ -60,7 +68,7 @@ class Database(val trackbook: Trackbook)
|
|||
put("device_id", trkpt.device_id)
|
||||
put("lat", trkpt.latitude)
|
||||
put("lon", trkpt.longitude)
|
||||
put("time", GregorianCalendar.getInstance().time.time)
|
||||
put("time", trkpt.time)
|
||||
put("accuracy", trkpt.accuracy)
|
||||
put("sat", trkpt.numberSatellites)
|
||||
put("ele", trkpt.altitude)
|
||||
|
@ -108,8 +116,9 @@ class Database(val trackbook: Trackbook)
|
|||
{
|
||||
begin_transaction()
|
||||
this.connection.execSQL("CREATE TABLE IF NOT EXISTS meta(name TEXT PRIMARY KEY, value TEXT)")
|
||||
this.connection.execSQL("CREATE TABLE IF NOT EXISTS trkpt(lat REAL NOT NULL, lon REAL NOT NULL, time INTEGER NOT NULL, accuracy REAL, device_id INTEGER NOT NULL, ele INTEGER, sat INTEGER, PRIMARY KEY(lat, lon, time, device_id))")
|
||||
this.connection.execSQL("CREATE TABLE IF NOT EXISTS trkpt(lat REAL NOT NULL, lon REAL NOT NULL, time INTEGER NOT NULL, accuracy REAL, device_id INTEGER NOT NULL, ele INTEGER, sat INTEGER, PRIMARY KEY(device_id, time))")
|
||||
this.connection.execSQL("CREATE TABLE IF NOT EXISTS homepoints(id INTEGER PRIMARY KEY, lat REAL NOT NULL, lon REAL NOT NULL, radius REAL NOT NULL, name TEXT)")
|
||||
this.connection.execSQL("CREATE INDEX IF NOT EXISTS index_trkpt_device_id_time on trkpt(device_id, time)")
|
||||
// The pragmas don't seem to execute unless you call moveToNext.
|
||||
var cursor: Cursor
|
||||
cursor = this.connection.rawQuery("PRAGMA journal_mode = DELETE", null)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.y20k.trackbook
|
||||
|
||||
import android.graphics.Color
|
||||
import java.util.*
|
||||
|
||||
/*
|
||||
|
@ -109,4 +110,7 @@ object Keys {
|
|||
// notification
|
||||
const val TRACKER_SERVICE_NOTIFICATION_ID: Int = 1
|
||||
const val NOTIFICATION_CHANNEL_RECORDING: String = "notificationChannelIdRecordingChannel"
|
||||
|
||||
const val POLYLINE_THICKNESS = 4F
|
||||
val POLYLINE_COLOR = Color.argb(255, 255, 0, 255)
|
||||
}
|
||||
|
|
|
@ -40,8 +40,6 @@ import androidx.fragment.app.Fragment
|
|||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.osmdroid.api.IGeoPoint
|
||||
import org.osmdroid.api.IMapController
|
||||
import org.osmdroid.events.MapEventsReceiver
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
|
@ -51,46 +49,46 @@ import org.osmdroid.views.overlay.MapEventsOverlay
|
|||
import org.osmdroid.views.overlay.Overlay
|
||||
import org.osmdroid.views.overlay.OverlayItem
|
||||
import org.osmdroid.views.overlay.Polygon
|
||||
import org.osmdroid.views.overlay.Polyline
|
||||
import org.osmdroid.views.overlay.TilesOverlay
|
||||
import org.osmdroid.views.overlay.compass.CompassOverlay
|
||||
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
||||
import org.y20k.trackbook.helpers.*
|
||||
|
||||
/*
|
||||
* MapFragment class
|
||||
*/
|
||||
class MapFragment : Fragment()
|
||||
{
|
||||
/* Main class variables */
|
||||
private lateinit var trackbook: Trackbook
|
||||
|
||||
private var bound: Boolean = false
|
||||
private val handler: Handler = Handler(Looper.getMainLooper())
|
||||
val handler: Handler = Handler(Looper.getMainLooper())
|
||||
private var trackingState: Int = Keys.STATE_TRACKING_STOPPED
|
||||
private var gpsProviderActive: Boolean = false
|
||||
private var networkProviderActive: Boolean = false
|
||||
private lateinit var currentBestLocation: Location
|
||||
private lateinit var trackerService: TrackerService
|
||||
|
||||
private lateinit var trackbook: Trackbook
|
||||
lateinit var rootView: View
|
||||
var continuous_auto_center: Boolean = true
|
||||
lateinit var currentLocationButton: FloatingActionButton
|
||||
private lateinit var trackerService: TrackerService
|
||||
private lateinit var database_changed_listener: DatabaseChangedListener
|
||||
|
||||
var thismapfragment: MapFragment? = null
|
||||
lateinit var rootView: View
|
||||
private lateinit var mapView: MapView
|
||||
lateinit var mainButton: ExtendedFloatingActionButton
|
||||
|
||||
lateinit var zoom_in_button: FloatingActionButton
|
||||
lateinit var zoom_out_button: FloatingActionButton
|
||||
lateinit var mainButton: ExtendedFloatingActionButton
|
||||
private lateinit var mapView: MapView
|
||||
lateinit var currentLocationButton: FloatingActionButton
|
||||
private var current_track_overlay: Polyline? = null
|
||||
private var current_position_overlays = ArrayList<Overlay>()
|
||||
private var currentTrackOverlay: SimpleFastPointOverlay? = null
|
||||
private lateinit var locationErrorBar: Snackbar
|
||||
private var zoomLevel: Double = Keys.DEFAULT_ZOOM_LEVEL
|
||||
private var homepoints_overlays = ArrayList<Overlay>()
|
||||
private lateinit var database_changed_listener: DatabaseChangedListener
|
||||
private lateinit var locationErrorBar: Snackbar
|
||||
|
||||
/* Overrides onCreate from Fragment */
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.onCreate")
|
||||
super.onCreate(savedInstanceState)
|
||||
thismapfragment = this
|
||||
this.trackbook = (requireContext().applicationContext as Trackbook)
|
||||
database_changed_listener = object: DatabaseChangedListener
|
||||
{
|
||||
|
@ -99,7 +97,7 @@ class MapFragment : Fragment()
|
|||
Log.i("VOUSSOIR", "MapFragment database_ready_changed to ${trackbook.database.ready}")
|
||||
if (trackbook.database.ready)
|
||||
{
|
||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
||||
create_homepoint_overlays()
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -116,7 +114,7 @@ class MapFragment : Fragment()
|
|||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.onCreateView")
|
||||
// find views
|
||||
|
||||
rootView = inflater.inflate(R.layout.fragment_map, container, false)
|
||||
mapView = rootView.findViewById(R.id.map)
|
||||
currentLocationButton = rootView.findViewById(R.id.location_button)
|
||||
|
@ -130,8 +128,6 @@ class MapFragment : Fragment()
|
|||
true
|
||||
}
|
||||
mapView.isLongClickable = true
|
||||
|
||||
// basic map setup
|
||||
mapView.isTilesScaledToDpi = true
|
||||
mapView.isVerticalMapRepetitionEnabled = false
|
||||
mapView.setTileSource(TileSourceFactory.MAPNIK)
|
||||
|
@ -145,10 +141,8 @@ class MapFragment : Fragment()
|
|||
}
|
||||
|
||||
val densityScalingFactor: Float = UiHelper.getDensityScalingFactor(requireContext())
|
||||
|
||||
val compassOverlay = CompassOverlay(requireContext(), InternalCompassOrientationProvider(requireContext()), mapView)
|
||||
compassOverlay.enableCompass()
|
||||
// compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / densityScalingFactor)) // TODO uncomment when transparent status bar is re-implemented
|
||||
val screen_width = Resources.getSystem().displayMetrics.widthPixels
|
||||
compassOverlay.setCompassCenter((screen_width / densityScalingFactor) - 36f, 36f)
|
||||
mapView.overlays.add(compassOverlay)
|
||||
|
@ -187,7 +181,7 @@ class MapFragment : Fragment()
|
|||
radius=radius,
|
||||
)
|
||||
trackbook.load_homepoints()
|
||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
||||
create_homepoint_overlays()
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
|
@ -198,28 +192,34 @@ class MapFragment : Fragment()
|
|||
mapView.overlays.add(MapEventsOverlay(receiver))
|
||||
|
||||
trackbook.load_homepoints()
|
||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
||||
create_homepoint_overlays()
|
||||
if (database_changed_listener !in trackbook.database_changed_listeners)
|
||||
{
|
||||
trackbook.database_changed_listeners.add(database_changed_listener)
|
||||
}
|
||||
|
||||
create_current_position_overlays(currentBestLocation, trackingState)
|
||||
|
||||
centerMap(currentBestLocation)
|
||||
|
||||
// initialize track overlays
|
||||
currentTrackOverlay = null
|
||||
|
||||
// initialize main button state
|
||||
update_main_button()
|
||||
current_track_overlay = null
|
||||
|
||||
mapView.setOnTouchListener { v, event ->
|
||||
continuous_auto_center = false
|
||||
false
|
||||
}
|
||||
|
||||
// set up buttons
|
||||
update_main_button()
|
||||
mainButton.setOnClickListener {
|
||||
handleTrackingManagementMenu()
|
||||
if (trackingState == Keys.STATE_TRACKING_ACTIVE)
|
||||
{
|
||||
trackerService.stopTracking()
|
||||
}
|
||||
else
|
||||
{
|
||||
startTracking()
|
||||
}
|
||||
handler.post(location_update_redraw)
|
||||
}
|
||||
currentLocationButton.setOnClickListener {
|
||||
centerMap(currentBestLocation, animated=true)
|
||||
|
@ -238,6 +238,7 @@ class MapFragment : Fragment()
|
|||
/* Overrides onStart from Fragment */
|
||||
override fun onStart()
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.onStart")
|
||||
super.onStart()
|
||||
// request location permission if denied
|
||||
if (ContextCompat.checkSelfPermission(activity as Context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED)
|
||||
|
@ -265,6 +266,10 @@ class MapFragment : Fragment()
|
|||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.onPause")
|
||||
super.onPause()
|
||||
if (::trackerService.isInitialized)
|
||||
{
|
||||
trackerService.mapfragment = null
|
||||
}
|
||||
saveBestLocationState(currentBestLocation)
|
||||
if (bound && trackingState != Keys.STATE_TRACKING_ACTIVE) {
|
||||
trackerService.removeGpsLocationListener()
|
||||
|
@ -278,6 +283,10 @@ class MapFragment : Fragment()
|
|||
override fun onStop()
|
||||
{
|
||||
super.onStop()
|
||||
if (::trackerService.isInitialized)
|
||||
{
|
||||
trackerService.mapfragment = null
|
||||
}
|
||||
// unbind from TrackerService
|
||||
if (bound)
|
||||
{
|
||||
|
@ -290,6 +299,7 @@ class MapFragment : Fragment()
|
|||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.onDestroy")
|
||||
super.onDestroy()
|
||||
trackerService.mapfragment = null
|
||||
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
|
||||
if (database_changed_listener in trackbook.database_changed_listeners)
|
||||
|
@ -314,14 +324,17 @@ class MapFragment : Fragment()
|
|||
toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
||||
}
|
||||
|
||||
/* Start recording waypoints */
|
||||
private fun startTracking() {
|
||||
private fun startTracking()
|
||||
{
|
||||
// start service via intent so that it keeps running after unbind
|
||||
val intent = Intent(activity, TrackerService::class.java)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
{
|
||||
// ... start service in foreground to prevent it being killed on Oreo
|
||||
activity?.startForegroundService(intent)
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
activity?.startService(intent)
|
||||
}
|
||||
trackerService.startTracking()
|
||||
|
@ -334,16 +347,9 @@ class MapFragment : Fragment()
|
|||
bound = false
|
||||
// unregister listener for changes in shared preferences
|
||||
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||
// stop receiving location updates
|
||||
handler.removeCallbacks(periodicLocationRequestRunnable)
|
||||
}
|
||||
|
||||
/* Starts / pauses tracking and toggles the recording sub menu_bottom_navigation */
|
||||
private fun handleTrackingManagementMenu()
|
||||
{
|
||||
when (trackingState) {
|
||||
Keys.STATE_TRACKING_ACTIVE -> trackerService.stopTracking()
|
||||
Keys.STATE_TRACKING_STOPPED -> startTracking()
|
||||
if (::trackerService.isInitialized)
|
||||
{
|
||||
trackerService.mapfragment = null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,7 +380,8 @@ class MapFragment : Fragment()
|
|||
continuous_auto_center = true
|
||||
}
|
||||
|
||||
fun saveBestLocationState(currentBestLocation: Location) {
|
||||
fun saveBestLocationState(currentBestLocation: Location)
|
||||
{
|
||||
PreferencesHelper.saveCurrentBestLocation(currentBestLocation)
|
||||
PreferencesHelper.saveZoomLevel(mapView.zoomLevelDouble)
|
||||
continuous_auto_center = true
|
||||
|
@ -395,38 +402,26 @@ class MapFragment : Fragment()
|
|||
/* Mark current position on map */
|
||||
fun create_current_position_overlays(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED)
|
||||
{
|
||||
// Log.i("VOUSSOIR", "MapFragmentLayoutHolder.markCurrentPosition")
|
||||
|
||||
clear_current_position_overlays()
|
||||
|
||||
val locationIsOld: Boolean = !(isRecentEnough(location))
|
||||
|
||||
// create marker
|
||||
val newMarker: Drawable
|
||||
val fillcolor: Int
|
||||
if (trackingState == Keys.STATE_TRACKING_ACTIVE)
|
||||
if (locationIsOld)
|
||||
{
|
||||
fillcolor = Color.argb(64, 0, 0, 0)
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_black_24dp)!!
|
||||
}
|
||||
else if (trackingState == Keys.STATE_TRACKING_ACTIVE)
|
||||
{
|
||||
fillcolor = Color.argb(64, 220, 61, 51)
|
||||
if (locationIsOld)
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_black_24dp)!!
|
||||
}
|
||||
else
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_red_24dp)!!
|
||||
}
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_red_24dp)!!
|
||||
}
|
||||
else
|
||||
{
|
||||
fillcolor = Color.argb(64, 60, 152, 219)
|
||||
if(locationIsOld)
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_black_24dp)!!
|
||||
}
|
||||
else
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_blue_24dp)!!
|
||||
}
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_blue_24dp)!!
|
||||
}
|
||||
|
||||
val current_location_radius = Polygon()
|
||||
|
@ -447,6 +442,21 @@ class MapFragment : Fragment()
|
|||
}
|
||||
}
|
||||
|
||||
fun clear_track_overlay()
|
||||
{
|
||||
mapView.overlays.remove(current_track_overlay)
|
||||
}
|
||||
|
||||
fun create_track_overlay()
|
||||
{
|
||||
clear_track_overlay()
|
||||
val pl = Polyline(mapView)
|
||||
pl.outlinePaint.strokeWidth = Keys.POLYLINE_THICKNESS
|
||||
pl.outlinePaint.color = Keys.POLYLINE_COLOR
|
||||
mapView.overlays.add(pl)
|
||||
current_track_overlay = pl
|
||||
}
|
||||
|
||||
fun clear_homepoint_overlays()
|
||||
{
|
||||
for (ov in homepoints_overlays)
|
||||
|
@ -459,15 +469,16 @@ class MapFragment : Fragment()
|
|||
homepoints_overlays.clear()
|
||||
}
|
||||
|
||||
fun create_homepoint_overlays(context: Context, map_view: MapView, homepoints: List<Homepoint>)
|
||||
fun create_homepoint_overlays()
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.createHomepointOverlays")
|
||||
|
||||
val context = requireContext()
|
||||
val newMarker: Drawable = ContextCompat.getDrawable(context, R.drawable.ic_homepoint_24dp)!!
|
||||
|
||||
clear_homepoint_overlays()
|
||||
|
||||
for (homepoint in homepoints)
|
||||
for (homepoint in trackbook.homepoints)
|
||||
{
|
||||
val p = Polygon()
|
||||
p.points = Polygon.pointsAsCircle(GeoPoint(homepoint.location.latitude, homepoint.location.longitude), homepoint.location.accuracy.toDouble())
|
||||
|
@ -476,7 +487,14 @@ class MapFragment : Fragment()
|
|||
homepoints_overlays.add(p)
|
||||
|
||||
val overlayItems: java.util.ArrayList<OverlayItem> = java.util.ArrayList<OverlayItem>()
|
||||
val overlayItem: OverlayItem = createOverlayItem(context, homepoint.location.latitude, homepoint.location.longitude, homepoint.location.accuracy, homepoint.location.provider.toString(), homepoint.location.time)
|
||||
val overlayItem: OverlayItem = createOverlayItem(
|
||||
context,
|
||||
homepoint.location.latitude,
|
||||
homepoint.location.longitude,
|
||||
homepoint.location.accuracy,
|
||||
homepoint.location.provider.toString(),
|
||||
homepoint.location.time
|
||||
)
|
||||
overlayItem.setMarker(newMarker)
|
||||
overlayItems.add(overlayItem)
|
||||
val homepoint_overlay = ItemizedIconOverlay<OverlayItem>(context, overlayItems,
|
||||
|
@ -504,14 +522,14 @@ class MapFragment : Fragment()
|
|||
delete_button.setOnClickListener {
|
||||
trackbook.database.delete_homepoint(homepoint.id)
|
||||
trackbook.load_homepoints()
|
||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
||||
create_homepoint_overlays()
|
||||
dialog.dismiss()
|
||||
}
|
||||
save_button.setOnClickListener {
|
||||
val radius = radius_input.text.toString().toDoubleOrNull() ?: 25.0
|
||||
trackbook.database.update_homepoint(homepoint.id, name=name_input.text.toString(), radius=radius)
|
||||
trackbook.load_homepoints()
|
||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
||||
create_homepoint_overlays()
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
|
@ -529,19 +547,6 @@ class MapFragment : Fragment()
|
|||
}
|
||||
}
|
||||
|
||||
/* Overlay current track on map */
|
||||
fun create_current_track_overlay(geopoints: MutableList<IGeoPoint>, trackingState: Int)
|
||||
{
|
||||
if (currentTrackOverlay != null)
|
||||
{
|
||||
mapView.overlays.remove(currentTrackOverlay)
|
||||
}
|
||||
if (geopoints.isNotEmpty())
|
||||
{
|
||||
currentTrackOverlay = createTrackOverlay(requireContext(), mapView, geopoints, trackingState)
|
||||
}
|
||||
}
|
||||
|
||||
fun update_main_button()
|
||||
{
|
||||
mainButton.isEnabled = trackbook.database.ready
|
||||
|
@ -592,14 +597,13 @@ class MapFragment : Fragment()
|
|||
// get reference to tracker service
|
||||
val binder = service as TrackerService.LocalBinder
|
||||
trackerService = binder.service
|
||||
trackerService.mapfragment = thismapfragment
|
||||
// get state of tracking and update button if necessary
|
||||
trackingState = trackerService.trackingState
|
||||
update_main_button()
|
||||
// register listener for changes in shared preferences
|
||||
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||
// start listening for location updates
|
||||
handler.removeCallbacks(periodicLocationRequestRunnable)
|
||||
handler.postDelayed(periodicLocationRequestRunnable, 0)
|
||||
}
|
||||
override fun onServiceDisconnected(arg0: ComponentName)
|
||||
{
|
||||
|
@ -608,22 +612,27 @@ class MapFragment : Fragment()
|
|||
}
|
||||
}
|
||||
|
||||
private val periodicLocationRequestRunnable: Runnable = object : Runnable {
|
||||
val location_update_redraw: Runnable = object : Runnable
|
||||
{
|
||||
override fun run()
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.location_update_redraw")
|
||||
currentBestLocation = trackerService.currentBestLocation
|
||||
gpsProviderActive = trackerService.gpsProviderActive
|
||||
networkProviderActive = trackerService.networkProviderActive
|
||||
trackingState = trackerService.trackingState
|
||||
// update location and track
|
||||
|
||||
create_current_position_overlays(currentBestLocation, trackingState)
|
||||
create_current_track_overlay(trackerService.recent_trackpoints_for_mapview, trackingState)
|
||||
// center map, if it had not been dragged/zoomed before
|
||||
if (current_track_overlay == null)
|
||||
{
|
||||
create_track_overlay()
|
||||
}
|
||||
current_track_overlay!!.setPoints(trackerService.recent_trackpoints_for_mapview)
|
||||
|
||||
if (continuous_auto_center)
|
||||
{
|
||||
centerMap(currentBestLocation, animated=false)
|
||||
}
|
||||
handler.postDelayed(this, Keys.REQUEST_CURRENT_LOCATION_INTERVAL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,13 @@ package org.y20k.trackbook
|
|||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.database.DatabaseUtils.dumpCursorToString
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import org.y20k.trackbook.helpers.iso8601_format
|
||||
import org.y20k.trackbook.helpers.iso8601
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
@ -33,7 +34,7 @@ data class Track (
|
|||
var start_time: Date,
|
||||
var end_time: Date,
|
||||
var name: String = "",
|
||||
val trkpts: ArrayDeque<Trkpt> = ArrayDeque<Trkpt>(),
|
||||
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,
|
||||
|
@ -73,7 +74,7 @@ data class Track (
|
|||
>
|
||||
""".trimIndent())
|
||||
write("\t<metadata>")
|
||||
write("\t\t<name>Trackbook Recording: ${this.name}</name>")
|
||||
write("\t\t<name>${this.name}</name>")
|
||||
write("\t\t<device>${this.device_id}</device>")
|
||||
write("\t</metadata>")
|
||||
|
||||
|
@ -81,7 +82,6 @@ data class Track (
|
|||
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
|
||||
dateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||
write("\t<trk>")
|
||||
write("\t\t<name>${this.name}</name>")
|
||||
write("\t\t<trkseg>")
|
||||
|
||||
var previous: Trkpt? = null
|
||||
|
@ -94,7 +94,8 @@ data class Track (
|
|||
}
|
||||
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_format.format(trkpt.time)}</time>")
|
||||
write("\t\t\t\t<time>${iso8601(trkpt.time)}</time>")
|
||||
write("\t\t\t\t<unix>${trkpt.time}</unix>")
|
||||
write("\t\t\t\t<sat>${trkpt.numberSatellites}</sat>")
|
||||
write("\t\t\t</trkpt>")
|
||||
previous = trkpt
|
||||
|
@ -170,11 +171,11 @@ data class Track (
|
|||
|
||||
fun trkpt_generator() = iterator<Trkpt>
|
||||
{
|
||||
val cursor: Cursor = database.connection.rawQuery(
|
||||
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}")
|
||||
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")
|
||||
|
|
|
@ -20,6 +20,7 @@ import YesNoDialog
|
|||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Paint
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
|
@ -48,32 +49,38 @@ import org.osmdroid.events.ZoomEvent
|
|||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.ItemizedIconOverlay
|
||||
import org.osmdroid.views.overlay.OverlayItem
|
||||
import org.osmdroid.views.overlay.Polyline
|
||||
import org.osmdroid.views.overlay.TilesOverlay
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
|
||||
import org.y20k.trackbook.helpers.AppThemeHelper
|
||||
import org.y20k.trackbook.helpers.DateTimeHelper
|
||||
import org.y20k.trackbook.helpers.LengthUnitHelper
|
||||
import org.y20k.trackbook.helpers.PreferencesHelper
|
||||
import org.y20k.trackbook.helpers.createTrackOverlay
|
||||
import org.y20k.trackbook.helpers.UiHelper
|
||||
import org.y20k.trackbook.helpers.create_start_end_markers
|
||||
import org.y20k.trackbook.helpers.iso8601_format
|
||||
import org.y20k.trackbook.helpers.iso8601
|
||||
import org.y20k.trackbook.helpers.iso8601_parse
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||
{
|
||||
private lateinit var trackbook: Trackbook
|
||||
|
||||
lateinit var rootView: View
|
||||
lateinit var save_track_button: ImageButton
|
||||
lateinit var deleteButton: ImageButton
|
||||
lateinit var zoom_in_button: FloatingActionButton
|
||||
lateinit var zoom_out_button: FloatingActionButton
|
||||
lateinit var trackNameView: MaterialTextView
|
||||
lateinit var selected_trkpt_info: MaterialTextView
|
||||
lateinit var track_query_start_date: DatePicker
|
||||
lateinit var track_query_start_time: TimePicker
|
||||
lateinit var track_query_end_date: DatePicker
|
||||
lateinit var track_query_end_time: TimePicker
|
||||
lateinit var delete_selected_trkpt_button: ImageButton
|
||||
var track_query_start_time_previous: Int = 0
|
||||
var track_query_end_time_previous: Int = 0
|
||||
private lateinit var mapView: MapView
|
||||
|
@ -95,22 +102,25 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
|||
private lateinit var negativeElevationView: MaterialTextView
|
||||
private lateinit var elevationDataViews: Group
|
||||
private lateinit var track: Track
|
||||
private lateinit var track_segment_overlays: ArrayDeque<Polyline>
|
||||
private var track_geopoints: MutableList<IGeoPoint> = mutableListOf()
|
||||
private var track_points_overlay: SimpleFastPointOverlay? = null
|
||||
// private lateinit var trkpt_infowindow: InfoWindow
|
||||
private var useImperialUnits: Boolean = false
|
||||
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
|
||||
|
||||
/* Overrides onCreateView from Fragment */
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
||||
{
|
||||
this.trackbook = (requireContext().applicationContext as Trackbook)
|
||||
val database: Database = (requireActivity().applicationContext as Trackbook).database
|
||||
track = Track(
|
||||
database=database,
|
||||
name=this.requireArguments().getString(Keys.ARG_TRACK_TITLE, ""),
|
||||
device_id= this.requireArguments().getString(Keys.ARG_TRACK_DEVICE_ID, ""),
|
||||
start_time= iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_START_TIME)!!),
|
||||
end_time=iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_STOP_TIME)!!),
|
||||
start_time=iso8601_parse(this.requireArguments().getString(Keys.ARG_TRACK_START_TIME)!!),
|
||||
end_time=iso8601_parse(this.requireArguments().getString(Keys.ARG_TRACK_STOP_TIME)!!),
|
||||
)
|
||||
track.load_trkpts()
|
||||
rootView = inflater.inflate(R.layout.fragment_track, container, false)
|
||||
|
@ -131,6 +141,8 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
|||
controller.setCenter(GeoPoint(track.view_latitude, track.view_longitude))
|
||||
controller.setZoom(Keys.DEFAULT_ZOOM_LEVEL)
|
||||
|
||||
// trkpt_infowindow = MarkerInfoWindow(R.layout.trkpt_infowindow, mapView)
|
||||
|
||||
statisticsSheet = rootView.findViewById(R.id.statistics_sheet)
|
||||
statisticsView = rootView.findViewById(R.id.statistics_view)
|
||||
distanceView = rootView.findViewById(R.id.statistics_data_distance)
|
||||
|
@ -227,6 +239,27 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
|||
}
|
||||
})
|
||||
|
||||
selected_trkpt_info = rootView.findViewById(R.id.selected_trkpt_info)
|
||||
delete_selected_trkpt_button = rootView.findViewById(R.id.delete_selected_trkpt_button)
|
||||
delete_selected_trkpt_button.setOnClickListener {
|
||||
Log.i("VOUSSOIR", "delete selected trkpt button.")
|
||||
if (track_points_overlay != null)
|
||||
{
|
||||
val selected = (track_geopoints[track_points_overlay!!.selectedPoint] as Trkpt)
|
||||
track_geopoints.remove(selected)
|
||||
track_points_overlay!!.selectedPoint = null
|
||||
Log.i("VOUSSOIR", selected.rendered_by_polyline?.actualPoints?.size.toString())
|
||||
selected.rendered_by_polyline?.actualPoints?.remove(selected)
|
||||
Log.i("VOUSSOIR", selected.rendered_by_polyline?.actualPoints?.size.toString())
|
||||
selected.rendered_by_polyline?.setPoints(ArrayList(selected.rendered_by_polyline?.actualPoints))
|
||||
Log.i("VOUSSOIR", selected.rendered_by_polyline?.actualPoints?.size.toString())
|
||||
trackbook.database.delete_trkpt(selected.device_id, selected.time)
|
||||
delete_selected_trkpt_button.visibility = View.GONE
|
||||
selected_trkpt_info.text = ""
|
||||
mapView.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
save_track_button.setOnClickListener {
|
||||
openSaveGpxDialog()
|
||||
}
|
||||
|
@ -251,6 +284,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
|||
statisticsSheetBehavior = BottomSheetBehavior.from<View>(statisticsSheet)
|
||||
statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
|
||||
track_segment_overlays = ArrayDeque<Polyline>(10)
|
||||
render_track()
|
||||
|
||||
return rootView
|
||||
|
@ -265,25 +299,81 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
|||
fun render_track()
|
||||
{
|
||||
Log.i("VOUSSOIR", "TrackFragment.render_track")
|
||||
if (special_points_overlay != null)
|
||||
mapView.overlays.clear()
|
||||
track_segment_overlays.clear()
|
||||
delete_selected_trkpt_button.visibility = View.GONE
|
||||
|
||||
setupStatisticsViews()
|
||||
|
||||
if (track.trkpts.isEmpty())
|
||||
{
|
||||
mapView.overlays.remove(special_points_overlay)
|
||||
return
|
||||
}
|
||||
if (track_overlay != null)
|
||||
{
|
||||
mapView.overlays.remove(track_overlay)
|
||||
}
|
||||
val geopoints: MutableList<IGeoPoint> = mutableListOf()
|
||||
Log.i("VOUSSOIR", "MapOverlayHelper.createTrackOverlay")
|
||||
track_geopoints = mutableListOf()
|
||||
for (trkpt in track.trkpts)
|
||||
{
|
||||
geopoints.add(trkpt)
|
||||
track_geopoints.add(trkpt)
|
||||
}
|
||||
if (track.trkpts.isNotEmpty())
|
||||
|
||||
var pl = new_track_segment_overlay()
|
||||
var previous_time: Long = 0
|
||||
for (trkpt in track.trkpts)
|
||||
{
|
||||
track_overlay = createTrackOverlay(requireContext(), mapView, geopoints, Keys.STATE_TRACKING_STOPPED)
|
||||
special_points_overlay = create_start_end_markers(requireContext(), mapView, track.trkpts)
|
||||
if (previous_time > 0 && (trkpt.time - previous_time) > Keys.STOP_OVER_THRESHOLD)
|
||||
{
|
||||
pl = new_track_segment_overlay()
|
||||
}
|
||||
pl.addPoint(trkpt)
|
||||
trkpt.rendered_by_polyline = pl
|
||||
previous_time = trkpt.time
|
||||
}
|
||||
setupStatisticsViews()
|
||||
|
||||
for (pl in track_segment_overlays)
|
||||
{
|
||||
create_start_end_markers(requireContext(), mapView, pl.actualPoints.first() as Trkpt, pl.actualPoints.last() as Trkpt)
|
||||
}
|
||||
|
||||
val pointTheme = SimplePointTheme(track_geopoints, false)
|
||||
val style = Paint()
|
||||
style.style = Paint.Style.FILL
|
||||
style.color = Keys.POLYLINE_COLOR
|
||||
style.flags = Paint.ANTI_ALIAS_FLAG
|
||||
val overlayOptions: SimpleFastPointOverlayOptions = SimpleFastPointOverlayOptions.getDefaultStyle()
|
||||
.setAlgorithm(SimpleFastPointOverlayOptions.RenderingAlgorithm.MEDIUM_OPTIMIZATION)
|
||||
.setSymbol(SimpleFastPointOverlayOptions.Shape.CIRCLE)
|
||||
.setPointStyle(style)
|
||||
.setRadius(((Keys.POLYLINE_THICKNESS + 1 ) / 2) * UiHelper.getDensityScalingFactor(requireContext()))
|
||||
.setIsClickable(true)
|
||||
.setCellSize(12)
|
||||
track_points_overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
|
||||
mapView.overlays.add(track_points_overlay)
|
||||
|
||||
track_points_overlay!!.setOnClickListener(object : SimpleFastPointOverlay.OnClickListener {
|
||||
override fun onClick(points: SimpleFastPointOverlay.PointAdapter?, point: Int?)
|
||||
{
|
||||
if (points == null || point == null || point == 0)
|
||||
{
|
||||
return
|
||||
}
|
||||
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}"
|
||||
delete_selected_trkpt_button.visibility = View.VISIBLE
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun new_track_segment_overlay(): Polyline
|
||||
{
|
||||
var pl = Polyline(mapView)
|
||||
pl.outlinePaint.strokeWidth = Keys.POLYLINE_THICKNESS
|
||||
pl.outlinePaint.color = Keys.POLYLINE_COLOR
|
||||
pl.infoWindow = null
|
||||
track_segment_overlays.add(pl)
|
||||
mapView.overlays.add(pl)
|
||||
return pl
|
||||
}
|
||||
|
||||
fun get_datetime(datepicker: DatePicker, timepicker: TimePicker, seconds: Int): Date
|
||||
|
@ -348,7 +438,16 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
|||
/* Overrides onZoom from MapListener */
|
||||
override fun onZoom(event: ZoomEvent?): Boolean
|
||||
{
|
||||
return (event != null)
|
||||
if (event == null)
|
||||
{
|
||||
return false
|
||||
}
|
||||
if (track_points_overlay == null)
|
||||
{
|
||||
return false
|
||||
}
|
||||
track_points_overlay!!.isEnabled = event.zoomLevel >= 16
|
||||
return true
|
||||
}
|
||||
|
||||
/* Overrides onScroll from MapListener */
|
||||
|
|
|
@ -30,9 +30,7 @@ import android.Manifest
|
|||
import android.os.*
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.osmdroid.api.IGeoPoint
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.overlay.simplefastpoint.LabelledGeoPoint
|
||||
import java.util.*
|
||||
import org.y20k.trackbook.helpers.*
|
||||
|
||||
|
@ -55,8 +53,8 @@ class TrackerService: Service()
|
|||
var location_min_time_ms: Long = 0
|
||||
private val RECENT_TRKPT_COUNT = 7200
|
||||
lateinit var recent_trkpts: Deque<Trkpt>
|
||||
lateinit var recent_displacement_trkpts: Deque<Trkpt>
|
||||
var recent_trackpoints_for_mapview: MutableList<IGeoPoint> = mutableListOf()
|
||||
lateinit var recent_displacement_locations: Deque<Location>
|
||||
var recent_trackpoints_for_mapview: MutableList<GeoPoint> = mutableListOf()
|
||||
var gpsLocationListenerRegistered: Boolean = false
|
||||
var networkLocationListenerRegistered: Boolean = false
|
||||
var bound: Boolean = false
|
||||
|
@ -67,6 +65,7 @@ class TrackerService: Service()
|
|||
private lateinit var notificationHelper: NotificationHelper
|
||||
private lateinit var gpsLocationListener: LocationListener
|
||||
private lateinit var networkLocationListener: LocationListener
|
||||
var mapfragment: MapFragment? = null
|
||||
|
||||
private fun addGpsLocationListener()
|
||||
{
|
||||
|
@ -160,6 +159,11 @@ class TrackerService: Service()
|
|||
|
||||
currentBestLocation = location
|
||||
|
||||
if (mapfragment != null)
|
||||
{
|
||||
mapfragment!!.handler.postDelayed(mapfragment!!.location_update_redraw, 0)
|
||||
}
|
||||
|
||||
if (trackingState != Keys.STATE_TRACKING_ACTIVE)
|
||||
{
|
||||
return
|
||||
|
@ -195,7 +199,7 @@ class TrackerService: Service()
|
|||
return
|
||||
}
|
||||
}
|
||||
if (! (recent_displacement_trkpts.isEmpty() || isDifferentEnough(recent_displacement_trkpts.first().toLocation(), location, omitRests)))
|
||||
if (! (recent_displacement_locations.isEmpty() || isDifferentEnough(recent_displacement_locations.first(), location, omitRests)))
|
||||
{
|
||||
Log.i("VOUSSOIR", "Omitting due to too close to previous.")
|
||||
return
|
||||
|
@ -215,10 +219,10 @@ class TrackerService: Service()
|
|||
recent_trackpoints_for_mapview.removeFirst()
|
||||
}
|
||||
|
||||
recent_displacement_trkpts.add(trkpt)
|
||||
while (recent_displacement_trkpts.size > 5)
|
||||
recent_displacement_locations.add(location)
|
||||
while (recent_displacement_locations.size > 5)
|
||||
{
|
||||
recent_displacement_trkpts.removeFirst()
|
||||
recent_displacement_locations.removeFirst()
|
||||
}
|
||||
|
||||
if (location.time - lastCommit > Keys.COMMIT_INTERVAL)
|
||||
|
@ -275,7 +279,7 @@ class TrackerService: Service()
|
|||
trackbook = (applicationContext as Trackbook)
|
||||
trackbook.load_homepoints()
|
||||
recent_trkpts = ArrayDeque<Trkpt>(RECENT_TRKPT_COUNT)
|
||||
recent_displacement_trkpts = ArrayDeque<Trkpt>(5)
|
||||
recent_displacement_locations = ArrayDeque<Location>(5)
|
||||
recent_trackpoints_for_mapview = mutableListOf()
|
||||
use_gps_location = PreferencesHelper.load_location_gps()
|
||||
use_network_location = PreferencesHelper.load_location_network()
|
||||
|
@ -392,7 +396,7 @@ class TrackerService: Service()
|
|||
addNetworkLocationListener()
|
||||
trackingState = Keys.STATE_TRACKING_ACTIVE
|
||||
PreferencesHelper.saveTrackingState(trackingState)
|
||||
recent_displacement_trkpts.clear()
|
||||
recent_displacement_locations.clear()
|
||||
startForeground(Keys.TRACKER_SERVICE_NOTIFICATION_ID, displayNotification())
|
||||
}
|
||||
|
||||
|
@ -402,7 +406,7 @@ class TrackerService: Service()
|
|||
|
||||
trackingState = Keys.STATE_TRACKING_STOPPED
|
||||
PreferencesHelper.saveTrackingState(trackingState)
|
||||
recent_displacement_trkpts.clear()
|
||||
recent_displacement_locations.clear()
|
||||
displayNotification()
|
||||
stopForeground(STOP_FOREGROUND_DETACH)
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import org.y20k.trackbook.helpers.UiHelper
|
||||
import org.y20k.trackbook.helpers.iso8601_format
|
||||
import org.y20k.trackbook.helpers.iso8601
|
||||
import org.y20k.trackbook.tracklist.TracklistAdapter
|
||||
|
||||
/*
|
||||
|
@ -46,7 +46,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
|||
private lateinit var trackElementList: RecyclerView
|
||||
private lateinit var tracklistOnboarding: ConstraintLayout
|
||||
|
||||
/* Overrides onCreateView from Fragment */
|
||||
/* Overrides onCreate from Fragment */
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
{
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -67,18 +67,6 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
|||
trackElementList.itemAnimator = DefaultItemAnimator()
|
||||
trackElementList.adapter = tracklistAdapter
|
||||
|
||||
// enable swipe to delete
|
||||
val swipeHandler = object : UiHelper.SwipeToDeleteCallback(activity as Context) {
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
// ask user
|
||||
val adapterPosition: Int = viewHolder.adapterPosition // first position in list is reserved for statistics
|
||||
val dialogMessage: String = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${tracklistAdapter.getTrackName(adapterPosition)}"
|
||||
YesNoDialog(this@TracklistFragment 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, payload = adapterPosition)
|
||||
}
|
||||
}
|
||||
val itemTouchHelper = ItemTouchHelper(swipeHandler)
|
||||
itemTouchHelper.attachToRecyclerView(trackElementList)
|
||||
|
||||
// toggle onboarding layout
|
||||
toggleOnboardingLayout()
|
||||
|
||||
|
@ -90,40 +78,15 @@ 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_format.format(track.start_time),
|
||||
Keys.ARG_TRACK_STOP_TIME to iso8601_format.format(track.end_time),
|
||||
Keys.ARG_TRACK_START_TIME to iso8601(track.start_time),
|
||||
Keys.ARG_TRACK_STOP_TIME to iso8601(track.end_time),
|
||||
)
|
||||
findNavController().navigate(R.id.fragment_track, bundle)
|
||||
}
|
||||
|
||||
/* Overrides onYesNoDialog from YesNoDialogListener */
|
||||
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String)
|
||||
{
|
||||
when (type)
|
||||
{
|
||||
Keys.DIALOG_DELETE_TRACK ->
|
||||
{
|
||||
when (dialogResult) {
|
||||
// user tapped remove track
|
||||
true ->
|
||||
{
|
||||
tracklistAdapter.delete_track_at_position(activity as Context, payload)
|
||||
toggleOnboardingLayout()
|
||||
}
|
||||
// user tapped cancel
|
||||
false ->
|
||||
{
|
||||
// The user slid the track over to the side and turned it red, we have to
|
||||
// bring it back.
|
||||
tracklistAdapter.notifyItemChanged(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// toggle onboarding layout
|
||||
private fun toggleOnboardingLayout() {
|
||||
private fun toggleOnboardingLayout()
|
||||
{
|
||||
when (tracklistAdapter.isEmpty()) {
|
||||
true -> {
|
||||
// show onboarding layout
|
||||
|
@ -143,11 +106,13 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
|||
*/
|
||||
inner class CustomLinearLayoutManager(context: Context): LinearLayoutManager(context, VERTICAL, false)
|
||||
{
|
||||
override fun supportsPredictiveItemAnimations(): Boolean {
|
||||
override fun supportsPredictiveItemAnimations(): Boolean
|
||||
{
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onLayoutCompleted(state: RecyclerView.State?) {
|
||||
override fun onLayoutCompleted(state: RecyclerView.State?)
|
||||
{
|
||||
super.onLayoutCompleted(state)
|
||||
// handle delete request from TrackFragment - after layout calculations are complete
|
||||
val deleteTrackId: Long = arguments?.getLong(Keys.ARG_TRACK_ID, -1L) ?: -1L
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.y20k.trackbook
|
|||
import android.location.Location
|
||||
import org.osmdroid.api.IGeoPoint
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.overlay.Polyline
|
||||
import org.y20k.trackbook.helpers.getNumberOfSatellites
|
||||
|
||||
class Trkpt(
|
||||
|
@ -30,6 +31,7 @@ class Trkpt(
|
|||
val accuracy: Float,
|
||||
val time: Long,
|
||||
val numberSatellites: Int = 0,
|
||||
var rendered_by_polyline: Polyline? = null
|
||||
) : GeoPoint(latitude, longitude, altitude)
|
||||
{
|
||||
constructor(device_id: String, location: Location) : this(
|
||||
|
|
|
@ -15,12 +15,25 @@ import java.text.SimpleDateFormat
|
|||
import java.util.*
|
||||
import kotlin.random.Random.Default.nextBits
|
||||
|
||||
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US)
|
||||
private val RNG = SecureRandom()
|
||||
|
||||
fun iso8601(timestamp: Long): String
|
||||
{
|
||||
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||
iso8601_format.timeZone = TimeZone.getTimeZone("UTC")
|
||||
return iso8601_format.format(timestamp)
|
||||
}
|
||||
|
||||
fun iso8601(datetime: Date): String
|
||||
{
|
||||
return iso8601_format.format(datetime)
|
||||
return iso8601(datetime.time)
|
||||
}
|
||||
|
||||
fun iso8601_parse(datetime: String): Date
|
||||
{
|
||||
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||
iso8601_format.timeZone = TimeZone.getTimeZone("UTC")
|
||||
return iso8601_format.parse(datetime)
|
||||
}
|
||||
|
||||
fun random_int(): Int
|
||||
|
|
|
@ -83,15 +83,4 @@ object DateTimeHelper {
|
|||
fun convertToReadableDateAndTime(date: Date, dateStyle: Int = DateFormat.SHORT, timeStyle: Int = DateFormat.SHORT): String {
|
||||
return "${DateFormat.getDateInstance(dateStyle, Locale.getDefault()).format(date)} ${DateFormat.getTimeInstance(timeStyle, Locale.getDefault()).format(date)}"
|
||||
}
|
||||
|
||||
/* Calculates time difference between two locations */
|
||||
fun calculateTimeDistance(previousLocation: Location?, location: Location): Long {
|
||||
var timeDifference: Long = 0L
|
||||
// two data points needed to calculate time difference
|
||||
if (previousLocation != null) {
|
||||
// get time difference
|
||||
timeDifference = location.time - previousLocation.time
|
||||
}
|
||||
return timeDifference
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,81 +17,27 @@
|
|||
package org.y20k.trackbook.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Paint
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.osmdroid.api.IGeoPoint
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.ItemizedIconOverlay
|
||||
import org.osmdroid.views.overlay.OverlayItem
|
||||
import org.osmdroid.views.overlay.simplefastpoint.LabelledGeoPoint
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
|
||||
import org.y20k.trackbook.Keys
|
||||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.Trkpt
|
||||
import java.text.DecimalFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
fun createTrackOverlay(context: Context, map_view: MapView, geopoints: MutableList<IGeoPoint>, trackingState: Int): SimpleFastPointOverlay
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapOverlayHelper.createTrackOverlay")
|
||||
val pointTheme = SimplePointTheme(geopoints, false)
|
||||
val style = Paint()
|
||||
style.style = Paint.Style.FILL
|
||||
style.color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.default_red) else context.getColor(R.color.default_blue)
|
||||
style.flags = Paint.ANTI_ALIAS_FLAG
|
||||
val overlayOptions: SimpleFastPointOverlayOptions = SimpleFastPointOverlayOptions.getDefaultStyle()
|
||||
.setAlgorithm(SimpleFastPointOverlayOptions.RenderingAlgorithm.MAXIMUM_OPTIMIZATION)
|
||||
.setSymbol(SimpleFastPointOverlayOptions.Shape.CIRCLE)
|
||||
.setPointStyle(style)
|
||||
.setRadius(6F * UiHelper.getDensityScalingFactor(context)) // radius is set in px - scaling factor makes that display density independent (= dp)
|
||||
.setIsClickable(true)
|
||||
.setCellSize(12) // Sets the grid cell size used for indexing, in pixels. Larger cells result in faster rendering speed, but worse fidelity. Default is 10 pixels, for large datasets (>10k points), use 15.
|
||||
var overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
|
||||
|
||||
overlay.setOnClickListener(object : SimpleFastPointOverlay.OnClickListener {
|
||||
override fun onClick(points: SimpleFastPointOverlay.PointAdapter?, point: Int?)
|
||||
{
|
||||
if (points == null || point == null || point == 0)
|
||||
{
|
||||
return
|
||||
}
|
||||
val trkpt = (points[point]) as Trkpt
|
||||
Log.i("VOUSSOIR", "Clicked ${trkpt.device_id} ${trkpt.time}")
|
||||
// trackpoints.remove(points[point])
|
||||
// map_view.overlays.remove(overlay)
|
||||
// overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
|
||||
// overlay.setOnClickListener(this)
|
||||
// map_view.overlays.add(overlay)
|
||||
// map_view.postInvalidate()
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
map_view.overlays.add(overlay)
|
||||
return overlay
|
||||
}
|
||||
|
||||
fun create_start_end_markers(context: Context, map_view: MapView, trkpts: Collection<Trkpt>): ItemizedIconOverlay<OverlayItem>?
|
||||
fun create_start_end_markers(context: Context, map_view: MapView, startpoint: Trkpt, endpoint: Trkpt): ItemizedIconOverlay<OverlayItem>?
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapOverlayHelper.create_start_end_markers")
|
||||
if (trkpts.size == 0)
|
||||
{
|
||||
return null
|
||||
}
|
||||
|
||||
val overlayItems: ArrayList<OverlayItem> = ArrayList<OverlayItem>()
|
||||
val startpoint = trkpts.first()
|
||||
val endpoint = trkpts.last()
|
||||
val startmarker: OverlayItem = createOverlayItem(context, startpoint.latitude, startpoint.longitude, startpoint.accuracy, startpoint.provider, startpoint.time)
|
||||
startmarker.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_start_blue_48dp)!!)
|
||||
overlayItems.add(startmarker)
|
||||
if (trkpts.size > 1)
|
||||
if (startpoint != endpoint)
|
||||
{
|
||||
val endmarker: OverlayItem = createOverlayItem(context, endpoint.latitude, endpoint.longitude, endpoint.accuracy, endpoint.provider, endpoint.time)
|
||||
endmarker.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_end_blue_48dp)!!)
|
||||
|
|
|
@ -12,19 +12,42 @@
|
|||
android:layout_height="match_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:src="@drawable/ic_delete_24dp"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/default_transparent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/map"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:srcCompat="@drawable/ic_delete_24dp" />
|
||||
|
||||
|
||||
<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"
|
||||
android:scaleX="0.6"
|
||||
android:scaleY="0.6"
|
||||
android:translationX="-45dp"
|
||||
android:translationY="-30dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:scaleX="0.5"
|
||||
android:scaleY="0.5"
|
||||
/>
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TimePicker
|
||||
android:id="@+id/track_query_start_time"
|
||||
|
@ -35,8 +58,8 @@
|
|||
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"
|
||||
android:scaleX="0.6"
|
||||
android:scaleY="0.6"
|
||||
/>
|
||||
|
||||
<DatePicker
|
||||
|
@ -49,8 +72,8 @@
|
|||
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"
|
||||
android:scaleX="0.6"
|
||||
android:scaleY="0.6"
|
||||
/>
|
||||
|
||||
<TimePicker
|
||||
|
@ -62,8 +85,8 @@
|
|||
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"
|
||||
android:scaleX="0.6"
|
||||
android:scaleY="0.6"
|
||||
/>
|
||||
|
||||
<org.osmdroid.views.MapView
|
||||
|
|
Loading…
Reference in a new issue