checkpoint

This commit is contained in:
voussoir 2023-03-11 16:05:37 -08:00
parent 172ca703a9
commit 62675e1b97
7 changed files with 169 additions and 35 deletions

View file

@ -47,6 +47,7 @@ class Database(trackbook: Trackbook)
fun insert_trkpt(device_id: String, trkpt: Trkpt) fun insert_trkpt(device_id: String, trkpt: Trkpt)
{ {
Log.i("VOUSSOIR", "Database.insert_trkpt")
val values = ContentValues().apply { val values = ContentValues().apply {
put("device_id", device_id) put("device_id", device_id)
put("lat", trkpt.latitude) put("lat", trkpt.latitude)
@ -63,6 +64,24 @@ class Database(trackbook: Trackbook)
connection.insert("trkpt", null, values) connection.insert("trkpt", null, values)
} }
fun insert_homepoint(name: String, latitude: Double, longitude: Double, radius: Double)
{
Log.i("VOUSSOIR", "Database.insert_homepoint")
val values = ContentValues().apply {
put("lat", latitude)
put("lon", longitude)
put("radius", radius)
put("name", name)
}
if (! connection.inTransaction())
{
connection.beginTransaction()
}
connection.insert("homepoints", null, values)
commit()
trackbook.load_homepoints()
}
private fun initialize_tables() private fun initialize_tables()
{ {
this.connection.beginTransaction() this.connection.beginTransaction()

View file

@ -17,6 +17,7 @@
package org.y20k.trackbook package org.y20k.trackbook
import android.Manifest import android.Manifest
import android.app.Dialog
import android.content.* import android.content.*
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Resources import android.content.res.Resources
@ -29,6 +30,9 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
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
@ -37,10 +41,12 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import org.osmdroid.api.IMapController import org.osmdroid.api.IMapController
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
import org.osmdroid.views.MapView import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.ItemizedIconOverlay import org.osmdroid.views.overlay.ItemizedIconOverlay
import org.osmdroid.views.overlay.MapEventsOverlay
import org.osmdroid.views.overlay.Overlay import org.osmdroid.views.overlay.Overlay
import org.osmdroid.views.overlay.OverlayItem import org.osmdroid.views.overlay.OverlayItem
import org.osmdroid.views.overlay.Polygon import org.osmdroid.views.overlay.Polygon
@ -70,7 +76,7 @@ class MapFragment : Fragment()
private lateinit var trackbook: Trackbook private lateinit var trackbook: Trackbook
lateinit var rootView: View lateinit var rootView: View
var userInteraction: Boolean = false var continuous_auto_center: Boolean = true
lateinit var currentLocationButton: FloatingActionButton lateinit var currentLocationButton: FloatingActionButton
lateinit var zoom_in_button: FloatingActionButton lateinit var zoom_in_button: FloatingActionButton
lateinit var zoom_out_button: FloatingActionButton lateinit var zoom_out_button: FloatingActionButton
@ -124,6 +130,12 @@ class MapFragment : Fragment()
mainButton = rootView.findViewById(R.id.main_button) mainButton = rootView.findViewById(R.id.main_button)
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE) locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
mapView.setOnLongClickListener{
Log.i("VOUSSOIR", "mapview longpress")
true
}
mapView.isLongClickable = true
// basic map setup // basic map setup
controller = mapView.controller controller = mapView.controller
mapView.isTilesScaledToDpi = true mapView.isTilesScaledToDpi = true
@ -133,16 +145,13 @@ class MapFragment : Fragment()
zoomLevel = PreferencesHelper.loadZoomLevel() zoomLevel = PreferencesHelper.loadZoomLevel()
controller.setZoom(zoomLevel) controller.setZoom(zoomLevel)
// set dark map tiles, if necessary
if (AppThemeHelper.isDarkModeOn(requireActivity())) if (AppThemeHelper.isDarkModeOn(requireActivity()))
{ {
mapView.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS) mapView.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS)
} }
// store Density Scaling Factor
val densityScalingFactor: Float = UiHelper.getDensityScalingFactor(requireContext()) val densityScalingFactor: Float = UiHelper.getDensityScalingFactor(requireContext())
// add compass to map
val compassOverlay = CompassOverlay(requireContext(), InternalCompassOrientationProvider(requireContext()), mapView) val compassOverlay = CompassOverlay(requireContext(), InternalCompassOrientationProvider(requireContext()), mapView)
compassOverlay.enableCompass() compassOverlay.enableCompass()
// compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / densityScalingFactor)) // TODO uncomment when transparent status bar is re-implemented // compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / densityScalingFactor)) // TODO uncomment when transparent status bar is re-implemented
@ -167,8 +176,47 @@ class MapFragment : Fragment()
// initialize main button state // initialize main button state
update_main_button() update_main_button()
// listen for user interaction mapView.setOnTouchListener { v, event ->
addInteractionListener() continuous_auto_center = false
false
}
val receiver: MapEventsReceiver = object: MapEventsReceiver
{
override fun singleTapConfirmedHelper(p: GeoPoint?): Boolean
{
return true
}
override fun longPressHelper(point: GeoPoint): Boolean
{
Log.i("VOUSSOIR", "MapFragment MapEventsReceiver.longPressHelper")
val dialog = Dialog(activity as Context)
dialog.setContentView(R.layout.dialog_homepoint)
dialog.setTitle("Homepoint")
(dialog.findViewById(R.id.homepoint_dialog_title) as TextView).text = "Add a homepoint"
val name_input: EditText = dialog.findViewById(R.id.homepoint_name_input)
val radius_input: EditText = dialog.findViewById(R.id.homepoint_radius_input)
val cancel_button: Button = dialog.findViewById(R.id.homepoint_delete_cancel_button)
val save_button: Button = dialog.findViewById(R.id.homepoint_save_button)
cancel_button.text = "Cancel"
cancel_button.setOnClickListener {
dialog.cancel()
}
save_button.setOnClickListener {
app.database.insert_homepoint(name=name_input.text.toString(), latitude=point.latitude, longitude=point.longitude, radius=radius_input.text.toString().toDouble())
app.load_homepoints()
create_homepoint_overlays(requireContext(), mapView, app.homepoints)
dialog.dismiss()
}
dialog.show()
return true
}
}
mapView.overlays.add(MapEventsOverlay(receiver))
// set up buttons // set up buttons
mainButton.setOnClickListener { mainButton.setOnClickListener {
@ -338,28 +386,19 @@ class MapFragment : Fragment()
} }
} }
private fun addInteractionListener()
{
mapView.setOnTouchListener { v, event ->
userInteraction = true
false
}
}
fun centerMap(location: Location, animated: Boolean = false) { fun centerMap(location: Location, animated: Boolean = false) {
val position = GeoPoint(location.latitude, location.longitude) val position = GeoPoint(location.latitude, location.longitude)
when (animated) { when (animated) {
true -> controller.animateTo(position) true -> controller.animateTo(position)
false -> controller.setCenter(position) false -> controller.setCenter(position)
} }
userInteraction = false continuous_auto_center = true
} }
fun saveBestLocationState(currentBestLocation: Location) { fun saveBestLocationState(currentBestLocation: Location) {
PreferencesHelper.saveCurrentBestLocation(currentBestLocation) PreferencesHelper.saveCurrentBestLocation(currentBestLocation)
PreferencesHelper.saveZoomLevel(mapView.zoomLevelDouble) PreferencesHelper.saveZoomLevel(mapView.zoomLevelDouble)
// reset user interaction state continuous_auto_center = true
userInteraction = false
} }
fun clear_current_position_overlays() fun clear_current_position_overlays()
@ -377,7 +416,7 @@ class MapFragment : Fragment()
/* Mark current position on map */ /* Mark current position on map */
fun create_current_position_overlays(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED) fun create_current_position_overlays(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED)
{ {
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.markCurrentPosition") // Log.i("VOUSSOIR", "MapFragmentLayoutHolder.markCurrentPosition")
clear_current_position_overlays() clear_current_position_overlays()
@ -555,7 +594,7 @@ class MapFragment : Fragment()
create_current_position_overlays(currentBestLocation, trackingState) create_current_position_overlays(currentBestLocation, trackingState)
create_current_track_overlay(track, trackingState) create_current_track_overlay(track, trackingState)
// center map, if it had not been dragged/zoomed before // center map, if it had not been dragged/zoomed before
if (!userInteraction) if (continuous_auto_center)
{ {
centerMap(currentBestLocation, true) centerMap(currentBestLocation, true)
} }

View file

@ -161,7 +161,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
PreferencesHelper.save_database_folder(path) PreferencesHelper.save_database_folder(path)
preferenceDatabaseFolder.summary = (getString(R.string.pref_database_folder_summary) + "\n" + path).trim() preferenceDatabaseFolder.summary = (getString(R.string.pref_database_folder_summary) + "\n" + path).trim()
} }
preferenceDatabaseFolder.title = "Database Directory" preferenceDatabaseFolder.title = getString(R.string.pref_database_folder)
preferenceDatabaseFolder.setIcon(R.drawable.ic_save_to_storage_24dp) preferenceDatabaseFolder.setIcon(R.drawable.ic_save_to_storage_24dp)
preferenceDatabaseFolder.key = Keys.PREF_DATABASE_DIRECTORY preferenceDatabaseFolder.key = Keys.PREF_DATABASE_DIRECTORY
preferenceDatabaseFolder.summary = (getString(R.string.pref_database_folder_summary) + "\n" + PreferencesHelper.load_database_folder()).trim() preferenceDatabaseFolder.summary = (getString(R.string.pref_database_folder_summary) + "\n" + PreferencesHelper.load_database_folder()).trim()
@ -175,7 +175,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
return@setOnPreferenceClickListener true return@setOnPreferenceClickListener true
} }
preferenceDatabaseFolder.setOnPreferenceChangeListener { preference, newValue -> preferenceDatabaseFolder.setOnPreferenceChangeListener { preference, newValue ->
preferenceDatabaseFolder.summary = "Directory to contain your database file." + "\n" + newValue preferenceDatabaseFolder.summary = getString(R.string.pref_database_folder_summary) + "\n" + newValue
return@setOnPreferenceChangeListener true return@setOnPreferenceChangeListener true
} }

