Compare commits
10 commits
a27b8713a9
...
29a4a1dae3
| Author | SHA1 | Date | |
|---|---|---|---|
| 29a4a1dae3 | |||
| 31c77528d7 | |||
| 535133ce22 | |||
| a04a0d3a3a | |||
| eb1ad45c4c | |||
| 47c338f38f | |||
| cd25415e9b | |||
| a75ee66ca4 | |||
| 70e6e64918 | |||
| d5df922d3d |
7 changed files with 190 additions and 98 deletions
|
|
@ -10,8 +10,8 @@ android {
|
||||||
applicationId 'net.voussoir.trkpt'
|
applicationId 'net.voussoir.trkpt'
|
||||||
minSdkVersion 25
|
minSdkVersion 25
|
||||||
targetSdk 32
|
targetSdk 32
|
||||||
versionCode 56
|
versionCode 58
|
||||||
versionName '1.2.0'
|
versionName '1.3.0'
|
||||||
resConfigs "en", "da", "de", "fr", "hr", "id", "it", "ja", "nb-rNO", "nl", "pl", "pt-rBR", "ru", "sv", "tr", "zh-rCN"
|
resConfigs "en", "da", "de", "fr", "hr", "id", "it", "ja", "nb-rNO", "nl", "pl", "pt-rBR", "ru", "sv", "tr", "zh-rCN"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,11 @@ class Database(val trackbook: Trackbook)
|
||||||
this.connection.endTransaction()
|
this.connection.endTransaction()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delete_trkpt(device_id: String, time: Long)
|
fun delete_trkpt(trkpt: Trkpt)
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "Database.delete_trkpt")
|
Log.i("VOUSSOIR", "Database.delete_trkpt")
|
||||||
begin_transaction()
|
begin_transaction()
|
||||||
connection.delete("trkpt", "device_id = ? AND time = ?", arrayOf(device_id, time.toString()))
|
connection.delete("trkpt", "device_id = ? AND time = ?", arrayOf(trkpt.device_id, trkpt.time.toString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delete_trkpt_start_end(device_id: String, start_time: Long, end_time: Long)
|
fun delete_trkpt_start_end(device_id: String, start_time: Long, end_time: Long)
|
||||||
|
|
@ -101,18 +101,33 @@ class Database(val trackbook: Trackbook)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun select_trkpt_bounding_box(device_id: String, north: Double, south: Double, east: Double, west: Double, max_accuracy: Float=Keys.DEFAULT_MAX_ACCURACY): Iterator<Trkpt>
|
fun select_trkpt_bounding_box(device_id: String?, north: Double, south: Double, east: Double, west: Double, max_accuracy: Float=Keys.DEFAULT_MAX_ACCURACY): Iterator<Trkpt>
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "Track.trkpt_generator: Querying points between $north, $south, $east, $west.")
|
Log.i("VOUSSOIR", "Track.trkpt_generator: Querying points between $north, $south, $east, $west.")
|
||||||
return _trkpt_generator(this.connection.rawQuery(
|
if (device_id == null)
|
||||||
"""
|
{
|
||||||
|
return _trkpt_generator(this.connection.rawQuery(
|
||||||
|
"""
|
||||||
|
SELECT device_id, lat, lon, time, provider, ele, accuracy, sat
|
||||||
|
FROM trkpt
|
||||||
|
WHERE lat >= ? AND lat <= ? AND lon >= ? AND lon <= ? AND accuracy <= ?
|
||||||
|
ORDER BY time ASC
|
||||||
|
""",
|
||||||
|
arrayOf(south.toString(), north.toString(), west.toString(), east.toString(), max_accuracy.toString())
|
||||||
|
))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _trkpt_generator(this.connection.rawQuery(
|
||||||
|
"""
|
||||||
SELECT device_id, lat, lon, time, provider, ele, accuracy, sat
|
SELECT device_id, lat, lon, time, provider, ele, accuracy, sat
|
||||||
FROM trkpt
|
FROM trkpt
|
||||||
WHERE device_id = ? AND lat >= ? AND lat <= ? AND lon >= ? AND lon <= ? AND accuracy <= ?
|
WHERE device_id = ? AND lat >= ? AND lat <= ? AND lon >= ? AND lon <= ? AND accuracy <= ?
|
||||||
ORDER BY time ASC
|
ORDER BY time ASC
|
||||||
""",
|
""",
|
||||||
arrayOf(device_id, south.toString(), north.toString(), west.toString(), east.toString(), max_accuracy.toString())
|
arrayOf(device_id, south.toString(), north.toString(), west.toString(), east.toString(), max_accuracy.toString())
|
||||||
))
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun _trkpt_generator(cursor: Cursor) = iterator<Trkpt>
|
fun _trkpt_generator(cursor: Cursor) = iterator<Trkpt>
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,10 @@ object Keys {
|
||||||
const val STATE_THEME_LIGHT_MODE: String = "stateLightMode"
|
const val STATE_THEME_LIGHT_MODE: String = "stateLightMode"
|
||||||
const val STATE_THEME_DARK_MODE: String = "stateDarkMode"
|
const val STATE_THEME_DARK_MODE: String = "stateDarkMode"
|
||||||
|
|
||||||
|
// Track view
|
||||||
|
const val SELECTION_MODE_STARTSTOP = 1
|
||||||
|
const val SELECTION_MODE_SPATIAL = 2
|
||||||
|
|
||||||
// dialog types
|
// dialog types
|
||||||
const val DIALOG_DELETE_TRACK: Int = 1
|
const val DIALOG_DELETE_TRACK: Int = 1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ class MainActivity: AppCompatActivity()
|
||||||
/* Overrides onCreate from AppCompatActivity */
|
/* Overrides onCreate from AppCompatActivity */
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
|
Log.i("VOUSSOIR", "MainActivity.onCreate")
|
||||||
trackbook = (applicationContext as Trackbook)
|
trackbook = (applicationContext as Trackbook)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
request_permissions(this)
|
request_permissions(this)
|
||||||
|
|
@ -125,6 +126,7 @@ class MainActivity: AppCompatActivity()
|
||||||
/* Overrides onDestroy from AppCompatActivity */
|
/* Overrides onDestroy from AppCompatActivity */
|
||||||
override fun onDestroy()
|
override fun onDestroy()
|
||||||
{
|
{
|
||||||
|
Log.i("VOUSSOIR", "MainActivity.onDestroy")
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
// unregister listener for changes in shared preferences
|
// unregister listener for changes in shared preferences
|
||||||
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
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 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
|
||||||
|
|
@ -57,15 +56,16 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
private lateinit var trackbook: Trackbook
|
private lateinit var trackbook: Trackbook
|
||||||
|
|
||||||
private var bound: Boolean = false
|
private var tracker_service_bound: Boolean = false
|
||||||
|
private var tracker_service: TrackerService? = null
|
||||||
|
|
||||||
val handler: Handler = Handler(Looper.getMainLooper())
|
val handler: Handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
var continuous_auto_center: Boolean = true
|
var continuous_auto_center: Boolean = true
|
||||||
private var trackerService: TrackerService? = null
|
|
||||||
private lateinit var database_changed_listener: DatabaseChangedListener
|
|
||||||
var show_debug: Boolean = false
|
var show_debug: Boolean = false
|
||||||
|
|
||||||
var thismapfragment: MapFragment? = null
|
private lateinit var database_changed_listener: DatabaseChangedListener
|
||||||
|
|
||||||
lateinit var rootView: View
|
lateinit var rootView: View
|
||||||
private lateinit var mapView: MapView
|
private lateinit var mapView: MapView
|
||||||
lateinit var mainButton: ExtendedFloatingActionButton
|
lateinit var mainButton: ExtendedFloatingActionButton
|
||||||
|
|
@ -77,14 +77,12 @@ class MapFragment : Fragment()
|
||||||
private var current_track_overlay: Polyline? = null
|
private var current_track_overlay: Polyline? = null
|
||||||
private var current_position_overlays = ArrayList<Overlay>()
|
private var current_position_overlays = ArrayList<Overlay>()
|
||||||
private var homepoints_overlays = ArrayList<Overlay>()
|
private var homepoints_overlays = ArrayList<Overlay>()
|
||||||
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
|
||||||
{
|
{
|
||||||
|
|
@ -116,7 +114,6 @@ class MapFragment : Fragment()
|
||||||
zoom_out_button = rootView.findViewById(R.id.zoom_out_button)
|
zoom_out_button = rootView.findViewById(R.id.zoom_out_button)
|
||||||
map_current_time = rootView.findViewById(R.id.map_current_time)
|
map_current_time = rootView.findViewById(R.id.map_current_time)
|
||||||
mainButton = rootView.findViewById(R.id.main_button)
|
mainButton = rootView.findViewById(R.id.main_button)
|
||||||
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
|
|
||||||
|
|
||||||
mapView.setOnLongClickListener{
|
mapView.setOnLongClickListener{
|
||||||
Log.i("VOUSSOIR", "mapview longpress")
|
Log.i("VOUSSOIR", "mapview longpress")
|
||||||
|
|
@ -198,7 +195,7 @@ class MapFragment : Fragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
mainButton.setOnClickListener {
|
mainButton.setOnClickListener {
|
||||||
val tracker = trackerService
|
val tracker = tracker_service
|
||||||
if (tracker == null)
|
if (tracker == null)
|
||||||
{
|
{
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
|
|
@ -214,7 +211,7 @@ class MapFragment : Fragment()
|
||||||
handler.postDelayed(redraw_runnable, 0)
|
handler.postDelayed(redraw_runnable, 0)
|
||||||
}
|
}
|
||||||
currentLocationButton.setOnClickListener {
|
currentLocationButton.setOnClickListener {
|
||||||
val tracker = trackerService
|
val tracker = tracker_service
|
||||||
if (tracker == null)
|
if (tracker == null)
|
||||||
{
|
{
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
|
|
@ -240,13 +237,7 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "MapFragment.onStart")
|
Log.i("VOUSSOIR", "MapFragment.onStart")
|
||||||
super.onStart()
|
super.onStart()
|
||||||
// request location permission if denied
|
activity?.bindService(Intent(activity, TrackerService::class.java), tracker_service_connection, Context.BIND_AUTO_CREATE)
|
||||||
if (ContextCompat.checkSelfPermission(activity as Context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED)
|
|
||||||
{
|
|
||||||
requestLocationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
|
|
||||||
}
|
|
||||||
// bind to TrackerService
|
|
||||||
activity?.bindService(Intent(activity, TrackerService::class.java), connection, Context.BIND_AUTO_CREATE)
|
|
||||||
handler.post(redraw_runnable)
|
handler.post(redraw_runnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,13 +261,13 @@ class MapFragment : Fragment()
|
||||||
super.onPause()
|
super.onPause()
|
||||||
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
|
||||||
val tracker = trackerService
|
val tracker = tracker_service
|
||||||
if (tracker == null)
|
if (tracker == null)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
saveBestLocationState(tracker.currentBestLocation)
|
saveBestLocationState(tracker.currentBestLocation)
|
||||||
if (bound && (tracker.tracking_state == Keys.STATE_MAPVIEW || tracker.tracking_state == Keys.STATE_STOP))
|
if (tracker_service_bound && (tracker.tracking_state == Keys.STATE_MAPVIEW || tracker.tracking_state == Keys.STATE_STOP))
|
||||||
{
|
{
|
||||||
tracker.remove_gps_location_listener()
|
tracker.remove_gps_location_listener()
|
||||||
tracker.remove_network_location_listener()
|
tracker.remove_network_location_listener()
|
||||||
|
|
@ -290,9 +281,9 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
super.onStop()
|
super.onStop()
|
||||||
// unbind from TrackerService
|
// unbind from TrackerService
|
||||||
if (bound)
|
if (tracker_service_bound)
|
||||||
{
|
{
|
||||||
activity?.unbindService(connection)
|
activity?.unbindService(tracker_service_connection)
|
||||||
handleServiceUnbind()
|
handleServiceUnbind()
|
||||||
}
|
}
|
||||||
handler.removeCallbacks(redraw_runnable)
|
handler.removeCallbacks(redraw_runnable)
|
||||||
|
|
@ -317,24 +308,6 @@ class MapFragment : Fragment()
|
||||||
handler.removeCallbacks(redraw_runnable)
|
handler.removeCallbacks(redraw_runnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val requestLocationPermissionLauncher = registerForActivityResult(RequestPermission()) { isGranted: Boolean ->
|
|
||||||
if (isGranted)
|
|
||||||
{
|
|
||||||
// permission was granted - re-bind service
|
|
||||||
activity?.unbindService(connection)
|
|
||||||
activity?.bindService(Intent(activity, TrackerService::class.java), connection, Context.BIND_AUTO_CREATE)
|
|
||||||
Log.i("VOUSSOIR", "Request result: Location permission has been granted.")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// permission denied - unbind service
|
|
||||||
activity?.unbindService(connection)
|
|
||||||
}
|
|
||||||
val gpsProviderActive = if (trackerService == null) false else trackerService!!.gpsProviderActive
|
|
||||||
val networkProviderActive = if (trackerService == null) false else trackerService!!.networkProviderActive
|
|
||||||
toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -348,9 +321,9 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
activity?.startService(intent)
|
activity?.startService(intent)
|
||||||
}
|
}
|
||||||
if (trackerService != null)
|
if (tracker_service != null)
|
||||||
{
|
{
|
||||||
trackerService!!.state_full_recording()
|
tracker_service!!.state_full_recording()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -358,7 +331,7 @@ class MapFragment : Fragment()
|
||||||
/* Handles state when service is being unbound */
|
/* Handles state when service is being unbound */
|
||||||
private fun handleServiceUnbind()
|
private fun handleServiceUnbind()
|
||||||
{
|
{
|
||||||
bound = false
|
tracker_service_bound = false
|
||||||
// unregister listener for changes in shared preferences
|
// unregister listener for changes in shared preferences
|
||||||
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
}
|
}
|
||||||
|
|
@ -397,12 +370,11 @@ class MapFragment : Fragment()
|
||||||
current_position_overlays.clear()
|
current_position_overlays.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mark current position on map */
|
|
||||||
fun create_current_position_overlays()
|
fun create_current_position_overlays()
|
||||||
{
|
{
|
||||||
clear_current_position_overlays()
|
clear_current_position_overlays()
|
||||||
|
|
||||||
val tracker = trackerService
|
val tracker = tracker_service
|
||||||
if (tracker == null)
|
if (tracker == null)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
|
|
@ -579,7 +551,7 @@ class MapFragment : Fragment()
|
||||||
|
|
||||||
fun update_main_button()
|
fun update_main_button()
|
||||||
{
|
{
|
||||||
val tracker = trackerService
|
val tracker = tracker_service
|
||||||
mainButton.isEnabled = trackbook.database.ready
|
mainButton.isEnabled = trackbook.database.ready
|
||||||
currentLocationButton.isVisible = true
|
currentLocationButton.isVisible = true
|
||||||
if (! trackbook.database.ready)
|
if (! trackbook.database.ready)
|
||||||
|
|
@ -601,27 +573,7 @@ class MapFragment : Fragment()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggleLocationErrorBar(gpsProviderActive: Boolean, networkProviderActive: Boolean)
|
private val tracker_service_connection = object : ServiceConnection {
|
||||||
{
|
|
||||||
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED)
|
|
||||||
{
|
|
||||||
// CASE: Location permission not granted
|
|
||||||
locationErrorBar.setText(R.string.snackbar_message_location_permission_denied)
|
|
||||||
if (!locationErrorBar.isShown) locationErrorBar.show()
|
|
||||||
}
|
|
||||||
else if (!gpsProviderActive && !networkProviderActive)
|
|
||||||
{
|
|
||||||
// CASE: Location setting is off
|
|
||||||
locationErrorBar.setText(R.string.snackbar_message_location_offline)
|
|
||||||
if (!locationErrorBar.isShown) locationErrorBar.show()
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (locationErrorBar.isShown) locationErrorBar.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val connection = object : ServiceConnection {
|
|
||||||
override fun onServiceConnected(className: ComponentName, service: IBinder)
|
override fun onServiceConnected(className: ComponentName, service: IBinder)
|
||||||
{
|
{
|
||||||
// get reference to tracker service]
|
// get reference to tracker service]
|
||||||
|
|
@ -630,8 +582,8 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bound = true
|
tracker_service_bound = true
|
||||||
trackerService = serviceref
|
tracker_service = serviceref
|
||||||
// get state of tracking and update button if necessary
|
// get state of tracking and update button if necessary
|
||||||
redraw()
|
redraw()
|
||||||
// register listener for changes in shared preferences
|
// register listener for changes in shared preferences
|
||||||
|
|
@ -648,7 +600,7 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
// Log.i("VOUSSOIR", "MapFragment.redraw")
|
// Log.i("VOUSSOIR", "MapFragment.redraw")
|
||||||
update_main_button()
|
update_main_button()
|
||||||
val tracker = trackerService
|
val tracker = tracker_service
|
||||||
if (tracker == null)
|
if (tracker == null)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
|
|
@ -693,7 +645,7 @@ class MapFragment : Fragment()
|
||||||
|
|
||||||
fun state_name(): String
|
fun state_name(): String
|
||||||
{
|
{
|
||||||
val tracker = trackerService
|
val tracker = tracker_service
|
||||||
if (tracker == null)
|
if (tracker == null)
|
||||||
{
|
{
|
||||||
return "null"
|
return "null"
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import java.util.*
|
||||||
|
|
||||||
data class Track (
|
data class Track (
|
||||||
val database: Database,
|
val database: Database,
|
||||||
val device_id: String,
|
var device_id: String,
|
||||||
var name: String = "",
|
var name: String = "",
|
||||||
var _start_time: Long = 0L,
|
var _start_time: Long = 0L,
|
||||||
var _end_time: Long = 0L,
|
var _end_time: Long = 0L,
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,17 @@
|
||||||
package net.voussoir.trkpt
|
package net.voussoir.trkpt
|
||||||
|
|
||||||
import YesNoDialog
|
import YesNoDialog
|
||||||
|
import android.Manifest
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.Color
|
||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
|
import android.location.LocationListener
|
||||||
|
import android.location.LocationManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
|
@ -55,10 +60,7 @@ 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.MapEventsOverlay
|
import org.osmdroid.views.overlay.*
|
||||||
import org.osmdroid.views.overlay.Marker
|
|
||||||
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.SimpleFastPointOverlay
|
||||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
|
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
|
||||||
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
|
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
|
||||||
|
|
@ -71,7 +73,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
|
|
||||||
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 delete_whole_track_button: 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
|
||||||
|
|
@ -93,6 +95,8 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
var ready_to_straighten: Boolean = false
|
var ready_to_straighten: Boolean = false
|
||||||
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
|
||||||
|
var selection_mode: Int = Keys.SELECTION_MODE_STARTSTOP
|
||||||
|
|
||||||
private lateinit var mapView: MapView
|
private lateinit var mapView: MapView
|
||||||
private lateinit var controller: IMapController
|
private lateinit var controller: IMapController
|
||||||
private lateinit var statisticsSheetBehavior: BottomSheetBehavior<View>
|
private lateinit var statisticsSheetBehavior: BottomSheetBehavior<View>
|
||||||
|
|
@ -115,6 +119,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
private lateinit var track_segment_overlays: ArrayDeque<Polyline>
|
private lateinit var track_segment_overlays: ArrayDeque<Polyline>
|
||||||
private var track_geopoints: MutableList<IGeoPoint> = mutableListOf()
|
private var track_geopoints: MutableList<IGeoPoint> = mutableListOf()
|
||||||
private var track_points_overlay: SimpleFastPointOverlay? = null
|
private var track_points_overlay: SimpleFastPointOverlay? = null
|
||||||
|
private var current_position_overlays = ArrayList<Overlay>()
|
||||||
// private lateinit var trkpt_infowindow: InfoWindow
|
// 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())
|
||||||
|
|
@ -123,6 +128,10 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
var selected_trkpt: Trkpt? = null
|
var selected_trkpt: Trkpt? = null
|
||||||
lateinit var selected_trkpt_marker: Marker
|
lateinit var selected_trkpt_marker: Marker
|
||||||
|
|
||||||
|
private lateinit var locationManager: LocationManager
|
||||||
|
private lateinit var gpsLocationListener: LocationListener
|
||||||
|
private var gpslistenerregistered = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
@ -156,10 +165,12 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
end_time=requested_end_time,
|
end_time=requested_end_time,
|
||||||
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
||||||
))
|
))
|
||||||
|
selection_mode = Keys.SELECTION_MODE_STARTSTOP
|
||||||
|
|
||||||
rootView = inflater.inflate(R.layout.fragment_track, container, false)
|
rootView = inflater.inflate(R.layout.fragment_track, container, false)
|
||||||
mapView = rootView.findViewById(R.id.map)
|
mapView = rootView.findViewById(R.id.map)
|
||||||
save_track_button = rootView.findViewById(R.id.save_button)
|
save_track_button = rootView.findViewById(R.id.save_button)
|
||||||
deleteButton = rootView.findViewById(R.id.delete_button)
|
delete_whole_track_button = rootView.findViewById(R.id.delete_button)
|
||||||
zoom_in_button = rootView.findViewById(R.id.zoom_in_button)
|
zoom_in_button = rootView.findViewById(R.id.zoom_in_button)
|
||||||
zoom_out_button = rootView.findViewById(R.id.zoom_out_button)
|
zoom_out_button = rootView.findViewById(R.id.zoom_out_button)
|
||||||
trackNameView = rootView.findViewById(R.id.statistics_track_name_headline)
|
trackNameView = rootView.findViewById(R.id.statistics_track_name_headline)
|
||||||
|
|
@ -178,6 +189,20 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
}
|
}
|
||||||
controller.setZoom(Keys.DEFAULT_ZOOM_LEVEL)
|
controller.setZoom(Keys.DEFAULT_ZOOM_LEVEL)
|
||||||
|
|
||||||
|
val has_permission: Boolean = ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||||
|
if (has_permission)
|
||||||
|
{
|
||||||
|
locationManager = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
|
gpsLocationListener = createLocationListener()
|
||||||
|
locationManager.requestLocationUpdates(
|
||||||
|
LocationManager.GPS_PROVIDER,
|
||||||
|
0,
|
||||||
|
0f,
|
||||||
|
gpsLocationListener,
|
||||||
|
)
|
||||||
|
gpslistenerregistered = true
|
||||||
|
}
|
||||||
|
|
||||||
// trkpt_infowindow = MarkerInfoWindow(R.layout.trkpt_infowindow, mapView)
|
// trkpt_infowindow = MarkerInfoWindow(R.layout.trkpt_infowindow, mapView)
|
||||||
|
|
||||||
statisticsSheet = rootView.findViewById(R.id.statistics_sheet)
|
statisticsSheet = rootView.findViewById(R.id.statistics_sheet)
|
||||||
|
|
@ -271,7 +296,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
Log.i("VOUSSOIR", selected.rendered_by_polyline?.actualPoints?.size.toString())
|
Log.i("VOUSSOIR", selected.rendered_by_polyline?.actualPoints?.size.toString())
|
||||||
selected.rendered_by_polyline?.setPoints(ArrayList(selected.rendered_by_polyline?.actualPoints))
|
selected.rendered_by_polyline?.setPoints(ArrayList(selected.rendered_by_polyline?.actualPoints))
|
||||||
Log.i("VOUSSOIR", selected.rendered_by_polyline?.actualPoints?.size.toString())
|
Log.i("VOUSSOIR", selected.rendered_by_polyline?.actualPoints?.size.toString())
|
||||||
trackbook.database.delete_trkpt(selected.device_id, selected.time)
|
trackbook.database.delete_trkpt(selected)
|
||||||
trackbook.database.commit()
|
trackbook.database.commit()
|
||||||
deselect_trkpt()
|
deselect_trkpt()
|
||||||
mapView.invalidate()
|
mapView.invalidate()
|
||||||
|
|
@ -291,6 +316,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
end_time=track.trkpts.last().time,
|
end_time=track.trkpts.last().time,
|
||||||
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
||||||
))
|
))
|
||||||
|
selection_mode = Keys.SELECTION_MODE_STARTSTOP
|
||||||
deselect_trkpt()
|
deselect_trkpt()
|
||||||
render_track()
|
render_track()
|
||||||
}
|
}
|
||||||
|
|
@ -309,6 +335,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
end_time=selected.time,
|
end_time=selected.time,
|
||||||
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
||||||
))
|
))
|
||||||
|
selection_mode = Keys.SELECTION_MODE_STARTSTOP
|
||||||
deselect_trkpt()
|
deselect_trkpt()
|
||||||
render_track()
|
render_track()
|
||||||
}
|
}
|
||||||
|
|
@ -323,12 +350,14 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
val polyline = selected.rendered_by_polyline
|
val polyline = selected.rendered_by_polyline
|
||||||
if (polyline != null)
|
if (polyline != null)
|
||||||
{
|
{
|
||||||
|
track.device_id = selected.device_id
|
||||||
track.load_trkpts(trackbook.database.select_trkpt_start_end(
|
track.load_trkpts(trackbook.database.select_trkpt_start_end(
|
||||||
track.device_id,
|
selected.device_id,
|
||||||
start_time=(polyline.actualPoints.first() as Trkpt).time,
|
start_time=(polyline.actualPoints.first() as Trkpt).time,
|
||||||
end_time=(polyline.actualPoints.last() as Trkpt).time,
|
end_time=(polyline.actualPoints.last() as Trkpt).time,
|
||||||
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
||||||
))
|
))
|
||||||
|
selection_mode = Keys.SELECTION_MODE_STARTSTOP
|
||||||
|
|
||||||
track.expand_to_trkseg_bounds()
|
track.expand_to_trkseg_bounds()
|
||||||
|
|
||||||
|
|
@ -342,13 +371,14 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
when_was_i_here_button.setOnClickListener {
|
when_was_i_here_button.setOnClickListener {
|
||||||
Log.i("VOUSSOIR", "when_was_i_here_button.")
|
Log.i("VOUSSOIR", "when_was_i_here_button.")
|
||||||
track.load_trkpts(trackbook.database.select_trkpt_bounding_box(
|
track.load_trkpts(trackbook.database.select_trkpt_bounding_box(
|
||||||
device_id=track.device_id,
|
device_id=null,
|
||||||
north=mapView.boundingBox.actualNorth,
|
north=mapView.boundingBox.actualNorth,
|
||||||
south=mapView.boundingBox.actualSouth,
|
south=mapView.boundingBox.actualSouth,
|
||||||
east=mapView.boundingBox.lonEast,
|
east=mapView.boundingBox.lonEast,
|
||||||
west=mapView.boundingBox.lonWest,
|
west=mapView.boundingBox.lonWest,
|
||||||
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
||||||
))
|
))
|
||||||
|
selection_mode = Keys.SELECTION_MODE_SPATIAL
|
||||||
set_datetimes_from_track()
|
set_datetimes_from_track()
|
||||||
render_track()
|
render_track()
|
||||||
}
|
}
|
||||||
|
|
@ -414,7 +444,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
dialog.show()
|
dialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteButton.setOnClickListener {
|
delete_whole_track_button.setOnClickListener {
|
||||||
val dialogMessage = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n${track.trkpts.size} trackpoints"
|
val dialogMessage = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n${track.trkpts.size} trackpoints"
|
||||||
YesNoDialog(this@TrackFragment as YesNoDialog.YesNoDialogListener).show(
|
YesNoDialog(this@TrackFragment as YesNoDialog.YesNoDialogListener).show(
|
||||||
context = activity as Context,
|
context = activity as Context,
|
||||||
|
|
@ -468,12 +498,78 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
return rootView
|
return rootView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView()
|
||||||
|
{
|
||||||
|
super.onDestroyView()
|
||||||
|
if (gpslistenerregistered)
|
||||||
|
{
|
||||||
|
locationManager.removeUpdates(gpsLocationListener)
|
||||||
|
gpslistenerregistered = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear_current_position_overlays()
|
||||||
|
{
|
||||||
|
for (ov in current_position_overlays)
|
||||||
|
{
|
||||||
|
if (ov in mapView.overlays)
|
||||||
|
{
|
||||||
|
mapView.overlays.remove(ov)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_position_overlays.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun create_current_position_overlays(location: Location)
|
||||||
|
{
|
||||||
|
clear_current_position_overlays()
|
||||||
|
|
||||||
|
val newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_blue_24dp)!!
|
||||||
|
val fillcolor = Color.argb(64, 60, 152, 219)
|
||||||
|
|
||||||
|
val current_location_radius = Polygon()
|
||||||
|
current_location_radius.points = Polygon.pointsAsCircle(
|
||||||
|
GeoPoint(location.latitude, location.longitude),
|
||||||
|
location.accuracy.toDouble()
|
||||||
|
)
|
||||||
|
current_location_radius.fillPaint.color = fillcolor
|
||||||
|
current_location_radius.outlinePaint.color = Color.argb(0, 0, 0, 0)
|
||||||
|
current_position_overlays.add(current_location_radius)
|
||||||
|
|
||||||
|
val overlayItems: java.util.ArrayList<OverlayItem> = java.util.ArrayList<OverlayItem>()
|
||||||
|
val overlayItem: OverlayItem = createOverlayItem(
|
||||||
|
location.latitude,
|
||||||
|
location.longitude,
|
||||||
|
title="Current location",
|
||||||
|
description="Current location",
|
||||||
|
)
|
||||||
|
overlayItem.setMarker(newMarker)
|
||||||
|
overlayItems.add(overlayItem)
|
||||||
|
current_position_overlays.add(createOverlay(requireContext(), overlayItems))
|
||||||
|
|
||||||
|
for (ov in current_position_overlays)
|
||||||
|
{
|
||||||
|
mapView.overlays.add(ov)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Overrides onResume from Fragment */
|
/* Overrides onResume from Fragment */
|
||||||
override fun onResume()
|
override fun onResume()
|
||||||
{
|
{
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createLocationListener(): LocationListener
|
||||||
|
{
|
||||||
|
return object : LocationListener
|
||||||
|
{
|
||||||
|
override fun onLocationChanged(location: Location)
|
||||||
|
{
|
||||||
|
create_current_position_overlays(location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun select_trkpt(trkpt: Trkpt)
|
fun select_trkpt(trkpt: Trkpt)
|
||||||
{
|
{
|
||||||
selected_trkpt = trkpt
|
selected_trkpt = trkpt
|
||||||
|
|
@ -657,6 +753,11 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
{
|
{
|
||||||
create_start_end_markers(requireContext(), mapView, pl.actualPoints.first() as Trkpt, pl.actualPoints.last() as Trkpt)
|
create_start_end_markers(requireContext(), mapView, pl.actualPoints.first() as Trkpt, pl.actualPoints.last() as Trkpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (ov in current_position_overlays)
|
||||||
|
{
|
||||||
|
mapView.overlays.add(ov)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun new_track_segment_overlay(): Polyline
|
fun new_track_segment_overlay(): Polyline
|
||||||
|
|
@ -830,6 +931,7 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
end_time=get_datetime(track_query_end_date, track_query_end_time, seconds=59).time,
|
end_time=get_datetime(track_query_end_date, track_query_end_time, seconds=59).time,
|
||||||
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
max_accuracy=PreferencesHelper.load_max_accuracy(),
|
||||||
))
|
))
|
||||||
|
selection_mode = Keys.SELECTION_MODE_STARTSTOP
|
||||||
Log.i("VOUSSOIR", "TrackFragment.requery_and_render: Reloaded ${track.trkpts.size} trkpts.")
|
Log.i("VOUSSOIR", "TrackFragment.requery_and_render: Reloaded ${track.trkpts.size} trkpts.")
|
||||||
render_track()
|
render_track()
|
||||||
}
|
}
|
||||||
|
|
@ -863,14 +965,31 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
{
|
{
|
||||||
if (type == Keys.DIALOG_DELETE_TRACK && dialogResult && track.trkpts.isNotEmpty())
|
if (type == Keys.DIALOG_DELETE_TRACK && dialogResult && track.trkpts.isNotEmpty())
|
||||||
{
|
{
|
||||||
trackbook.database.delete_trkpt_start_end(
|
if (selection_mode == Keys.SELECTION_MODE_STARTSTOP)
|
||||||
track.device_id,
|
{
|
||||||
track.trkpts.first().time,
|
trackbook.database.delete_trkpt_start_end(
|
||||||
track.trkpts.last().time,
|
track.device_id,
|
||||||
)
|
track.trkpts.first().time,
|
||||||
trackbook.database.commit()
|
track.trkpts.last().time,
|
||||||
handler.removeCallbacks(requery_and_render)
|
)
|
||||||
handler.postDelayed(requery_and_render, RERENDER_DELAY)
|
trackbook.database.commit()
|
||||||
|
handler.removeCallbacks(requery_and_render)
|
||||||
|
handler.postDelayed(requery_and_render, RERENDER_DELAY)
|
||||||
|
}
|
||||||
|
else if (selection_mode == Keys.SELECTION_MODE_SPATIAL)
|
||||||
|
{
|
||||||
|
// If the user clicked the "when was I here" button to select an area of points, and
|
||||||
|
// then presses the Track delete button, we don't want to delete all points between
|
||||||
|
// the earliest start and latest stop -- we just want to delete the points that are
|
||||||
|
// shown on screen.
|
||||||
|
for (trkpt in track.trkpts)
|
||||||
|
{
|
||||||
|
trackbook.database.delete_trkpt(trkpt)
|
||||||
|
}
|
||||||
|
trackbook.database.commit()
|
||||||
|
handler.removeCallbacks(requery_and_render)
|
||||||
|
handler.postDelayed(requery_and_render, RERENDER_DELAY)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue