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()
|
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)
|
fun insert_trkpt(trkpt: Trkpt)
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "Database.insert_trkpt")
|
Log.i("VOUSSOIR", "Database.insert_trkpt")
|
||||||
|
@ -60,7 +68,7 @@ class Database(val trackbook: Trackbook)
|
||||||
put("device_id", trkpt.device_id)
|
put("device_id", trkpt.device_id)
|
||||||
put("lat", trkpt.latitude)
|
put("lat", trkpt.latitude)
|
||||||
put("lon", trkpt.longitude)
|
put("lon", trkpt.longitude)
|
||||||
put("time", GregorianCalendar.getInstance().time.time)
|
put("time", trkpt.time)
|
||||||
put("accuracy", trkpt.accuracy)
|
put("accuracy", trkpt.accuracy)
|
||||||
put("sat", trkpt.numberSatellites)
|
put("sat", trkpt.numberSatellites)
|
||||||
put("ele", trkpt.altitude)
|
put("ele", trkpt.altitude)
|
||||||
|
@ -108,8 +116,9 @@ class Database(val trackbook: Trackbook)
|
||||||
{
|
{
|
||||||
begin_transaction()
|
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 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 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.
|
// The pragmas don't seem to execute unless you call moveToNext.
|
||||||
var cursor: Cursor
|
var cursor: Cursor
|
||||||
cursor = this.connection.rawQuery("PRAGMA journal_mode = DELETE", null)
|
cursor = this.connection.rawQuery("PRAGMA journal_mode = DELETE", null)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.y20k.trackbook
|
package org.y20k.trackbook
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -109,4 +110,7 @@ object Keys {
|
||||||
// notification
|
// notification
|
||||||
const val TRACKER_SERVICE_NOTIFICATION_ID: Int = 1
|
const val TRACKER_SERVICE_NOTIFICATION_ID: Int = 1
|
||||||
const val NOTIFICATION_CHANNEL_RECORDING: String = "notificationChannelIdRecordingChannel"
|
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.ExtendedFloatingActionButton
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import com.google.android.material.snackbar.Snackbar
|
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.events.MapEventsReceiver
|
||||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||||
import org.osmdroid.util.GeoPoint
|
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.Overlay
|
||||||
import org.osmdroid.views.overlay.OverlayItem
|
import org.osmdroid.views.overlay.OverlayItem
|
||||||
import org.osmdroid.views.overlay.Polygon
|
import org.osmdroid.views.overlay.Polygon
|
||||||
|
import org.osmdroid.views.overlay.Polyline
|
||||||
import org.osmdroid.views.overlay.TilesOverlay
|
import org.osmdroid.views.overlay.TilesOverlay
|
||||||
import org.osmdroid.views.overlay.compass.CompassOverlay
|
import org.osmdroid.views.overlay.compass.CompassOverlay
|
||||||
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
|
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
|
||||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
|
||||||
import org.y20k.trackbook.helpers.*
|
import org.y20k.trackbook.helpers.*
|
||||||
|
|
||||||
/*
|
|
||||||
* MapFragment class
|
|
||||||
*/
|
|
||||||
class MapFragment : Fragment()
|
class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
/* Main class variables */
|
private lateinit var trackbook: Trackbook
|
||||||
|
|
||||||
private var bound: Boolean = false
|
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 trackingState: Int = Keys.STATE_TRACKING_STOPPED
|
||||||
private var gpsProviderActive: Boolean = false
|
private var gpsProviderActive: Boolean = false
|
||||||
private var networkProviderActive: Boolean = false
|
private var networkProviderActive: Boolean = false
|
||||||
private lateinit var currentBestLocation: Location
|
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
|
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_in_button: FloatingActionButton
|
||||||
lateinit var zoom_out_button: FloatingActionButton
|
lateinit var zoom_out_button: FloatingActionButton
|
||||||
lateinit var mainButton: ExtendedFloatingActionButton
|
lateinit var currentLocationButton: FloatingActionButton
|
||||||
private lateinit var mapView: MapView
|
private var current_track_overlay: Polyline? = null
|
||||||
private var current_position_overlays = ArrayList<Overlay>()
|
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 var homepoints_overlays = ArrayList<Overlay>()
|
||||||
private lateinit var database_changed_listener: DatabaseChangedListener
|
private lateinit var locationErrorBar: Snackbar
|
||||||
|
|
||||||
/* Overrides onCreate from Fragment */
|
/* Overrides onCreate from Fragment */
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "MapFragment.onCreate")
|
Log.i("VOUSSOIR", "MapFragment.onCreate")
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
thismapfragment = this
|
||||||
this.trackbook = (requireContext().applicationContext as Trackbook)
|
this.trackbook = (requireContext().applicationContext as Trackbook)
|
||||||
database_changed_listener = object: DatabaseChangedListener
|
database_changed_listener = object: DatabaseChangedListener
|
||||||
{
|
{
|
||||||
|
@ -99,7 +97,7 @@ class MapFragment : Fragment()
|
||||||
Log.i("VOUSSOIR", "MapFragment database_ready_changed to ${trackbook.database.ready}")
|
Log.i("VOUSSOIR", "MapFragment database_ready_changed to ${trackbook.database.ready}")
|
||||||
if (trackbook.database.ready)
|
if (trackbook.database.ready)
|
||||||
{
|
{
|
||||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
create_homepoint_overlays()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -116,7 +114,7 @@ class MapFragment : Fragment()
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "MapFragment.onCreateView")
|
Log.i("VOUSSOIR", "MapFragment.onCreateView")
|
||||||
// find views
|
|
||||||
rootView = inflater.inflate(R.layout.fragment_map, container, false)
|
rootView = inflater.inflate(R.layout.fragment_map, container, false)
|
||||||
mapView = rootView.findViewById(R.id.map)
|
mapView = rootView.findViewById(R.id.map)
|
||||||
currentLocationButton = rootView.findViewById(R.id.location_button)
|
currentLocationButton = rootView.findViewById(R.id.location_button)
|
||||||
|
@ -130,8 +128,6 @@ class MapFragment : Fragment()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
mapView.isLongClickable = true
|
mapView.isLongClickable = true
|
||||||
|
|
||||||
// basic map setup
|
|
||||||
mapView.isTilesScaledToDpi = true
|
mapView.isTilesScaledToDpi = true
|
||||||
mapView.isVerticalMapRepetitionEnabled = false
|
mapView.isVerticalMapRepetitionEnabled = false
|
||||||
mapView.setTileSource(TileSourceFactory.MAPNIK)
|
mapView.setTileSource(TileSourceFactory.MAPNIK)
|
||||||
|
@ -145,10 +141,8 @@ class MapFragment : Fragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
val densityScalingFactor: Float = UiHelper.getDensityScalingFactor(requireContext())
|
val densityScalingFactor: Float = UiHelper.getDensityScalingFactor(requireContext())
|
||||||
|
|
||||||
val compassOverlay = CompassOverlay(requireContext(), InternalCompassOrientationProvider(requireContext()), mapView)
|
val compassOverlay = CompassOverlay(requireContext(), InternalCompassOrientationProvider(requireContext()), mapView)
|
||||||
compassOverlay.enableCompass()
|
compassOverlay.enableCompass()
|
||||||
// compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / densityScalingFactor)) // TODO uncomment when transparent status bar is re-implemented
|
|
||||||
val screen_width = Resources.getSystem().displayMetrics.widthPixels
|
val screen_width = Resources.getSystem().displayMetrics.widthPixels
|
||||||
compassOverlay.setCompassCenter((screen_width / densityScalingFactor) - 36f, 36f)
|
compassOverlay.setCompassCenter((screen_width / densityScalingFactor) - 36f, 36f)
|
||||||
mapView.overlays.add(compassOverlay)
|
mapView.overlays.add(compassOverlay)
|
||||||
|
@ -187,7 +181,7 @@ class MapFragment : Fragment()
|
||||||
radius=radius,
|
radius=radius,
|
||||||
)
|
)
|
||||||
trackbook.load_homepoints()
|
trackbook.load_homepoints()
|
||||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
create_homepoint_overlays()
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,28 +192,34 @@ class MapFragment : Fragment()
|
||||||
mapView.overlays.add(MapEventsOverlay(receiver))
|
mapView.overlays.add(MapEventsOverlay(receiver))
|
||||||
|
|
||||||
trackbook.load_homepoints()
|
trackbook.load_homepoints()
|
||||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
create_homepoint_overlays()
|
||||||
if (database_changed_listener !in trackbook.database_changed_listeners)
|
if (database_changed_listener !in trackbook.database_changed_listeners)
|
||||||
{
|
{
|
||||||
trackbook.database_changed_listeners.add(database_changed_listener)
|
trackbook.database_changed_listeners.add(database_changed_listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create_current_position_overlays(currentBestLocation, trackingState)
|
||||||
|
|
||||||
centerMap(currentBestLocation)
|
centerMap(currentBestLocation)
|
||||||
|
|
||||||
// initialize track overlays
|
current_track_overlay = null
|
||||||
currentTrackOverlay = null
|
|
||||||
|
|
||||||
// initialize main button state
|
|
||||||
update_main_button()
|
|
||||||
|
|
||||||
mapView.setOnTouchListener { v, event ->
|
mapView.setOnTouchListener { v, event ->
|
||||||
continuous_auto_center = false
|
continuous_auto_center = false
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up buttons
|
update_main_button()
|
||||||
mainButton.setOnClickListener {
|
mainButton.setOnClickListener {
|
||||||
handleTrackingManagementMenu()
|
if (trackingState == Keys.STATE_TRACKING_ACTIVE)
|
||||||
|
{
|
||||||
|
trackerService.stopTracking()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
startTracking()
|
||||||
|
}
|
||||||
|
handler.post(location_update_redraw)
|
||||||
}
|
}
|
||||||
currentLocationButton.setOnClickListener {
|
currentLocationButton.setOnClickListener {
|
||||||
centerMap(currentBestLocation, animated=true)
|
centerMap(currentBestLocation, animated=true)
|
||||||
|
@ -238,6 +238,7 @@ class MapFragment : Fragment()
|
||||||
/* Overrides onStart from Fragment */
|
/* Overrides onStart from Fragment */
|
||||||
override fun onStart()
|
override fun onStart()
|
||||||
{
|
{
|
||||||
|
Log.i("VOUSSOIR", "MapFragment.onStart")
|
||||||
super.onStart()
|
super.onStart()
|
||||||
// request location permission if denied
|
// request location permission if denied
|
||||||
if (ContextCompat.checkSelfPermission(activity as Context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_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")
|
Log.i("VOUSSOIR", "MapFragment.onPause")
|
||||||
super.onPause()
|
super.onPause()
|
||||||
|
if (::trackerService.isInitialized)
|
||||||
|
{
|
||||||
|
trackerService.mapfragment = null
|
||||||
|
}
|
||||||
saveBestLocationState(currentBestLocation)
|
saveBestLocationState(currentBestLocation)
|
||||||
if (bound && trackingState != Keys.STATE_TRACKING_ACTIVE) {
|
if (bound && trackingState != Keys.STATE_TRACKING_ACTIVE) {
|
||||||
trackerService.removeGpsLocationListener()
|
trackerService.removeGpsLocationListener()
|
||||||
|
@ -278,6 +283,10 @@ class MapFragment : Fragment()
|
||||||
override fun onStop()
|
override fun onStop()
|
||||||
{
|
{
|
||||||
super.onStop()
|
super.onStop()
|
||||||
|
if (::trackerService.isInitialized)
|
||||||
|
{
|
||||||
|
trackerService.mapfragment = null
|
||||||
|
}
|
||||||
// unbind from TrackerService
|
// unbind from TrackerService
|
||||||
if (bound)
|
if (bound)
|
||||||
{
|
{
|
||||||
|
@ -290,6 +299,7 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "MapFragment.onDestroy")
|
Log.i("VOUSSOIR", "MapFragment.onDestroy")
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
trackerService.mapfragment = null
|
||||||
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
|
||||||
if (database_changed_listener in trackbook.database_changed_listeners)
|
if (database_changed_listener in trackbook.database_changed_listeners)
|
||||||
|
@ -314,14 +324,17 @@ class MapFragment : Fragment()
|
||||||
toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start recording waypoints */
|
private fun startTracking()
|
||||||
private fun startTracking() {
|
{
|
||||||
// start service via intent so that it keeps running after unbind
|
// start service via intent so that it keeps running after unbind
|
||||||
val intent = Intent(activity, TrackerService::class.java)
|
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
|
// ... start service in foreground to prevent it being killed on Oreo
|
||||||
activity?.startForegroundService(intent)
|
activity?.startForegroundService(intent)
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
activity?.startService(intent)
|
activity?.startService(intent)
|
||||||
}
|
}
|
||||||
trackerService.startTracking()
|
trackerService.startTracking()
|
||||||
|
@ -334,16 +347,9 @@ class MapFragment : Fragment()
|
||||||
bound = false
|
bound = false
|
||||||
// unregister listener for changes in shared preferences
|
// unregister listener for changes in shared preferences
|
||||||
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
// stop receiving location updates
|
if (::trackerService.isInitialized)
|
||||||
handler.removeCallbacks(periodicLocationRequestRunnable)
|
{
|
||||||
}
|
trackerService.mapfragment = null
|
||||||
|
|
||||||
/* 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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +380,8 @@ class MapFragment : Fragment()
|
||||||
continuous_auto_center = true
|
continuous_auto_center = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveBestLocationState(currentBestLocation: Location) {
|
fun saveBestLocationState(currentBestLocation: Location)
|
||||||
|
{
|
||||||
PreferencesHelper.saveCurrentBestLocation(currentBestLocation)
|
PreferencesHelper.saveCurrentBestLocation(currentBestLocation)
|
||||||
PreferencesHelper.saveZoomLevel(mapView.zoomLevelDouble)
|
PreferencesHelper.saveZoomLevel(mapView.zoomLevelDouble)
|
||||||
continuous_auto_center = true
|
continuous_auto_center = true
|
||||||
|
@ -395,38 +402,26 @@ class MapFragment : Fragment()
|
||||||
/* Mark current position on map */
|
/* Mark current position on map */
|
||||||
fun create_current_position_overlays(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED)
|
fun create_current_position_overlays(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED)
|
||||||
{
|
{
|
||||||
// Log.i("VOUSSOIR", "MapFragmentLayoutHolder.markCurrentPosition")
|
|
||||||
|
|
||||||
clear_current_position_overlays()
|
clear_current_position_overlays()
|
||||||
|
|
||||||
val locationIsOld: Boolean = !(isRecentEnough(location))
|
val locationIsOld: Boolean = !(isRecentEnough(location))
|
||||||
|
|
||||||
// create marker
|
|
||||||
val newMarker: Drawable
|
val newMarker: Drawable
|
||||||
val fillcolor: Int
|
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)
|
fillcolor = Color.argb(64, 220, 61, 51)
|
||||||
if (locationIsOld)
|
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_red_24dp)!!
|
||||||
{
|
|
||||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_black_24dp)!!
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_red_24dp)!!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fillcolor = Color.argb(64, 60, 152, 219)
|
fillcolor = Color.argb(64, 60, 152, 219)
|
||||||
if(locationIsOld)
|
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_blue_24dp)!!
|
||||||
{
|
|
||||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_black_24dp)!!
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_blue_24dp)!!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val current_location_radius = Polygon()
|
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()
|
fun clear_homepoint_overlays()
|
||||||
{
|
{
|
||||||
for (ov in homepoints_overlays)
|
for (ov in homepoints_overlays)
|
||||||
|
@ -459,15 +469,16 @@ class MapFragment : Fragment()
|
||||||
homepoints_overlays.clear()
|
homepoints_overlays.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create_homepoint_overlays(context: Context, map_view: MapView, homepoints: List<Homepoint>)
|
fun create_homepoint_overlays()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.createHomepointOverlays")
|
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.createHomepointOverlays")
|
||||||
|
|
||||||
|
val context = requireContext()
|
||||||
val newMarker: Drawable = ContextCompat.getDrawable(context, R.drawable.ic_homepoint_24dp)!!
|
val newMarker: Drawable = ContextCompat.getDrawable(context, R.drawable.ic_homepoint_24dp)!!
|
||||||
|
|
||||||
clear_homepoint_overlays()
|
clear_homepoint_overlays()
|
||||||
|
|
||||||
for (homepoint in homepoints)
|
for (homepoint in trackbook.homepoints)
|
||||||
{
|
{
|
||||||
val p = Polygon()
|
val p = Polygon()
|
||||||
p.points = Polygon.pointsAsCircle(GeoPoint(homepoint.location.latitude, homepoint.location.longitude), homepoint.location.accuracy.toDouble())
|
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)
|
homepoints_overlays.add(p)
|
||||||
|
|
||||||
val overlayItems: java.util.ArrayList<OverlayItem> = java.util.ArrayList<OverlayItem>()
|
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)
|
overlayItem.setMarker(newMarker)
|
||||||
overlayItems.add(overlayItem)
|
overlayItems.add(overlayItem)
|
||||||
val homepoint_overlay = ItemizedIconOverlay<OverlayItem>(context, overlayItems,
|
val homepoint_overlay = ItemizedIconOverlay<OverlayItem>(context, overlayItems,
|
||||||
|
@ -504,14 +522,14 @@ class MapFragment : Fragment()
|
||||||
delete_button.setOnClickListener {
|
delete_button.setOnClickListener {
|
||||||
trackbook.database.delete_homepoint(homepoint.id)
|
trackbook.database.delete_homepoint(homepoint.id)
|
||||||
trackbook.load_homepoints()
|
trackbook.load_homepoints()
|
||||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
create_homepoint_overlays()
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}
|
||||||
save_button.setOnClickListener {
|
save_button.setOnClickListener {
|
||||||
val radius = radius_input.text.toString().toDoubleOrNull() ?: 25.0
|
val radius = radius_input.text.toString().toDoubleOrNull() ?: 25.0
|
||||||
trackbook.database.update_homepoint(homepoint.id, name=name_input.text.toString(), radius=radius)
|
trackbook.database.update_homepoint(homepoint.id, name=name_input.text.toString(), radius=radius)
|
||||||
trackbook.load_homepoints()
|
trackbook.load_homepoints()
|
||||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
create_homepoint_overlays()
|
||||||
dialog.dismiss()
|
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()
|
fun update_main_button()
|
||||||
{
|
{
|
||||||
mainButton.isEnabled = trackbook.database.ready
|
mainButton.isEnabled = trackbook.database.ready
|
||||||
|
@ -592,14 +597,13 @@ class MapFragment : Fragment()
|
||||||
// get reference to tracker service
|
// get reference to tracker service
|
||||||
val binder = service as TrackerService.LocalBinder
|
val binder = service as TrackerService.LocalBinder
|
||||||
trackerService = binder.service
|
trackerService = binder.service
|
||||||
|
trackerService.mapfragment = thismapfragment
|
||||||
// get state of tracking and update button if necessary
|
// get state of tracking and update button if necessary
|
||||||
trackingState = trackerService.trackingState
|
trackingState = trackerService.trackingState
|
||||||
update_main_button()
|
update_main_button()
|
||||||
// register listener for changes in shared preferences
|
// register listener for changes in shared preferences
|
||||||
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
// start listening for location updates
|
// start listening for location updates
|
||||||
handler.removeCallbacks(periodicLocationRequestRunnable)
|
|
||||||
handler.postDelayed(periodicLocationRequestRunnable, 0)
|
|
||||||
}
|
}
|
||||||
override fun onServiceDisconnected(arg0: ComponentName)
|
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()
|
override fun run()
|
||||||
{
|
{
|
||||||
|
Log.i("VOUSSOIR", "MapFragment.location_update_redraw")
|
||||||
currentBestLocation = trackerService.currentBestLocation
|
currentBestLocation = trackerService.currentBestLocation
|
||||||
gpsProviderActive = trackerService.gpsProviderActive
|
gpsProviderActive = trackerService.gpsProviderActive
|
||||||
networkProviderActive = trackerService.networkProviderActive
|
networkProviderActive = trackerService.networkProviderActive
|
||||||
trackingState = trackerService.trackingState
|
trackingState = trackerService.trackingState
|
||||||
// update location and track
|
|
||||||
create_current_position_overlays(currentBestLocation, trackingState)
|
create_current_position_overlays(currentBestLocation, trackingState)
|
||||||
create_current_track_overlay(trackerService.recent_trackpoints_for_mapview, trackingState)
|
if (current_track_overlay == null)
|
||||||
// center map, if it had not been dragged/zoomed before
|
{
|
||||||
|
create_track_overlay()
|
||||||
|
}
|
||||||
|
current_track_overlay!!.setPoints(trackerService.recent_trackpoints_for_mapview)
|
||||||
|
|
||||||
if (continuous_auto_center)
|
if (continuous_auto_center)
|
||||||
{
|
{
|
||||||
centerMap(currentBestLocation, animated=false)
|
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.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
|
import android.database.DatabaseUtils.dumpCursorToString
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import org.y20k.trackbook.helpers.iso8601_format
|
import org.y20k.trackbook.helpers.iso8601
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ data class Track (
|
||||||
var start_time: Date,
|
var start_time: Date,
|
||||||
var end_time: Date,
|
var end_time: Date,
|
||||||
var name: String = "",
|
var name: String = "",
|
||||||
val trkpts: ArrayDeque<Trkpt> = ArrayDeque<Trkpt>(),
|
val trkpts: ArrayList<Trkpt> = ArrayList<Trkpt>(),
|
||||||
var view_latitude: Double = Keys.DEFAULT_LATITUDE,
|
var view_latitude: Double = Keys.DEFAULT_LATITUDE,
|
||||||
var view_longitude: Double = Keys.DEFAULT_LONGITUDE,
|
var view_longitude: Double = Keys.DEFAULT_LONGITUDE,
|
||||||
var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMAT_VERSION,
|
var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMAT_VERSION,
|
||||||
|
@ -73,7 +74,7 @@ data class Track (
|
||||||
>
|
>
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
write("\t<metadata>")
|
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\t<device>${this.device_id}</device>")
|
||||||
write("\t</metadata>")
|
write("\t</metadata>")
|
||||||
|
|
||||||
|
@ -81,7 +82,6 @@ data class Track (
|
||||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
|
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
|
||||||
dateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
dateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
write("\t<trk>")
|
write("\t<trk>")
|
||||||
write("\t\t<name>${this.name}</name>")
|
|
||||||
write("\t\t<trkseg>")
|
write("\t\t<trkseg>")
|
||||||
|
|
||||||
var previous: Trkpt? = null
|
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<trkpt lat=\"${trkpt.latitude}\" lon=\"${trkpt.longitude}\">")
|
||||||
write("\t\t\t\t<ele>${trkpt.altitude}</ele>")
|
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\t<sat>${trkpt.numberSatellites}</sat>")
|
||||||
write("\t\t\t</trkpt>")
|
write("\t\t\t</trkpt>")
|
||||||
previous = trkpt
|
previous = trkpt
|
||||||
|
@ -170,11 +171,11 @@ data class Track (
|
||||||
|
|
||||||
fun trkpt_generator() = iterator<Trkpt>
|
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",
|
"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())
|
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_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")
|
||||||
|
|
|
@ -20,6 +20,7 @@ import YesNoDialog
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Paint
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
@ -48,32 +49,38 @@ 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.Polyline
|
||||||
import org.osmdroid.views.overlay.OverlayItem
|
|
||||||
import org.osmdroid.views.overlay.TilesOverlay
|
import org.osmdroid.views.overlay.TilesOverlay
|
||||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
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.AppThemeHelper
|
||||||
import org.y20k.trackbook.helpers.DateTimeHelper
|
import org.y20k.trackbook.helpers.DateTimeHelper
|
||||||
import org.y20k.trackbook.helpers.LengthUnitHelper
|
import org.y20k.trackbook.helpers.LengthUnitHelper
|
||||||
import org.y20k.trackbook.helpers.PreferencesHelper
|
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.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.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
{
|
{
|
||||||
|
private lateinit var trackbook: Trackbook
|
||||||
|
|
||||||
lateinit var rootView: View
|
lateinit var rootView: View
|
||||||
lateinit var save_track_button: ImageButton
|
lateinit var save_track_button: ImageButton
|
||||||
lateinit var deleteButton: ImageButton
|
lateinit var deleteButton: ImageButton
|
||||||
lateinit var zoom_in_button: FloatingActionButton
|
lateinit var zoom_in_button: FloatingActionButton
|
||||||
lateinit var zoom_out_button: FloatingActionButton
|
lateinit var zoom_out_button: FloatingActionButton
|
||||||
lateinit var trackNameView: MaterialTextView
|
lateinit var trackNameView: MaterialTextView
|
||||||
|
lateinit var selected_trkpt_info: MaterialTextView
|
||||||
lateinit var track_query_start_date: DatePicker
|
lateinit var track_query_start_date: DatePicker
|
||||||
lateinit var track_query_start_time: TimePicker
|
lateinit var track_query_start_time: TimePicker
|
||||||
lateinit var track_query_end_date: DatePicker
|
lateinit var track_query_end_date: DatePicker
|
||||||
lateinit var track_query_end_time: TimePicker
|
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_start_time_previous: Int = 0
|
||||||
var track_query_end_time_previous: Int = 0
|
var track_query_end_time_previous: Int = 0
|
||||||
private lateinit var mapView: MapView
|
private lateinit var mapView: MapView
|
||||||
|
@ -95,22 +102,25 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
private lateinit var negativeElevationView: MaterialTextView
|
private lateinit var negativeElevationView: MaterialTextView
|
||||||
private lateinit var elevationDataViews: Group
|
private lateinit var elevationDataViews: Group
|
||||||
private lateinit var track: Track
|
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 var useImperialUnits: Boolean = false
|
||||||
private val handler: Handler = Handler(Looper.getMainLooper())
|
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
|
val RERENDER_DELAY: Long = 1000
|
||||||
|
|
||||||
/* Overrides onCreateView from Fragment */
|
/* Overrides onCreateView from Fragment */
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
||||||
{
|
{
|
||||||
|
this.trackbook = (requireContext().applicationContext as Trackbook)
|
||||||
val database: Database = (requireActivity().applicationContext as Trackbook).database
|
val database: Database = (requireActivity().applicationContext as Trackbook).database
|
||||||
track = Track(
|
track = Track(
|
||||||
database=database,
|
database=database,
|
||||||
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_parse(this.requireArguments().getString(Keys.ARG_TRACK_START_TIME)!!),
|
||||||
end_time=iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_STOP_TIME)!!),
|
end_time=iso8601_parse(this.requireArguments().getString(Keys.ARG_TRACK_STOP_TIME)!!),
|
||||||
)
|
)
|
||||||
track.load_trkpts()
|
track.load_trkpts()
|
||||||
rootView = inflater.inflate(R.layout.fragment_track, container, false)
|
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.setCenter(GeoPoint(track.view_latitude, track.view_longitude))
|
||||||
controller.setZoom(Keys.DEFAULT_ZOOM_LEVEL)
|
controller.setZoom(Keys.DEFAULT_ZOOM_LEVEL)
|
||||||
|
|
||||||
|
// trkpt_infowindow = MarkerInfoWindow(R.layout.trkpt_infowindow, mapView)
|
||||||
|
|
||||||
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)
|
||||||
|
@ -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 {
|
save_track_button.setOnClickListener {
|
||||||
openSaveGpxDialog()
|
openSaveGpxDialog()
|
||||||
}
|
}
|
||||||
|
@ -251,6 +284,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
statisticsSheetBehavior = BottomSheetBehavior.from<View>(statisticsSheet)
|
statisticsSheetBehavior = BottomSheetBehavior.from<View>(statisticsSheet)
|
||||||
statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||||
|
|
||||||
|
track_segment_overlays = ArrayDeque<Polyline>(10)
|
||||||
render_track()
|
render_track()
|
||||||
|
|
||||||
return rootView
|
return rootView
|
||||||
|
@ -265,25 +299,81 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
fun render_track()
|
fun render_track()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "TrackFragment.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)
|
Log.i("VOUSSOIR", "MapOverlayHelper.createTrackOverlay")
|
||||||
{
|
track_geopoints = mutableListOf()
|
||||||
mapView.overlays.remove(track_overlay)
|
|
||||||
}
|
|
||||||
val geopoints: MutableList<IGeoPoint> = mutableListOf()
|
|
||||||
for (trkpt in track.trkpts)
|
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)
|
if (previous_time > 0 && (trkpt.time - previous_time) > Keys.STOP_OVER_THRESHOLD)
|
||||||
special_points_overlay = create_start_end_markers(requireContext(), mapView, track.trkpts)
|
{
|
||||||
|
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
|
fun get_datetime(datepicker: DatePicker, timepicker: TimePicker, seconds: Int): Date
|
||||||
|
@ -348,7 +438,16 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
/* Overrides onZoom from MapListener */
|
/* Overrides onZoom from MapListener */
|
||||||
override fun onZoom(event: ZoomEvent?): Boolean
|
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 */
|
/* Overrides onScroll from MapListener */
|
||||||
|
|
|
@ -30,9 +30,7 @@ import android.Manifest
|
||||||
import android.os.*
|
import android.os.*
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import org.osmdroid.api.IGeoPoint
|
|
||||||
import org.osmdroid.util.GeoPoint
|
import org.osmdroid.util.GeoPoint
|
||||||
import org.osmdroid.views.overlay.simplefastpoint.LabelledGeoPoint
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import org.y20k.trackbook.helpers.*
|
import org.y20k.trackbook.helpers.*
|
||||||
|
|
||||||
|
@ -55,8 +53,8 @@ class TrackerService: Service()
|
||||||
var location_min_time_ms: Long = 0
|
var location_min_time_ms: Long = 0
|
||||||
private val RECENT_TRKPT_COUNT = 7200
|
private val RECENT_TRKPT_COUNT = 7200
|
||||||
lateinit var recent_trkpts: Deque<Trkpt>
|
lateinit var recent_trkpts: Deque<Trkpt>
|
||||||
lateinit var recent_displacement_trkpts: Deque<Trkpt>
|
lateinit var recent_displacement_locations: Deque<Location>
|
||||||
var recent_trackpoints_for_mapview: MutableList<IGeoPoint> = mutableListOf()
|
var recent_trackpoints_for_mapview: MutableList<GeoPoint> = mutableListOf()
|
||||||
var gpsLocationListenerRegistered: Boolean = false
|
var gpsLocationListenerRegistered: Boolean = false
|
||||||
var networkLocationListenerRegistered: Boolean = false
|
var networkLocationListenerRegistered: Boolean = false
|
||||||
var bound: Boolean = false
|
var bound: Boolean = false
|
||||||
|
@ -67,6 +65,7 @@ class TrackerService: Service()
|
||||||
private lateinit var notificationHelper: NotificationHelper
|
private lateinit var notificationHelper: NotificationHelper
|
||||||
private lateinit var gpsLocationListener: LocationListener
|
private lateinit var gpsLocationListener: LocationListener
|
||||||
private lateinit var networkLocationListener: LocationListener
|
private lateinit var networkLocationListener: LocationListener
|
||||||
|
var mapfragment: MapFragment? = null
|
||||||
|
|
||||||
private fun addGpsLocationListener()
|
private fun addGpsLocationListener()
|
||||||
{
|
{
|
||||||
|
@ -160,6 +159,11 @@ class TrackerService: Service()
|
||||||
|
|
||||||
currentBestLocation = location
|
currentBestLocation = location
|
||||||
|
|
||||||
|
if (mapfragment != null)
|
||||||
|
{
|
||||||
|
mapfragment!!.handler.postDelayed(mapfragment!!.location_update_redraw, 0)
|
||||||
|
}
|
||||||
|
|
||||||
if (trackingState != Keys.STATE_TRACKING_ACTIVE)
|
if (trackingState != Keys.STATE_TRACKING_ACTIVE)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
|
@ -195,7 +199,7 @@ class TrackerService: Service()
|
||||||
return
|
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.")
|
Log.i("VOUSSOIR", "Omitting due to too close to previous.")
|
||||||
return
|
return
|
||||||
|
@ -215,10 +219,10 @@ class TrackerService: Service()
|
||||||
recent_trackpoints_for_mapview.removeFirst()
|
recent_trackpoints_for_mapview.removeFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
recent_displacement_trkpts.add(trkpt)
|
recent_displacement_locations.add(location)
|
||||||
while (recent_displacement_trkpts.size > 5)
|
while (recent_displacement_locations.size > 5)
|
||||||
{
|
{
|
||||||
recent_displacement_trkpts.removeFirst()
|
recent_displacement_locations.removeFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location.time - lastCommit > Keys.COMMIT_INTERVAL)
|
if (location.time - lastCommit > Keys.COMMIT_INTERVAL)
|
||||||
|
@ -275,7 +279,7 @@ class TrackerService: Service()
|
||||||
trackbook = (applicationContext as Trackbook)
|
trackbook = (applicationContext as Trackbook)
|
||||||
trackbook.load_homepoints()
|
trackbook.load_homepoints()
|
||||||
recent_trkpts = ArrayDeque<Trkpt>(RECENT_TRKPT_COUNT)
|
recent_trkpts = ArrayDeque<Trkpt>(RECENT_TRKPT_COUNT)
|
||||||
recent_displacement_trkpts = ArrayDeque<Trkpt>(5)
|
recent_displacement_locations = ArrayDeque<Location>(5)
|
||||||
recent_trackpoints_for_mapview = mutableListOf()
|
recent_trackpoints_for_mapview = mutableListOf()
|
||||||
use_gps_location = PreferencesHelper.load_location_gps()
|
use_gps_location = PreferencesHelper.load_location_gps()
|
||||||
use_network_location = PreferencesHelper.load_location_network()
|
use_network_location = PreferencesHelper.load_location_network()
|
||||||
|
@ -392,7 +396,7 @@ class TrackerService: Service()
|
||||||
addNetworkLocationListener()
|
addNetworkLocationListener()
|
||||||
trackingState = Keys.STATE_TRACKING_ACTIVE
|
trackingState = Keys.STATE_TRACKING_ACTIVE
|
||||||
PreferencesHelper.saveTrackingState(trackingState)
|
PreferencesHelper.saveTrackingState(trackingState)
|
||||||
recent_displacement_trkpts.clear()
|
recent_displacement_locations.clear()
|
||||||
startForeground(Keys.TRACKER_SERVICE_NOTIFICATION_ID, displayNotification())
|
startForeground(Keys.TRACKER_SERVICE_NOTIFICATION_ID, displayNotification())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,7 +406,7 @@ class TrackerService: Service()
|
||||||
|
|
||||||
trackingState = Keys.STATE_TRACKING_STOPPED
|
trackingState = Keys.STATE_TRACKING_STOPPED
|
||||||
PreferencesHelper.saveTrackingState(trackingState)
|
PreferencesHelper.saveTrackingState(trackingState)
|
||||||
recent_displacement_trkpts.clear()
|
recent_displacement_locations.clear()
|
||||||
displayNotification()
|
displayNotification()
|
||||||
stopForeground(STOP_FOREGROUND_DETACH)
|
stopForeground(STOP_FOREGROUND_DETACH)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import org.y20k.trackbook.helpers.UiHelper
|
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
|
import org.y20k.trackbook.tracklist.TracklistAdapter
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -46,7 +46,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
private lateinit var trackElementList: RecyclerView
|
private lateinit var trackElementList: RecyclerView
|
||||||
private lateinit var tracklistOnboarding: ConstraintLayout
|
private lateinit var tracklistOnboarding: ConstraintLayout
|
||||||
|
|
||||||
/* Overrides onCreateView from Fragment */
|
/* Overrides onCreate from Fragment */
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -67,18 +67,6 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
trackElementList.itemAnimator = DefaultItemAnimator()
|
trackElementList.itemAnimator = DefaultItemAnimator()
|
||||||
trackElementList.adapter = tracklistAdapter
|
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
|
// toggle onboarding layout
|
||||||
toggleOnboardingLayout()
|
toggleOnboardingLayout()
|
||||||
|
|
||||||
|
@ -90,40 +78,15 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
val bundle: Bundle = bundleOf(
|
val bundle: Bundle = bundleOf(
|
||||||
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(track.start_time),
|
||||||
Keys.ARG_TRACK_STOP_TIME to iso8601_format.format(track.end_time),
|
Keys.ARG_TRACK_STOP_TIME to iso8601(track.end_time),
|
||||||
)
|
)
|
||||||
findNavController().navigate(R.id.fragment_track, bundle)
|
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
|
// toggle onboarding layout
|
||||||
private fun toggleOnboardingLayout() {
|
private fun toggleOnboardingLayout()
|
||||||
|
{
|
||||||
when (tracklistAdapter.isEmpty()) {
|
when (tracklistAdapter.isEmpty()) {
|
||||||
true -> {
|
true -> {
|
||||||
// show onboarding layout
|
// show onboarding layout
|
||||||
|
@ -143,11 +106,13 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
*/
|
*/
|
||||||
inner class CustomLinearLayoutManager(context: Context): LinearLayoutManager(context, VERTICAL, false)
|
inner class CustomLinearLayoutManager(context: Context): LinearLayoutManager(context, VERTICAL, false)
|
||||||
{
|
{
|
||||||
override fun supportsPredictiveItemAnimations(): Boolean {
|
override fun supportsPredictiveItemAnimations(): Boolean
|
||||||
|
{
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLayoutCompleted(state: RecyclerView.State?) {
|
override fun onLayoutCompleted(state: RecyclerView.State?)
|
||||||
|
{
|
||||||
super.onLayoutCompleted(state)
|
super.onLayoutCompleted(state)
|
||||||
// handle delete request from TrackFragment - after layout calculations are complete
|
// handle delete request from TrackFragment - after layout calculations are complete
|
||||||
val deleteTrackId: Long = arguments?.getLong(Keys.ARG_TRACK_ID, -1L) ?: -1L
|
val deleteTrackId: Long = arguments?.getLong(Keys.ARG_TRACK_ID, -1L) ?: -1L
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.y20k.trackbook
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import org.osmdroid.api.IGeoPoint
|
import org.osmdroid.api.IGeoPoint
|
||||||
import org.osmdroid.util.GeoPoint
|
import org.osmdroid.util.GeoPoint
|
||||||
|
import org.osmdroid.views.overlay.Polyline
|
||||||
import org.y20k.trackbook.helpers.getNumberOfSatellites
|
import org.y20k.trackbook.helpers.getNumberOfSatellites
|
||||||
|
|
||||||
class Trkpt(
|
class Trkpt(
|
||||||
|
@ -30,6 +31,7 @@ class Trkpt(
|
||||||
val accuracy: Float,
|
val accuracy: Float,
|
||||||
val time: Long,
|
val time: Long,
|
||||||
val numberSatellites: Int = 0,
|
val numberSatellites: Int = 0,
|
||||||
|
var rendered_by_polyline: Polyline? = null
|
||||||
) : GeoPoint(latitude, longitude, altitude)
|
) : GeoPoint(latitude, longitude, altitude)
|
||||||
{
|
{
|
||||||
constructor(device_id: String, location: Location) : this(
|
constructor(device_id: String, location: Location) : this(
|
||||||
|
|
|
@ -15,12 +15,25 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.random.Random.Default.nextBits
|
import kotlin.random.Random.Default.nextBits
|
||||||
|
|
||||||
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US)
|
|
||||||
private val RNG = SecureRandom()
|
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
|
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
|
fun random_int(): Int
|
||||||
|
|
|
@ -83,15 +83,4 @@ object DateTimeHelper {
|
||||||
fun convertToReadableDateAndTime(date: Date, dateStyle: Int = DateFormat.SHORT, timeStyle: Int = DateFormat.SHORT): String {
|
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)}"
|
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
|
package org.y20k.trackbook.helpers
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Paint
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import org.osmdroid.api.IGeoPoint
|
|
||||||
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.ItemizedIconOverlay
|
||||||
import org.osmdroid.views.overlay.OverlayItem
|
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.R
|
||||||
import org.y20k.trackbook.Trkpt
|
import org.y20k.trackbook.Trkpt
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun createTrackOverlay(context: Context, map_view: MapView, geopoints: MutableList<IGeoPoint>, trackingState: Int): SimpleFastPointOverlay
|
fun create_start_end_markers(context: Context, map_view: MapView, startpoint: Trkpt, endpoint: Trkpt): ItemizedIconOverlay<OverlayItem>?
|
||||||
{
|
|
||||||
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>?
|
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "MapOverlayHelper.create_start_end_markers")
|
Log.i("VOUSSOIR", "MapOverlayHelper.create_start_end_markers")
|
||||||
if (trkpts.size == 0)
|
|
||||||
{
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val overlayItems: ArrayList<OverlayItem> = ArrayList<OverlayItem>()
|
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)
|
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)!!)
|
startmarker.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_start_blue_48dp)!!)
|
||||||
overlayItems.add(startmarker)
|
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)
|
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)!!)
|
endmarker.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_end_blue_48dp)!!)
|
||||||
|
|
|
@ -12,19 +12,42 @@
|
||||||
android:layout_height="match_parent"
|
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
|
<DatePicker
|
||||||
android:id="@+id/track_query_start_date"
|
android:id="@+id/track_query_start_date"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="130dp"
|
android:layout_height="130dp"
|
||||||
android:translationX="-45dp"
|
|
||||||
android:translationY="-30dp"
|
|
||||||
android:calendarViewShown="false"
|
android:calendarViewShown="false"
|
||||||
android:datePickerMode="spinner"
|
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"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:scaleX="0.5"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:scaleY="0.5"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TimePicker
|
<TimePicker
|
||||||
android:id="@+id/track_query_start_time"
|
android:id="@+id/track_query_start_time"
|
||||||
|
@ -35,8 +58,8 @@
|
||||||
android:timePickerMode="spinner"
|
android:timePickerMode="spinner"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/track_query_start_date"
|
app:layout_constraintStart_toEndOf="@+id/track_query_start_date"
|
||||||
android:scaleX="0.5"
|
android:scaleX="0.6"
|
||||||
android:scaleY="0.5"
|
android:scaleY="0.6"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DatePicker
|
<DatePicker
|
||||||
|
@ -49,8 +72,8 @@
|
||||||
android:calendarViewShown="false"
|
android:calendarViewShown="false"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/track_query_start_date"
|
app:layout_constraintTop_toBottomOf="@+id/track_query_start_date"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:scaleX="0.5"
|
android:scaleX="0.6"
|
||||||
android:scaleY="0.5"
|
android:scaleY="0.6"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TimePicker
|
<TimePicker
|
||||||
|
@ -62,8 +85,8 @@
|
||||||
android:timePickerMode="spinner"
|
android:timePickerMode="spinner"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/track_query_start_time"
|
app:layout_constraintTop_toBottomOf="@+id/track_query_start_time"
|
||||||
app:layout_constraintStart_toEndOf="@+id/track_query_start_date"
|
app:layout_constraintStart_toEndOf="@+id/track_query_start_date"
|
||||||
android:scaleX="0.5"
|
android:scaleX="0.6"
|
||||||
android:scaleY="0.5"
|
android:scaleY="0.6"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<org.osmdroid.views.MapView
|
<org.osmdroid.views.MapView
|
||||||
|
|
Loading…
Reference in a new issue