Compare commits

..

10 commits

Author SHA1 Message Date
29a4a1dae3 Version 1.3.0. 2023-12-26 19:14:16 -08:00
31c77528d7 Fix behavior of track delete button after using when was I here.
The delete button was deleting all points between the first and last,
which, when you use the "when was I here" button, could span days
or months that you don't want to delete. So, after doing a spatial
query, the delete button will only delete the points that are shown.
2023-12-25 21:37:35 -08:00
535133ce22 Rename deleteButton to delete_whole_track_button. 2023-12-25 20:13:59 -08:00
a04a0d3a3a Show current position on Track view.
This primarily helps when you want to use the "when was I here" button,
to remember a previous day when you were at the same location.

Besides that, seeing the current realtime location provides helpful
context to historical tracks.
2023-12-24 22:23:27 -08:00
eb1ad45c4c Remove permission launcher from MapFragment, already in MainActivity. 2023-12-24 22:20:43 -08:00
47c338f38f Rename tracker_service variables for clarity. 2023-12-24 22:19:35 -08:00
cd25415e9b Remove unneeded thismapfragment. 2023-12-24 22:18:34 -08:00
a75ee66ca4 Remove code for toggleLocationErrorBar. 2023-12-24 22:18:10 -08:00
70e6e64918 Let "when was I here" button query all device ids. 2023-12-24 19:47:13 -08:00
d5df922d3d Version 1.2.1. 2023-12-24 19:28:33 -08:00
7 changed files with 190 additions and 98 deletions

View file

@ -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"
} }

View file

@ -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>

View file

@ -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

View file

@ -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)

View file

@ -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"

View file

@ -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,

View file

@ -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)
}
} }
} }