checkpoint
This commit is contained in:
parent
2c33cc88f7
commit
8cbfa729f0
9 changed files with 57 additions and 205 deletions
|
@ -33,7 +33,6 @@ import android.view.WindowManager
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
@ -71,7 +70,6 @@ class MapFragment : Fragment()
|
||||||
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 track: Track
|
|
||||||
private lateinit var currentBestLocation: Location
|
private lateinit var currentBestLocation: Location
|
||||||
private lateinit var trackerService: TrackerService
|
private lateinit var trackerService: TrackerService
|
||||||
|
|
||||||
|
@ -185,12 +183,13 @@ class MapFragment : Fragment()
|
||||||
dialog.cancel()
|
dialog.cancel()
|
||||||
}
|
}
|
||||||
save_button.setOnClickListener {
|
save_button.setOnClickListener {
|
||||||
|
val radius = radius_input.text.toString().toDoubleOrNull() ?: 25.0
|
||||||
trackbook.database.insert_homepoint(
|
trackbook.database.insert_homepoint(
|
||||||
id=random_long(),
|
id=random_long(),
|
||||||
name=name_input.text.toString(),
|
name=name_input.text.toString(),
|
||||||
latitude=point.latitude,
|
latitude=point.latitude,
|
||||||
longitude=point.longitude,
|
longitude=point.longitude,
|
||||||
radius=radius_input.text.toString().toDouble(),
|
radius=radius,
|
||||||
)
|
)
|
||||||
trackbook.load_homepoints()
|
trackbook.load_homepoints()
|
||||||
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
|
||||||
|
@ -221,6 +220,7 @@ class MapFragment : Fragment()
|
||||||
|
|
||||||
mapView.setOnTouchListener { v, event ->
|
mapView.setOnTouchListener { v, event ->
|
||||||
continuous_auto_center = false
|
continuous_auto_center = false
|
||||||
|
zoomLevel = mapView.zoomLevelDouble
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,10 +232,12 @@ class MapFragment : Fragment()
|
||||||
centerMap(currentBestLocation, animated=true)
|
centerMap(currentBestLocation, animated=true)
|
||||||
}
|
}
|
||||||
zoom_in_button.setOnClickListener {
|
zoom_in_button.setOnClickListener {
|
||||||
controller.zoomTo(mapView.zoomLevelDouble + 0.5, 250)
|
zoomLevel += 0.5
|
||||||
|
controller.zoomTo(mapView.zoomLevelDouble + 0.5, 0)
|
||||||
}
|
}
|
||||||
zoom_out_button.setOnClickListener {
|
zoom_out_button.setOnClickListener {
|
||||||
controller.zoomTo(mapView.zoomLevelDouble - 0.5, 250)
|
zoomLevel -= 0.5
|
||||||
|
controller.zoomTo(mapView.zoomLevelDouble - 0.5, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
@ -247,7 +249,8 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
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)
|
||||||
|
{
|
||||||
requestLocationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
|
requestLocationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||||
}
|
}
|
||||||
// bind to TrackerService
|
// bind to TrackerService
|
||||||
|
@ -556,7 +559,7 @@ class MapFragment : Fragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Overlay current track on map */
|
/* Overlay current track on map */
|
||||||
fun create_current_track_overlay(track: Track, trackingState: Int)
|
fun create_current_track_overlay(trkpts: Collection<Trkpt>, trackingState: Int)
|
||||||
{
|
{
|
||||||
if (currentTrackOverlay != null) {
|
if (currentTrackOverlay != null) {
|
||||||
mapView.overlays.remove(currentTrackOverlay)
|
mapView.overlays.remove(currentTrackOverlay)
|
||||||
|
@ -564,9 +567,9 @@ class MapFragment : Fragment()
|
||||||
if (currentTrackSpecialMarkerOverlay != null) {
|
if (currentTrackSpecialMarkerOverlay != null) {
|
||||||
mapView.overlays.remove(currentTrackSpecialMarkerOverlay)
|
mapView.overlays.remove(currentTrackSpecialMarkerOverlay)
|
||||||
}
|
}
|
||||||
if (track.trkpts.isNotEmpty()) {
|
if (trkpts.isNotEmpty()) {
|
||||||
createTrackOverlay(requireContext(), mapView, track, trackingState)
|
createTrackOverlay(requireContext(), mapView, trkpts, trackingState)
|
||||||
createSpecialMakersTrackOverlay(requireContext(), mapView, track, trackingState)
|
createSpecialMakersTrackOverlay(requireContext(), mapView, trkpts, trackingState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,7 +611,8 @@ class MapFragment : Fragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val connection = object : ServiceConnection {
|
private val connection = object : ServiceConnection {
|
||||||
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
override fun onServiceConnected(className: ComponentName, service: IBinder)
|
||||||
|
{
|
||||||
bound = true
|
bound = true
|
||||||
// get reference to tracker service
|
// get reference to tracker service
|
||||||
val binder = service as TrackerService.LocalBinder
|
val binder = service as TrackerService.LocalBinder
|
||||||
|
@ -622,7 +626,8 @@ class MapFragment : Fragment()
|
||||||
handler.removeCallbacks(periodicLocationRequestRunnable)
|
handler.removeCallbacks(periodicLocationRequestRunnable)
|
||||||
handler.postDelayed(periodicLocationRequestRunnable, 0)
|
handler.postDelayed(periodicLocationRequestRunnable, 0)
|
||||||
}
|
}
|
||||||
override fun onServiceDisconnected(arg0: ComponentName) {
|
override fun onServiceDisconnected(arg0: ComponentName)
|
||||||
|
{
|
||||||
// service has crashed, or was killed by the system
|
// service has crashed, or was killed by the system
|
||||||
handleServiceUnbind()
|
handleServiceUnbind()
|
||||||
}
|
}
|
||||||
|
@ -632,17 +637,16 @@ class MapFragment : Fragment()
|
||||||
override fun run()
|
override fun run()
|
||||||
{
|
{
|
||||||
currentBestLocation = trackerService.currentBestLocation
|
currentBestLocation = trackerService.currentBestLocation
|
||||||
track = trackerService.track
|
|
||||||
gpsProviderActive = trackerService.gpsProviderActive
|
gpsProviderActive = trackerService.gpsProviderActive
|
||||||
networkProviderActive = trackerService.networkProviderActive
|
networkProviderActive = trackerService.networkProviderActive
|
||||||
trackingState = trackerService.trackingState
|
trackingState = trackerService.trackingState
|
||||||
// update location and track
|
// update location and track
|
||||||
create_current_position_overlays(currentBestLocation, trackingState)
|
create_current_position_overlays(currentBestLocation, trackingState)
|
||||||
create_current_track_overlay(track, trackingState)
|
create_current_track_overlay(trackerService.recent_trkpts, trackingState)
|
||||||
// center map, if it had not been dragged/zoomed before
|
// center map, if it had not been dragged/zoomed before
|
||||||
if (continuous_auto_center)
|
if (continuous_auto_center)
|
||||||
{
|
{
|
||||||
centerMap(currentBestLocation, true)
|
centerMap(currentBestLocation, animated=false)
|
||||||
}
|
}
|
||||||
handler.postDelayed(this, Keys.REQUEST_CURRENT_LOCATION_INTERVAL)
|
handler.postDelayed(this, Keys.REQUEST_CURRENT_LOCATION_INTERVAL)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,6 @@ import android.widget.Toast
|
||||||
import org.y20k.trackbook.helpers.iso8601_format
|
import org.y20k.trackbook.helpers.iso8601_format
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.coroutines.suspendCoroutine
|
|
||||||
|
|
||||||
data class Track (
|
data class Track (
|
||||||
val database: Database,
|
val database: Database,
|
||||||
|
|
|
@ -29,14 +29,13 @@ import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import org.y20k.trackbook.dialogs.RenameTrackDialog
|
|
||||||
import org.y20k.trackbook.helpers.LogHelper
|
import org.y20k.trackbook.helpers.LogHelper
|
||||||
import org.y20k.trackbook.helpers.iso8601_format
|
import org.y20k.trackbook.helpers.iso8601_format
|
||||||
import org.y20k.trackbook.ui.TrackFragmentLayoutHolder
|
import org.y20k.trackbook.ui.TrackFragmentLayoutHolder
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDialog.YesNoDialogListener
|
class TrackFragment : Fragment(), YesNoDialog.YesNoDialogListener
|
||||||
{
|
{
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TrackFragment::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TrackFragment::class.java)
|
||||||
|
@ -59,12 +58,12 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
layout = TrackFragmentLayoutHolder(activity as Context, inflater, container, track)
|
layout = TrackFragmentLayoutHolder(activity as Context, inflater, container, track)
|
||||||
|
|
||||||
// set up share button
|
// set up share button
|
||||||
layout.shareButton.setOnClickListener {
|
layout.save_track_button.setOnClickListener {
|
||||||
openSaveGpxDialog()
|
openSaveGpxDialog()
|
||||||
}
|
}
|
||||||
// set up delete button
|
// set up delete button
|
||||||
layout.deleteButton.setOnClickListener {
|
layout.deleteButton.setOnClickListener {
|
||||||
val dialogMessage = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${layout.trackNameView.text}"
|
val dialogMessage = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n${layout.trackNameView.text}"
|
||||||
YesNoDialog(this@TrackFragment as YesNoDialog.YesNoDialogListener).show(
|
YesNoDialog(this@TrackFragment as YesNoDialog.YesNoDialogListener).show(
|
||||||
context = activity as Context,
|
context = activity as Context,
|
||||||
type = Keys.DIALOG_DELETE_TRACK,
|
type = Keys.DIALOG_DELETE_TRACK,
|
||||||
|
@ -72,10 +71,6 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
yesButton = R.string.dialog_yes_no_positive_button_delete_recording
|
yesButton = R.string.dialog_yes_no_positive_button_delete_recording
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// set up rename button
|
|
||||||
layout.editButton.setOnClickListener {
|
|
||||||
RenameTrackDialog(this as RenameTrackDialog.RenameTrackListener).show(activity as Context, layout.trackNameView.text.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
return layout.rootView
|
return layout.rootView
|
||||||
}
|
}
|
||||||
|
@ -135,7 +130,6 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
/* Opens up a file picker to select the save location */
|
/* Opens up a file picker to select the save location */
|
||||||
private fun openSaveGpxDialog()
|
private fun openSaveGpxDialog()
|
||||||
{
|
{
|
||||||
val context = this.activity as Context
|
|
||||||
val export_name: String = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(layout.track.start_time) + " " + layout.track.device_id + Keys.GPX_FILE_EXTENSION
|
val export_name: String = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(layout.track.start_time) + " " + layout.track.device_id + Keys.GPX_FILE_EXTENSION
|
||||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
|
|
@ -31,7 +31,6 @@ import android.location.Location
|
||||||
import android.location.LocationListener
|
import android.location.LocationListener
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ContentValues
|
|
||||||
import android.os.*
|
import android.os.*
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
@ -60,8 +59,9 @@ class TrackerService: Service(), SensorEventListener
|
||||||
var currentBestLocation: Location = getDefaultLocation()
|
var currentBestLocation: Location = getDefaultLocation()
|
||||||
var lastCommit: Date = Keys.DEFAULT_DATE
|
var lastCommit: Date = Keys.DEFAULT_DATE
|
||||||
var location_min_time_ms: Long = 0
|
var location_min_time_ms: Long = 0
|
||||||
|
private val RECENT_TRKPT_COUNT = 7200
|
||||||
var stepCountOffset: Float = 0f
|
var stepCountOffset: Float = 0f
|
||||||
lateinit var track: Track
|
lateinit var recent_trkpts: Deque<Trkpt>
|
||||||
var gpsLocationListenerRegistered: Boolean = false
|
var gpsLocationListenerRegistered: Boolean = false
|
||||||
var networkLocationListenerRegistered: Boolean = false
|
var networkLocationListenerRegistered: Boolean = false
|
||||||
var bound: Boolean = false
|
var bound: Boolean = false
|
||||||
|
@ -170,10 +170,6 @@ class TrackerService: Service(), SensorEventListener
|
||||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive = isNetworkEnabled(locationManager)
|
LocationManager.NETWORK_PROVIDER -> networkProviderActive = isNetworkEnabled(locationManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?)
|
|
||||||
{
|
|
||||||
// deprecated method
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,9 +207,9 @@ class TrackerService: Service(), SensorEventListener
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
trackbook = (applicationContext as Trackbook)
|
trackbook = (applicationContext as Trackbook)
|
||||||
trackbook.load_homepoints()
|
trackbook.load_homepoints()
|
||||||
|
recent_trkpts = ArrayDeque<Trkpt>(RECENT_TRKPT_COUNT)
|
||||||
gpsOnly = PreferencesHelper.loadGpsOnly()
|
gpsOnly = PreferencesHelper.loadGpsOnly()
|
||||||
device_id = PreferencesHelper.load_device_id()
|
device_id = PreferencesHelper.load_device_id()
|
||||||
track = Track(trackbook.database, device_id, start_time=GregorianCalendar.getInstance().time, stop_time=Date(GregorianCalendar.getInstance().time.time + 86400))
|
|
||||||
useImperial = PreferencesHelper.loadUseImperialUnits()
|
useImperial = PreferencesHelper.loadUseImperialUnits()
|
||||||
omitRests = PreferencesHelper.loadOmitRests()
|
omitRests = PreferencesHelper.loadOmitRests()
|
||||||
commitInterval = PreferencesHelper.loadCommitInterval()
|
commitInterval = PreferencesHelper.loadCommitInterval()
|
||||||
|
@ -445,11 +441,11 @@ class TrackerService: Service(), SensorEventListener
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (track.trkpts.isEmpty())
|
if (recent_trkpts.isEmpty())
|
||||||
{
|
{
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (! isDifferentEnough(track.trkpts.last().toLocation(), location, omitRests))
|
if (! isDifferentEnough(recent_trkpts.last().toLocation(), location, omitRests))
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "Omitting due to too close to previous.")
|
Log.i("VOUSSOIR", "Omitting due to too close to previous.")
|
||||||
return false
|
return false
|
||||||
|
@ -466,11 +462,11 @@ class TrackerService: Service(), SensorEventListener
|
||||||
if (should_keep_point((currentBestLocation)))
|
if (should_keep_point((currentBestLocation)))
|
||||||
{
|
{
|
||||||
trackbook.database.insert_trkpt(device_id, trkpt)
|
trackbook.database.insert_trkpt(device_id, trkpt)
|
||||||
track.trkpts.add(trkpt)
|
recent_trkpts.add(trkpt)
|
||||||
|
|
||||||
while (track.trkpts.size > 7200)
|
while (recent_trkpts.size > RECENT_TRKPT_COUNT)
|
||||||
{
|
{
|
||||||
track.trkpts.removeFirst()
|
recent_trkpts.removeFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (now.time - lastCommit.time > Keys.SAVE_TEMP_TRACK_INTERVAL)
|
if (now.time - lastCommit.time > Keys.SAVE_TEMP_TRACK_INTERVAL)
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* RenameTrackDialog.kt
|
|
||||||
* Implements the RenameTrackDialog class
|
|
||||||
* A RenameTrackDialog offers user to change name of track
|
|
||||||
*
|
|
||||||
* This file is part of
|
|
||||||
* TRACKBOOK - Movement Recorder for Android
|
|
||||||
*
|
|
||||||
* Copyright (c) 2016-22 - Y20K.org
|
|
||||||
* Licensed under the MIT-License
|
|
||||||
* http://opensource.org/licenses/MIT
|
|
||||||
*
|
|
||||||
* Trackbook uses osmdroid - OpenStreetMap-Tools for Android
|
|
||||||
* https://github.com/osmdroid/osmdroid
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.y20k.trackbook.dialogs
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.text.InputType
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.TextView
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import org.y20k.trackbook.R
|
|
||||||
import org.y20k.trackbook.helpers.LogHelper
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RenameTrackDialog class
|
|
||||||
*/
|
|
||||||
class RenameTrackDialog (private var renameTrackListener: RenameTrackListener) {
|
|
||||||
|
|
||||||
/* Interface used to communicate back to activity */
|
|
||||||
interface RenameTrackListener {
|
|
||||||
fun onRenameTrackDialog(textInput: String) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Define log tag */
|
|
||||||
private val TAG = LogHelper.makeLogTag(RenameTrackDialog::class.java.simpleName)
|
|
||||||
|
|
||||||
/* Construct and show dialog */
|
|
||||||
fun show(context: Context, trackName: String) {
|
|
||||||
// prepare dialog builder
|
|
||||||
val builder: MaterialAlertDialogBuilder = MaterialAlertDialogBuilder(context, R.style.AlertDialogTheme)
|
|
||||||
|
|
||||||
// get input field
|
|
||||||
val inflater = LayoutInflater.from(context)
|
|
||||||
val view = inflater.inflate(R.layout.dialog_rename_track, null)
|
|
||||||
val inputField = view.findViewById<EditText>(R.id.dialog_rename_track_input_edit_text)
|
|
||||||
|
|
||||||
// pre-fill with current track name
|
|
||||||
inputField.setText(trackName, TextView.BufferType.EDITABLE)
|
|
||||||
inputField.setSelection(trackName.length)
|
|
||||||
inputField.inputType = InputType.TYPE_CLASS_TEXT
|
|
||||||
inputField.requestFocus()
|
|
||||||
|
|
||||||
// set dialog view
|
|
||||||
builder.setView(view)
|
|
||||||
|
|
||||||
// add "add" button
|
|
||||||
builder.setPositiveButton(R.string.dialog_rename_track_button) { _, _ ->
|
|
||||||
// hand text over to initiating activity
|
|
||||||
inputField.text?.let {
|
|
||||||
var newStationName: String = it.toString()
|
|
||||||
if (newStationName.isEmpty()) newStationName = trackName
|
|
||||||
renameTrackListener.onRenameTrackDialog(newStationName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add cancel button
|
|
||||||
builder.setNegativeButton(R.string.dialog_generic_button_cancel) { _, _ ->
|
|
||||||
// listen for click on cancel button
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
// display add dialog
|
|
||||||
builder.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -31,30 +31,26 @@ import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
|
||||||
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
|
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
|
||||||
import org.y20k.trackbook.Keys
|
import org.y20k.trackbook.Keys
|
||||||
import org.y20k.trackbook.R
|
import org.y20k.trackbook.R
|
||||||
import org.y20k.trackbook.Track
|
|
||||||
import org.y20k.trackbook.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.*
|
||||||
|
|
||||||
/* Creates icon overlay for track */
|
/* Creates icon overlay for track */
|
||||||
fun createTrackOverlay(context: Context, map_view: MapView, track: Track, trackingState: Int)
|
fun createTrackOverlay(context: Context, map_view: MapView, trkpts: Collection<Trkpt>, trackingState: Int)
|
||||||
{
|
{
|
||||||
// get marker color
|
|
||||||
val color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.default_red) else context.getColor(R.color.default_blue)
|
val color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.default_red) else context.getColor(R.color.default_blue)
|
||||||
// gather points for overlay
|
|
||||||
val points: MutableList<IGeoPoint> = mutableListOf()
|
val points: MutableList<IGeoPoint> = mutableListOf()
|
||||||
track.trkpts.forEach { wayPoint ->
|
trkpts.forEach { trkpt ->
|
||||||
val label: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(wayPoint.time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(wayPoint.accuracy)} (${wayPoint.provider})"
|
val label = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(trkpt.time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(trkpt.accuracy)} (${trkpt.provider})"
|
||||||
// only add normal points
|
// only add normal points
|
||||||
if (!wayPoint.starred)
|
if (!trkpt.starred)
|
||||||
{
|
{
|
||||||
points.add(LabelledGeoPoint(wayPoint.latitude, wayPoint.longitude, wayPoint.altitude, label))
|
points.add(LabelledGeoPoint(trkpt.latitude, trkpt.longitude, trkpt.altitude, label))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val pointTheme: SimplePointTheme = SimplePointTheme(points, false)
|
val pointTheme: SimplePointTheme = SimplePointTheme(points, false)
|
||||||
// set styling for overlay
|
val style = Paint()
|
||||||
val style: Paint = Paint()
|
|
||||||
style.style = Paint.Style.FILL
|
style.style = Paint.Style.FILL
|
||||||
style.color = color
|
style.color = color
|
||||||
style.flags = Paint.ANTI_ALIAS_FLAG
|
style.flags = Paint.ANTI_ALIAS_FLAG
|
||||||
|
@ -64,20 +60,20 @@ fun createTrackOverlay(context: Context, map_view: MapView, track: Track, tracki
|
||||||
.setSymbol(SimpleFastPointOverlayOptions.Shape.CIRCLE)
|
.setSymbol(SimpleFastPointOverlayOptions.Shape.CIRCLE)
|
||||||
.setPointStyle(style)
|
.setPointStyle(style)
|
||||||
.setRadius(6F * scalingFactor) // radius is set in px - scaling factor makes that display density independent (= dp)
|
.setRadius(6F * scalingFactor) // radius is set in px - scaling factor makes that display density independent (= dp)
|
||||||
.setIsClickable(true)
|
.setIsClickable(false)
|
||||||
.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.
|
.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.
|
||||||
val overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
|
val overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
|
||||||
map_view.overlays.add(overlay)
|
map_view.overlays.add(overlay)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Creates overlay containing start, stop, stopover and starred markers for track */
|
/* Creates overlay containing start, stop, stopover and starred markers for track */
|
||||||
fun createSpecialMakersTrackOverlay(context: Context, map_view: MapView, track: Track, trackingState: Int, displayStartEndMarker: Boolean = false)
|
fun createSpecialMakersTrackOverlay(context: Context, map_view: MapView, trkpts: Collection<Trkpt>, trackingState: Int, displayStartEndMarker: Boolean = false)
|
||||||
{
|
{
|
||||||
val overlayItems: ArrayList<OverlayItem> = ArrayList<OverlayItem>()
|
val overlayItems: ArrayList<OverlayItem> = ArrayList<OverlayItem>()
|
||||||
val trackingActive: Boolean = trackingState == Keys.STATE_TRACKING_ACTIVE
|
val trackingActive: Boolean = trackingState == Keys.STATE_TRACKING_ACTIVE
|
||||||
val maxIndex: Int = track.trkpts.size - 1
|
val maxIndex: Int = trkpts.size - 1
|
||||||
|
|
||||||
track.trkpts.forEachIndexed { index: Int, trkpt: Trkpt ->
|
trkpts.forEachIndexed { index: Int, trkpt: Trkpt ->
|
||||||
var overlayItem: OverlayItem? = null
|
var overlayItem: OverlayItem? = null
|
||||||
if (!trackingActive && index == 0 && displayStartEndMarker && trkpt.starred)
|
if (!trackingActive && index == 0 && displayStartEndMarker && trkpt.starred)
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,18 +37,14 @@ import org.osmdroid.events.ZoomEvent
|
||||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||||
import org.osmdroid.util.GeoPoint
|
import org.osmdroid.util.GeoPoint
|
||||||
import org.osmdroid.views.MapView
|
import org.osmdroid.views.MapView
|
||||||
import org.osmdroid.views.overlay.ItemizedIconOverlay
|
|
||||||
import org.osmdroid.views.overlay.OverlayItem
|
|
||||||
import org.osmdroid.views.overlay.TilesOverlay
|
import org.osmdroid.views.overlay.TilesOverlay
|
||||||
import org.osmdroid.views.overlay.compass.CompassOverlay
|
import org.osmdroid.views.overlay.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.Keys
|
import org.y20k.trackbook.Keys
|
||||||
import org.y20k.trackbook.R
|
import org.y20k.trackbook.R
|
||||||
import org.y20k.trackbook.Track
|
import org.y20k.trackbook.Track
|
||||||
import org.y20k.trackbook.TrackStatistics
|
import org.y20k.trackbook.TrackStatistics
|
||||||
import org.y20k.trackbook.helpers.*
|
import org.y20k.trackbook.helpers.*
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TrackFragmentLayoutHolder class
|
* TrackFragmentLayoutHolder class
|
||||||
|
@ -63,9 +59,8 @@ data class TrackFragmentLayoutHolder(
|
||||||
{
|
{
|
||||||
/* Main class variables */
|
/* Main class variables */
|
||||||
val rootView: View
|
val rootView: View
|
||||||
val shareButton: ImageButton
|
val save_track_button: ImageButton
|
||||||
val deleteButton: ImageButton
|
val deleteButton: ImageButton
|
||||||
val editButton: ImageButton
|
|
||||||
val trackNameView: MaterialTextView
|
val trackNameView: MaterialTextView
|
||||||
private val mapView: MapView
|
private val mapView: MapView
|
||||||
private var controller: IMapController
|
private var controller: IMapController
|
||||||
|
@ -87,17 +82,15 @@ data class TrackFragmentLayoutHolder(
|
||||||
private val positiveElevationView: MaterialTextView
|
private val positiveElevationView: MaterialTextView
|
||||||
private val negativeElevationView: MaterialTextView
|
private val negativeElevationView: MaterialTextView
|
||||||
private val elevationDataViews: Group
|
private val elevationDataViews: Group
|
||||||
private val trackManagementViews: Group
|
|
||||||
private val useImperialUnits: Boolean
|
private val useImperialUnits: Boolean
|
||||||
|
|
||||||
/* Init block */
|
init
|
||||||
init {
|
{
|
||||||
// find views
|
// find views
|
||||||
rootView = inflater.inflate(R.layout.fragment_track, container, false)
|
rootView = inflater.inflate(R.layout.fragment_track, container, false)
|
||||||
mapView = rootView.findViewById(R.id.map)
|
mapView = rootView.findViewById(R.id.map)
|
||||||
shareButton = rootView.findViewById(R.id.save_button)
|
save_track_button = rootView.findViewById(R.id.save_button)
|
||||||
deleteButton = rootView.findViewById(R.id.delete_button)
|
deleteButton = rootView.findViewById(R.id.delete_button)
|
||||||
editButton = rootView.findViewById(R.id.edit_button)
|
|
||||||
trackNameView = rootView.findViewById(R.id.statistics_track_name_headline)
|
trackNameView = rootView.findViewById(R.id.statistics_track_name_headline)
|
||||||
|
|
||||||
// basic map setup
|
// basic map setup
|
||||||
|
@ -128,7 +121,6 @@ data class TrackFragmentLayoutHolder(
|
||||||
positiveElevationView = rootView.findViewById(R.id.statistics_data_positive_elevation)
|
positiveElevationView = rootView.findViewById(R.id.statistics_data_positive_elevation)
|
||||||
negativeElevationView = rootView.findViewById(R.id.statistics_data_negative_elevation)
|
negativeElevationView = rootView.findViewById(R.id.statistics_data_negative_elevation)
|
||||||
elevationDataViews = rootView.findViewById(R.id.elevation_data)
|
elevationDataViews = rootView.findViewById(R.id.elevation_data)
|
||||||
trackManagementViews = rootView.findViewById(R.id.management_icons)
|
|
||||||
|
|
||||||
// get measurement unit system
|
// get measurement unit system
|
||||||
useImperialUnits = PreferencesHelper.loadUseImperialUnits()
|
useImperialUnits = PreferencesHelper.loadUseImperialUnits()
|
||||||
|
@ -146,14 +138,13 @@ data class TrackFragmentLayoutHolder(
|
||||||
mapView.overlays.add(compassOverlay)
|
mapView.overlays.add(compassOverlay)
|
||||||
|
|
||||||
if (track.trkpts.isNotEmpty()) {
|
if (track.trkpts.isNotEmpty()) {
|
||||||
createSpecialMakersTrackOverlay(context, mapView, track, Keys.STATE_TRACKING_STOPPED, displayStartEndMarker = true)
|
createSpecialMakersTrackOverlay(context, mapView, track.trkpts, Keys.STATE_TRACKING_STOPPED, displayStartEndMarker = true)
|
||||||
createTrackOverlay(context, mapView, track, Keys.STATE_TRACKING_STOPPED)
|
createTrackOverlay(context, mapView, track.trkpts, Keys.STATE_TRACKING_STOPPED)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up and show statistics sheet
|
// set up and show statistics sheet
|
||||||
statisticsSheetBehavior = BottomSheetBehavior.from<View>(statisticsSheet)
|
statisticsSheetBehavior = BottomSheetBehavior.from<View>(statisticsSheet)
|
||||||
statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||||
statisticsSheetBehavior.addBottomSheetCallback(getStatisticsSheetCallback())
|
|
||||||
setupStatisticsViews()
|
setupStatisticsViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,44 +178,17 @@ data class TrackFragmentLayoutHolder(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shows/hides the statistics sheet */
|
/* Shows/hides the statistics sheet */
|
||||||
private fun toggleStatisticsSheetVisibility() {
|
private fun toggleStatisticsSheetVisibility()
|
||||||
|
{
|
||||||
when (statisticsSheetBehavior.state) {
|
when (statisticsSheetBehavior.state) {
|
||||||
BottomSheetBehavior.STATE_EXPANDED -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
BottomSheetBehavior.STATE_EXPANDED -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||||
else -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
else -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Defines the behavior of the statistics sheet */
|
|
||||||
private fun getStatisticsSheetCallback(): BottomSheetBehavior.BottomSheetCallback {
|
|
||||||
return object : BottomSheetBehavior.BottomSheetCallback() {
|
|
||||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
|
||||||
when (newState) {
|
|
||||||
BottomSheetBehavior.STATE_EXPANDED -> {
|
|
||||||
trackManagementViews.isVisible = true
|
|
||||||
shareButton.isGone = true
|
|
||||||
// bottomSheet.setPadding(0,24,0,0)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
trackManagementViews.isGone = true
|
|
||||||
shareButton.isVisible = true
|
|
||||||
// bottomSheet.setPadding(0,0,0,0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
|
||||||
if (slideOffset < 0.125f) {
|
|
||||||
trackManagementViews.isGone = true
|
|
||||||
shareButton.isVisible = true
|
|
||||||
} else {
|
|
||||||
trackManagementViews.isVisible = true
|
|
||||||
shareButton.isGone = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Overrides onZoom from MapListener */
|
/* Overrides onZoom from MapListener */
|
||||||
override fun onZoom(event: ZoomEvent?): Boolean {
|
override fun onZoom(event: ZoomEvent?): Boolean
|
||||||
|
{
|
||||||
if (event == null) {
|
if (event == null) {
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
|
@ -234,7 +198,8 @@ data class TrackFragmentLayoutHolder(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Overrides onScroll from MapListener */
|
/* Overrides onScroll from MapListener */
|
||||||
override fun onScroll(event: ScrollEvent?): Boolean {
|
override fun onScroll(event: ScrollEvent?): Boolean
|
||||||
|
{
|
||||||
if (event == null) {
|
if (event == null) {
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,14 +15,6 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:constraint_referenced_ids="statistics_p_positive_elevation,statistics_data_positive_elevation,statistics_p_negative_elevation,statistics_data_negative_elevation,statistics_p_max_altitude,statistics_data_max_altitude,statistics_p_min_altitude,statistics_data_min_altitude" />
|
app:constraint_referenced_ids="statistics_p_positive_elevation,statistics_data_positive_elevation,statistics_p_negative_elevation,statistics_data_negative_elevation,statistics_p_max_altitude,statistics_data_max_altitude,statistics_p_min_altitude,statistics_data_min_altitude" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/management_icons"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:constraint_referenced_ids="delete_button,edit_button" />
|
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/statistics_track_name_headline"
|
android:id="@+id/statistics_track_name_headline"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
@ -35,7 +27,7 @@
|
||||||
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
|
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
|
||||||
android:textColor="@color/text_default"
|
android:textColor="@color/text_default"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/edit_button"
|
app:layout_constraintEnd_toStartOf="@+id/delete_button"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="@string/sample_text_track_name" />
|
tools:text="@string/sample_text_track_name" />
|
||||||
|
@ -46,21 +38,10 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:backgroundTint="@color/default_transparent"
|
android:backgroundTint="@color/default_transparent"
|
||||||
android:contentDescription="@string/descr_statistics_sheet_delete_button"
|
android:contentDescription="@string/descr_statistics_sheet_delete_button"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/edit_button"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/save_button"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/edit_button"
|
|
||||||
app:srcCompat="@drawable/ic_delete_24dp" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/edit_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:backgroundTint="@color/default_transparent"
|
|
||||||
android:contentDescription="@string/descr_statistics_sheet_edit_button"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/statistics_track_name_headline"
|
app:layout_constraintBottom_toBottomOf="@+id/statistics_track_name_headline"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/delete_button"
|
app:layout_constraintEnd_toStartOf="@+id/save_button"
|
||||||
app:layout_constraintTop_toTopOf="@+id/statistics_track_name_headline"
|
app:layout_constraintTop_toTopOf="@+id/statistics_track_name_headline"
|
||||||
app:srcCompat="@drawable/ic_edit_24dp" />
|
app:srcCompat="@drawable/ic_delete_24dp" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/save_button"
|
android:id="@+id/save_button"
|
||||||
|
@ -68,7 +49,6 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:backgroundTint="@color/default_transparent"
|
android:backgroundTint="@color/default_transparent"
|
||||||
android:contentDescription="@string/descr_statistics_sheet_save_button"
|
android:contentDescription="@string/descr_statistics_sheet_save_button"
|
||||||
android:visibility="visible"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/statistics_track_name_headline"
|
app:layout_constraintBottom_toBottomOf="@+id/statistics_track_name_headline"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/statistics_track_name_headline"
|
app:layout_constraintTop_toTopOf="@+id/statistics_track_name_headline"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<!-- please do not translate app_name - transcription into different alphabet types is fine though -->
|
<!-- please do not translate app_name - transcription into different alphabet types is fine though -->
|
||||||
<!-- Tabs -->
|
<!-- Tabs -->
|
||||||
<string name="tab_map">Map</string>
|
<string name="tab_map">Map</string>
|
||||||
<string name="tab_tracks">Tracks</string>
|
<string name="tab_tracks">History</string>
|
||||||
<string name="tab_settings">Settings</string>
|
<string name="tab_settings">Settings</string>
|
||||||
<!-- Notification -->
|
<!-- Notification -->
|
||||||
<string name="notification_title_trackbook_running">Trackbook running</string>
|
<string name="notification_title_trackbook_running">Trackbook running</string>
|
||||||
|
|
Loading…
Reference in a new issue