View file

@ -29,6 +29,7 @@ import org.osmdroid.api.IGeoPoint
import org.osmdroid.util.GeoPoint import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.ItemizedIconOverlay import org.osmdroid.views.overlay.ItemizedIconOverlay
import org.osmdroid.views.overlay.MapEventsOverlay
import org.osmdroid.views.overlay.OverlayItem import org.osmdroid.views.overlay.OverlayItem
import org.osmdroid.views.overlay.simplefastpoint.LabelledGeoPoint import org.osmdroid.views.overlay.simplefastpoint.LabelledGeoPoint
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
@ -125,11 +126,10 @@ fun createSpecialMakersTrackOverlay(context: Context, map_view: MapView, track:
fun createOverlayItem(context: Context, latitude: Double, longitude: Double, accuracy: Float, provider: String, time: Long): OverlayItem fun createOverlayItem(context: Context, latitude: Double, longitude: Double, accuracy: Float, provider: String, time: Long): OverlayItem
{ {
val title: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)}" val title = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)}"
//val description: String = "${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(accuracy)} (${provider})" val description = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(accuracy)} (${provider})"
val description: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(accuracy)} (${provider})" val position = GeoPoint(latitude, longitude)
val position: GeoPoint = GeoPoint(latitude, longitude) val item = OverlayItem(title, description, position)
val item: OverlayItem = OverlayItem(title, description, position)
item.markerHotspot = OverlayItem.HotspotPlace.CENTER item.markerHotspot = OverlayItem.HotspotPlace.CENTER
return item return item
} }

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp">
<TextView
android:id="@+id/homepoint_dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Homepoint"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/homepoint_name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/homepoint_dialog_title" />
<EditText
android:id="@+id/homepoint_name_input"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:minHeight="48dp"
android:text=""
app:layout_constraintStart_toStartOf="@+id/homepoint_name_label"
app:layout_constraintTop_toBottomOf="@+id/homepoint_name_label" />
<TextView
android:id="@+id/homepoint_radius_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Radius"
app:layout_constraintStart_toStartOf="@+id/homepoint_name_input"
app:layout_constraintTop_toBottomOf="@+id/homepoint_name_input" />
<EditText
android:id="@+id/homepoint_radius_input"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:minHeight="48dp"
app:layout_constraintStart_toStartOf="@+id/homepoint_radius_label"
app:layout_constraintTop_toBottomOf="@+id/homepoint_radius_label" />
<Button
android:id="@+id/homepoint_save_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Save"
app:cornerRadius="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/homepoint_radius_input" />
<Button
android:id="@+id/homepoint_delete_cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Delete"
app:cornerRadius="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/homepoint_radius_input" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -7,20 +7,19 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".MapFragment"> tools:context=".MapFragment">
<!-- MAP -->
<org.osmdroid.views.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/descr_map_current_track"
android:visibility="visible" />
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container" android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_dodgeInsetEdges="bottom"> app:layout_dodgeInsetEdges="bottom">
<org.osmdroid.views.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/descr_map_current_track"
android:visibility="visible" />
<!-- BUTTON SAVE --> <!-- BUTTON SAVE -->
<!-- BUTTON CLEAR --> <!-- BUTTON CLEAR -->

View file

@ -86,9 +86,10 @@
<string name="pref_altitude_smoothing_value_title" translatable="false">Altitude Smoothing</string> <string name="pref_altitude_smoothing_value_title" translatable="false">Altitude Smoothing</string>
<string name="pref_auto_export_interval_summary">Automatically export GPX file after this many hours.</string> <string name="pref_auto_export_interval_summary">Automatically export GPX file after this many hours.</string>
<string name="pref_device_id_summary">A unique ID to distinguish tracks recorded across multiple devices:</string> <string name="pref_device_id_summary">A unique ID to distinguish tracks recorded across multiple devices:</string>
<string name="pref_database_folder_summary">Directory to contain your database file:</string> <string name="pref_database_folder_summary">Directory to contain your database file. You could use Syncthing to sync with your PC!</string>
<string name="pref_auto_export_interval_title">Auto Export Interval</string> <string name="pref_auto_export_interval_title">Auto Export Interval</string>
<string name="pref_device_id">Device ID</string> <string name="pref_device_id">Device ID</string>
<string name="pref_database_folder">Database directory</string>
<string name="pref_advanced_title">Advanced</string> <string name="pref_advanced_title">Advanced</string>
<string name="pref_delete_non_starred_summary">Delete all recordings in \"Tracks\" that are not starred.</string> <string name="pref_delete_non_starred_summary">Delete all recordings in \"Tracks\" that are not starred.</string>
<string name="pref_delete_non_starred_title">Delete Non-Starred Recordings</string> <string name="pref_delete_non_starred_title">Delete Non-Starred Recordings</string>