new feature: you can mark a recorded waypoint as POI see #71
This commit is contained in:
parent
6290cbd709
commit
18d043cafe
18 changed files with 120 additions and 209 deletions
|
@ -10,7 +10,7 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId 'org.y20k.trackbook'
|
applicationId 'org.y20k.trackbook'
|
||||||
minSdkVersion 25
|
minSdkVersion 25
|
||||||
targetSdkVersion 27
|
targetSdkVersion 29
|
||||||
versionCode 42
|
versionCode 42
|
||||||
versionName '2.0.5'
|
versionName '2.0.5'
|
||||||
resConfigs "en", "da", "de", "fr", "hr", "id", "it", "ja", "nb-rNO", "nl", "sv", "zh-rCN"
|
resConfigs "en", "da", "de", "fr", "hr", "id", "it", "ja", "nb-rNO", "nl", "sv", "zh-rCN"
|
||||||
|
|
|
@ -30,7 +30,6 @@ import androidx.preference.PreferenceManager
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
import org.osmdroid.config.Configuration
|
import org.osmdroid.config.Configuration
|
||||||
import org.y20k.trackbook.helpers.AppThemeHelper
|
import org.y20k.trackbook.helpers.AppThemeHelper
|
||||||
import org.y20k.trackbook.helpers.ImportHelper
|
|
||||||
import org.y20k.trackbook.helpers.LogHelper
|
import org.y20k.trackbook.helpers.LogHelper
|
||||||
import org.y20k.trackbook.helpers.PreferencesHelper
|
import org.y20k.trackbook.helpers.PreferencesHelper
|
||||||
|
|
||||||
|
@ -91,12 +90,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert old tracks (one-time import)
|
|
||||||
if (PreferencesHelper.isHouseKeepingNecessary(this)) {
|
|
||||||
ImportHelper.convertOldTracks(this)
|
|
||||||
PreferencesHelper.saveHouseKeepingNecessaryState(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
// register listener for changes in shared preferences
|
// register listener for changes in shared preferences
|
||||||
PreferenceManager.getDefaultSharedPreferences(this as Context).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferenceManager.getDefaultSharedPreferences(this as Context).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ import org.y20k.trackbook.ui.MapFragmentLayoutHolder
|
||||||
/*
|
/*
|
||||||
* MapFragment class
|
* MapFragment class
|
||||||
*/
|
*/
|
||||||
class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener {
|
class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlay.MarkerListener {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(MapFragment::class.java)
|
private val TAG: String = LogHelper.makeLogTag(MapFragment::class.java)
|
||||||
|
@ -75,7 +75,7 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener {
|
||||||
/* Overrides onStop from Fragment */
|
/* Overrides onStop from Fragment */
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
// initialize layout
|
// initialize layout
|
||||||
layout = MapFragmentLayoutHolder(activity as Context, inflater, container, currentBestLocation, trackingState)
|
layout = MapFragmentLayoutHolder(activity as Context, this as MapOverlay.MarkerListener, inflater, container, currentBestLocation, trackingState)
|
||||||
|
|
||||||
// set up buttons
|
// set up buttons
|
||||||
layout.currentLocationButton.setOnClickListener {
|
layout.currentLocationButton.setOnClickListener {
|
||||||
|
@ -178,6 +178,17 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Overrides onMarkerTapped from MarkerListener */
|
||||||
|
override fun onMarkerTapped(latitude: Double, longitude: Double) {
|
||||||
|
super.onMarkerTapped(latitude, longitude)
|
||||||
|
if (bound) {
|
||||||
|
track = TrackHelper.toggleStarred(activity as Context, track, latitude, longitude)
|
||||||
|
layout.overlayCurrentTrack(track, trackingState)
|
||||||
|
trackerService.track = track
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Start tracker service */
|
/* Start tracker service */
|
||||||
private fun startTrackerService() {
|
private fun startTrackerService() {
|
||||||
val intent = Intent(activity, TrackerService::class.java)
|
val intent = Intent(activity, TrackerService::class.java)
|
||||||
|
|
|
@ -41,10 +41,12 @@ import org.y20k.trackbook.core.Track
|
||||||
import org.y20k.trackbook.dialogs.RenameTrackDialog
|
import org.y20k.trackbook.dialogs.RenameTrackDialog
|
||||||
import org.y20k.trackbook.helpers.FileHelper
|
import org.y20k.trackbook.helpers.FileHelper
|
||||||
import org.y20k.trackbook.helpers.LogHelper
|
import org.y20k.trackbook.helpers.LogHelper
|
||||||
|
import org.y20k.trackbook.helpers.MapOverlay
|
||||||
|
import org.y20k.trackbook.helpers.TrackHelper
|
||||||
import org.y20k.trackbook.ui.TrackFragmentLayoutHolder
|
import org.y20k.trackbook.ui.TrackFragmentLayoutHolder
|
||||||
|
|
||||||
|
|
||||||
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDialog.YesNoDialogListener {
|
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDialog.YesNoDialogListener, MapOverlay.MarkerListener {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TrackFragment::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TrackFragment::class.java)
|
||||||
|
@ -71,7 +73,7 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
/* Overrides onCreateView from Fragment */
|
/* Overrides onCreateView from Fragment */
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
// initialize layout
|
// initialize layout
|
||||||
layout = TrackFragmentLayoutHolder(activity as Context, inflater, container, track)
|
layout = TrackFragmentLayoutHolder(activity as Context, this as MapOverlay.MarkerListener, inflater, container, track)
|
||||||
|
|
||||||
// set up share button
|
// set up share button
|
||||||
layout.shareButton.setOnClickListener {
|
layout.shareButton.setOnClickListener {
|
||||||
|
@ -162,6 +164,19 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Overrides onMarkerTapped from MarkerListener */
|
||||||
|
override fun onMarkerTapped(latitude: Double, longitude: Double) {
|
||||||
|
super.onMarkerTapped(latitude, longitude)
|
||||||
|
// update track display
|
||||||
|
track = TrackHelper.toggleStarred(activity as Context, track, latitude, longitude)
|
||||||
|
layout.updateTrackOverlay(track)
|
||||||
|
// save track
|
||||||
|
GlobalScope.launch {
|
||||||
|
FileHelper.saveTrackSuspended(track, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 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 intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||||
|
@ -182,7 +197,7 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
/* Share track as GPX via share sheet */
|
/* Share track as GPX via share sheet */
|
||||||
private fun shareGpxTrack() {
|
private fun shareGpxTrack() {
|
||||||
val gpxFile = Uri.parse(layout.track.gpxUriString).toFile()
|
val gpxFile = Uri.parse(layout.track.gpxUriString).toFile()
|
||||||
val gpxShareUri = FileProvider.getUriForFile(this.activity as Context, "${activity!!.applicationContext.packageName}.provider", gpxFile)
|
val gpxShareUri = FileProvider.getUriForFile(this.activity as Context, "${requireActivity().applicationContext.packageName}.provider", gpxFile)
|
||||||
val shareIntent: Intent = Intent.createChooser(Intent().apply {
|
val shareIntent: Intent = Intent.createChooser(Intent().apply {
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
data = gpxShareUri
|
data = gpxShareUri
|
||||||
|
|
|
@ -37,7 +37,8 @@ data class WayPoint(@Expose val provider: String,
|
||||||
@Expose val time: Long,
|
@Expose val time: Long,
|
||||||
@Expose val distanceToStartingPoint: Float = 0f,
|
@Expose val distanceToStartingPoint: Float = 0f,
|
||||||
@Expose val numberSatellites: Int = 0,
|
@Expose val numberSatellites: Int = 0,
|
||||||
@Expose var isStopOver: Boolean = false): Parcelable {
|
@Expose var isStopOver: Boolean = false,
|
||||||
|
@Expose var starred: Boolean = false): Parcelable {
|
||||||
|
|
||||||
|
|
||||||
/* Converts WayPoint into Location */
|
/* Converts WayPoint into Location */
|
||||||
|
|
|
@ -1,168 +0,0 @@
|
||||||
/*
|
|
||||||
* ImportHelper.kt
|
|
||||||
* Implements the ImportHelper object
|
|
||||||
* A ImportHelper manages the one-time import of old .trackbook files
|
|
||||||
*
|
|
||||||
* This file is part of
|
|
||||||
* TRACKBOOK - Movement Recorder for Android
|
|
||||||
*
|
|
||||||
* Copyright (c) 2016-20 - 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.helpers
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.location.Location
|
|
||||||
import androidx.annotation.Keep
|
|
||||||
import com.google.gson.GsonBuilder
|
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.y20k.trackbook.Keys
|
|
||||||
import org.y20k.trackbook.core.Track
|
|
||||||
import org.y20k.trackbook.core.WayPoint
|
|
||||||
import java.io.BufferedReader
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.InputStreamReader
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ImportHelper data class
|
|
||||||
*/
|
|
||||||
object ImportHelper {
|
|
||||||
|
|
||||||
|
|
||||||
/* Define log tag */
|
|
||||||
private val TAG: String = LogHelper.makeLogTag(ImportHelper::class.java)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Converts older tracks of type .trackbook into the new format */
|
|
||||||
fun convertOldTracks(context: Context) {
|
|
||||||
val oldTracks: ArrayList<Track> = arrayListOf()
|
|
||||||
val trackFolder: File? = context.getExternalFilesDir(Keys.FOLDER_TRACKS)
|
|
||||||
|
|
||||||
if (trackFolder != null && trackFolder.exists() && trackFolder.isDirectory) {
|
|
||||||
trackFolder.listFiles()?.forEach { file ->
|
|
||||||
if (file.name.endsWith(".trackbook")) {
|
|
||||||
// read until last line reached
|
|
||||||
val stream: InputStream = file.inputStream()
|
|
||||||
val reader: BufferedReader = BufferedReader(InputStreamReader(stream))
|
|
||||||
val builder: StringBuilder = StringBuilder()
|
|
||||||
reader.forEachLine {
|
|
||||||
builder.append(it)
|
|
||||||
builder.append("\n") }
|
|
||||||
stream.close()
|
|
||||||
// get content of file
|
|
||||||
val fileContent: String = builder.toString()
|
|
||||||
// get LegacyTrack from JSON
|
|
||||||
val gsonBuilder = GsonBuilder()
|
|
||||||
gsonBuilder.setDateFormat("M/d/yy hh:mm a")
|
|
||||||
val oldTrack: LegacyTrack = gsonBuilder.create().fromJson(fileContent, LegacyTrack::class.java)
|
|
||||||
oldTracks.add(oldTrack.toTrack())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// save track using "deferred await"
|
|
||||||
if (oldTracks.isNotEmpty()) {
|
|
||||||
GlobalScope.launch {
|
|
||||||
oldTracks.forEach { oldTrack ->
|
|
||||||
// step 1: create and store filenames for json and gpx files
|
|
||||||
oldTrack.trackUriString = FileHelper.getTrackFileUri(context, oldTrack).toString()
|
|
||||||
oldTrack.gpxUriString = FileHelper.getGpxFileUri(context, oldTrack).toString()
|
|
||||||
// step 2: save track
|
|
||||||
FileHelper.saveTrackSuspended(oldTrack, saveGpxToo = true)
|
|
||||||
// step 3: save tracklist
|
|
||||||
FileHelper.addTrackAndSaveTracklistSuspended(context, oldTrack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Inner class: Legacy version of Track - used for one-time import only
|
|
||||||
* Warning: Works only as long as targetSdkVersion < 28
|
|
||||||
*/
|
|
||||||
@Keep
|
|
||||||
private data class LegacyTrack (
|
|
||||||
@SerializedName("b") var mTrackFormatVersion: Int = 0,
|
|
||||||
@SerializedName("c") var mWayPoints: List<LegacyWayPoint>,
|
|
||||||
@SerializedName("d") var mTrackLength: Float = 0f,
|
|
||||||
@SerializedName("e") var mDuration: Long = 0,
|
|
||||||
@SerializedName("f") var mStepCount: Float = 0f,
|
|
||||||
@SerializedName("g") var mRecordingStart: Date = GregorianCalendar.getInstance().time,
|
|
||||||
@SerializedName("h") var mRecordingStop: Date = mRecordingStart,
|
|
||||||
@SerializedName("i") var mMaxAltitude: Double = 0.0,
|
|
||||||
@SerializedName("j") var mMinAltitude: Double = 0.0,
|
|
||||||
@SerializedName("k") var mPositiveElevation: Double = 0.0,
|
|
||||||
@SerializedName("l") var mNegativeElevation: Double = 0.0) {
|
|
||||||
|
|
||||||
|
|
||||||
/* Converts */
|
|
||||||
fun toTrack():Track {
|
|
||||||
val track: Track = Track()
|
|
||||||
track.trackFormatVersion = mTrackFormatVersion
|
|
||||||
mWayPoints.forEach { legacyWayPoint ->
|
|
||||||
val wayPoint: WayPoint= WayPoint(
|
|
||||||
provider = legacyWayPoint.mLocation.provider,
|
|
||||||
latitude = legacyWayPoint.mLocation.latitude,
|
|
||||||
longitude = legacyWayPoint.mLocation.longitude,
|
|
||||||
altitude = legacyWayPoint.mLocation.altitude,
|
|
||||||
accuracy = legacyWayPoint.mLocation.accuracy,
|
|
||||||
time = legacyWayPoint.mLocation.time,
|
|
||||||
distanceToStartingPoint = legacyWayPoint.mDistanceToStartingPoint,
|
|
||||||
numberSatellites = legacyWayPoint.mNumberSatellites,
|
|
||||||
isStopOver = legacyWayPoint.mIsStopOver
|
|
||||||
)
|
|
||||||
track.wayPoints.add(wayPoint)
|
|
||||||
}
|
|
||||||
track.length = mTrackLength
|
|
||||||
track.duration = mDuration
|
|
||||||
track.stepCount = mStepCount
|
|
||||||
track.recordingStart = mRecordingStart
|
|
||||||
track.recordingStop = mRecordingStop
|
|
||||||
track.maxAltitude = mMaxAltitude
|
|
||||||
track.minAltitude = mMinAltitude
|
|
||||||
track.positiveElevation = mPositiveElevation
|
|
||||||
track.negativeElevation = mNegativeElevation
|
|
||||||
track.latitude = track.wayPoints[0].latitude
|
|
||||||
track.longitude = track.wayPoints[0].longitude
|
|
||||||
track.name = DateTimeHelper.convertToReadableDate(mRecordingStart)
|
|
||||||
return track
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* End of inner class
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Inner class: Legacy version of WayPoint - used for one-time import only
|
|
||||||
* Warning: Works only as long as targetSdkVersion < 28
|
|
||||||
*/
|
|
||||||
@Keep
|
|
||||||
private data class LegacyWayPoint (
|
|
||||||
@SerializedName("a") var mLocation: Location,
|
|
||||||
@SerializedName("b") var mIsStopOver: Boolean = false,
|
|
||||||
@SerializedName("c") var mDistanceToStartingPoint: Float = 0f,
|
|
||||||
@SerializedName("d") var mNumberSatellites: Int = 0) {
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* End of inner class
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -106,7 +106,6 @@ object LengthUnitHelper {
|
||||||
// speed in km/h / mph
|
// speed in km/h / mph
|
||||||
val velocity: Double = convertMetersPerSecond((trackLength / duration), useImperialUnits)
|
val velocity: Double = convertMetersPerSecond((trackLength / duration), useImperialUnits)
|
||||||
// create readable speed string
|
// create readable speed string
|
||||||
LogHelper.e("TAG", "duration = $duration velocity = $velocity")
|
|
||||||
var bd: BigDecimal = BigDecimal.valueOf(velocity)
|
var bd: BigDecimal = BigDecimal.valueOf(velocity)
|
||||||
bd = bd.setScale(1, RoundingMode.HALF_UP)
|
bd = bd.setScale(1, RoundingMode.HALF_UP)
|
||||||
speed = bd.toPlainString()
|
speed = bd.toPlainString()
|
||||||
|
|
|
@ -36,12 +36,18 @@ import java.util.*
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MapHelper object
|
* MapHelper class
|
||||||
*/
|
*/
|
||||||
object MapHelper {
|
class MapOverlay (private var markerListener: MarkerListener) {
|
||||||
|
|
||||||
|
/* Interface used to communicate back to activity/fragment */
|
||||||
|
interface MarkerListener {
|
||||||
|
fun onMarkerTapped(latitude: Double, longitude: Double) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val LOG_TAG = MapHelper::class.java.simpleName
|
private val TAG = MapOverlay::class.java.simpleName
|
||||||
|
|
||||||
|
|
||||||
/* Creates icon overlay for current position (used in MapFragment) */
|
/* Creates icon overlay for current position (used in MapFragment) */
|
||||||
|
@ -93,16 +99,22 @@ object MapHelper {
|
||||||
when (trackingState) {
|
when (trackingState) {
|
||||||
// CASE: Recording is active
|
// CASE: Recording is active
|
||||||
Keys.STATE_TRACKING_ACTIVE -> {
|
Keys.STATE_TRACKING_ACTIVE -> {
|
||||||
when (wayPoint.isStopOver) {
|
if (wayPoint.starred) {
|
||||||
true -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_grey_24dp)!!
|
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_star_red_24dp)!!
|
||||||
false -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_red_24dp)!!
|
} else if (wayPoint.isStopOver) {
|
||||||
|
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_grey_24dp)!!
|
||||||
|
} else {
|
||||||
|
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_red_24dp)!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// CASE: Recording is paused/stopped
|
// CASE: Recording is paused/stopped
|
||||||
else -> {
|
else -> {
|
||||||
when (wayPoint.isStopOver) {
|
if (wayPoint.starred) {
|
||||||
true -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_grey_24dp)!!
|
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_star_blue_24dp)!!
|
||||||
false -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_blue_24dp)!!
|
} else if (wayPoint.isStopOver) {
|
||||||
|
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_grey_24dp)!!
|
||||||
|
} else {
|
||||||
|
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_blue_24dp)!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,7 +133,8 @@ object MapHelper {
|
||||||
/* Creates a marker overlay item */
|
/* Creates a marker overlay item */
|
||||||
private fun createOverlayItem(context: Context, latitude: Double, longitude: Double, accuracy: Float, provider: String, time: Long): OverlayItem {
|
private 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: String = "${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: String = "${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 = GeoPoint(latitude, longitude)
|
val position: GeoPoint = GeoPoint(latitude, longitude)
|
||||||
return OverlayItem(title, description, position)
|
return OverlayItem(title, description, position)
|
||||||
}
|
}
|
||||||
|
@ -129,10 +142,10 @@ object MapHelper {
|
||||||
|
|
||||||
/* Creates an overlay */
|
/* Creates an overlay */
|
||||||
private fun createOverlay(context: Context, overlayItems: ArrayList<OverlayItem>): ItemizedIconOverlay<OverlayItem> {
|
private fun createOverlay(context: Context, overlayItems: ArrayList<OverlayItem>): ItemizedIconOverlay<OverlayItem> {
|
||||||
return ItemizedIconOverlay(overlayItems,
|
return ItemizedIconOverlay<OverlayItem>(context, overlayItems,
|
||||||
object : ItemizedIconOverlay.OnItemGestureListener<OverlayItem> {
|
object : ItemizedIconOverlay.OnItemGestureListener<OverlayItem> {
|
||||||
override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean {
|
override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean {
|
||||||
Toast.makeText(context, item.title, Toast.LENGTH_LONG).show()
|
markerListener.onMarkerTapped(item.point.latitude, item.point.longitude)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
override fun onItemLongPress(index: Int, item: OverlayItem): Boolean {
|
override fun onItemLongPress(index: Int, item: OverlayItem): Boolean {
|
||||||
|
@ -141,8 +154,7 @@ object MapHelper {
|
||||||
Toast.makeText(context, item.snippet, Toast.LENGTH_LONG).show()
|
Toast.makeText(context, item.snippet, Toast.LENGTH_LONG).show()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}, context)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -19,6 +19,8 @@ package org.y20k.trackbook.helpers
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
|
import android.widget.Toast
|
||||||
|
import org.y20k.trackbook.R
|
||||||
import org.y20k.trackbook.core.Track
|
import org.y20k.trackbook.core.Track
|
||||||
import org.y20k.trackbook.core.TracklistElement
|
import org.y20k.trackbook.core.TracklistElement
|
||||||
import org.y20k.trackbook.core.WayPoint
|
import org.y20k.trackbook.core.WayPoint
|
||||||
|
@ -219,5 +221,18 @@ object TrackHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Toggles starred flag for given position */
|
||||||
|
fun toggleStarred(context: Context, track: Track, latitude: Double, longitude: Double): Track {
|
||||||
|
track.wayPoints.forEach { waypoint ->
|
||||||
|
if (waypoint.latitude == latitude && waypoint.longitude == longitude) {
|
||||||
|
waypoint.starred = !waypoint.starred
|
||||||
|
when (waypoint.starred) {
|
||||||
|
true -> Toast.makeText(context, R.string.toast_message_poi_added, Toast.LENGTH_LONG).show()
|
||||||
|
false -> Toast.makeText(context, R.string.toast_message_poi_removed, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return track
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -88,8 +88,8 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
trackElementViewHolder.trackNameView.text = tracklist.tracklistElements[position].name
|
trackElementViewHolder.trackNameView.text = tracklist.tracklistElements[position].name
|
||||||
trackElementViewHolder.trackDataView.text = createTrackDataString(position)
|
trackElementViewHolder.trackDataView.text = createTrackDataString(position)
|
||||||
when (tracklist.tracklistElements[position].starred) {
|
when (tracklist.tracklistElements[position].starred) {
|
||||||
true -> trackElementViewHolder.starButton.setImageDrawable(context.getDrawable(R.drawable.ic_star_24dp))
|
true -> trackElementViewHolder.starButton.setImageDrawable(context.getDrawable(R.drawable.ic_star_filled_24dp))
|
||||||
false -> trackElementViewHolder.starButton.setImageDrawable(context.getDrawable(R.drawable.ic_star_border_24dp))
|
false -> trackElementViewHolder.starButton.setImageDrawable(context.getDrawable(R.drawable.ic_star_outline_24dp))
|
||||||
}
|
}
|
||||||
trackElementViewHolder.trackElement.setOnClickListener {
|
trackElementViewHolder.trackElement.setOnClickListener {
|
||||||
tracklistListener.onTrackElementTapped(tracklist.tracklistElements[position])
|
tracklistListener.onTrackElementTapped(tracklist.tracklistElements[position])
|
||||||
|
@ -134,11 +134,11 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
val starButton: ImageButton = view as ImageButton
|
val starButton: ImageButton = view as ImageButton
|
||||||
when (tracklist.tracklistElements[position].starred) {
|
when (tracklist.tracklistElements[position].starred) {
|
||||||
true -> {
|
true -> {
|
||||||
starButton.setImageDrawable(context.getDrawable(R.drawable.ic_star_border_24dp))
|
starButton.setImageDrawable(context.getDrawable(R.drawable.ic_star_outline_24dp))
|
||||||
tracklist.tracklistElements[position].starred = false
|
tracklist.tracklistElements[position].starred = false
|
||||||
}
|
}
|
||||||
false -> {
|
false -> {
|
||||||
starButton.setImageDrawable(context.getDrawable(R.drawable.ic_star_24dp))
|
starButton.setImageDrawable(context.getDrawable(R.drawable.ic_star_filled_24dp))
|
||||||
tracklist.tracklistElements[position].starred = true
|
tracklist.tracklistElements[position].starred = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,14 +44,14 @@ import org.y20k.trackbook.R
|
||||||
import org.y20k.trackbook.core.Track
|
import org.y20k.trackbook.core.Track
|
||||||
import org.y20k.trackbook.helpers.AppThemeHelper
|
import org.y20k.trackbook.helpers.AppThemeHelper
|
||||||
import org.y20k.trackbook.helpers.LogHelper
|
import org.y20k.trackbook.helpers.LogHelper
|
||||||
import org.y20k.trackbook.helpers.MapHelper
|
import org.y20k.trackbook.helpers.MapOverlay
|
||||||
import org.y20k.trackbook.helpers.PreferencesHelper
|
import org.y20k.trackbook.helpers.PreferencesHelper
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MapFragmentLayoutHolder class
|
* MapFragmentLayoutHolder class
|
||||||
*/
|
*/
|
||||||
data class MapFragmentLayoutHolder(var context: Context, var inflater: LayoutInflater, var container: ViewGroup?, val startLocation: Location, val trackingState: Int) {
|
data class MapFragmentLayoutHolder(private var context: Context, private var markerListener: MapOverlay.MarkerListener, private var inflater: LayoutInflater, private var container: ViewGroup?, private val startLocation: Location, private val trackingState: Int) {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(MapFragmentLayoutHolder::class.java)
|
private val TAG: String = LogHelper.makeLogTag(MapFragmentLayoutHolder::class.java)
|
||||||
|
@ -109,7 +109,7 @@ data class MapFragmentLayoutHolder(var context: Context, var inflater: LayoutInf
|
||||||
mapView.overlays.add(compassOverlay)
|
mapView.overlays.add(compassOverlay)
|
||||||
|
|
||||||
// add my location overlay
|
// add my location overlay
|
||||||
currentPositionOverlay = MapHelper.createMyLocationOverlay(context, startLocation, trackingState)
|
currentPositionOverlay = MapOverlay(markerListener).createMyLocationOverlay(context, startLocation, trackingState)
|
||||||
mapView.overlays.add(currentPositionOverlay)
|
mapView.overlays.add(currentPositionOverlay)
|
||||||
centerMap(startLocation)
|
centerMap(startLocation)
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ data class MapFragmentLayoutHolder(var context: Context, var inflater: LayoutInf
|
||||||
/* Mark current position on map */
|
/* Mark current position on map */
|
||||||
fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_NOT) {
|
fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_NOT) {
|
||||||
mapView.overlays.remove(currentPositionOverlay)
|
mapView.overlays.remove(currentPositionOverlay)
|
||||||
currentPositionOverlay = MapHelper.createMyLocationOverlay(context, location, trackingState)
|
currentPositionOverlay = MapOverlay(markerListener).createMyLocationOverlay(context, location, trackingState)
|
||||||
mapView.overlays.add(currentPositionOverlay)
|
mapView.overlays.add(currentPositionOverlay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ data class MapFragmentLayoutHolder(var context: Context, var inflater: LayoutInf
|
||||||
mapView.overlays.remove(currentTrackOverlay)
|
mapView.overlays.remove(currentTrackOverlay)
|
||||||
}
|
}
|
||||||
if (track.wayPoints.isNotEmpty()) {
|
if (track.wayPoints.isNotEmpty()) {
|
||||||
currentTrackOverlay = MapHelper.createTrackOverlay(context, track, trackingState)
|
currentTrackOverlay = MapOverlay(markerListener).createTrackOverlay(context, track, trackingState)
|
||||||
mapView.overlays.add(currentTrackOverlay)
|
mapView.overlays.add(currentTrackOverlay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ import kotlin.math.roundToInt
|
||||||
/*
|
/*
|
||||||
* TrackFragmentLayoutHolder class
|
* TrackFragmentLayoutHolder class
|
||||||
*/
|
*/
|
||||||
data class TrackFragmentLayoutHolder(var context: Context, var inflater: LayoutInflater, var container: ViewGroup?, var track: Track) {
|
data class TrackFragmentLayoutHolder(private var context: Context, private var markerListener: MapOverlay.MarkerListener, private var inflater: LayoutInflater, private var container: ViewGroup?, var track: Track) {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TrackFragmentLayoutHolder::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TrackFragmentLayoutHolder::class.java)
|
||||||
|
@ -140,7 +140,7 @@ data class TrackFragmentLayoutHolder(var context: Context, var inflater: LayoutI
|
||||||
mapView.overlays.add(compassOverlay)
|
mapView.overlays.add(compassOverlay)
|
||||||
|
|
||||||
// create map overlay
|
// create map overlay
|
||||||
trackOverlay = MapHelper.createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT)
|
trackOverlay = MapOverlay(markerListener).createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT)
|
||||||
if (track.wayPoints.isNotEmpty()) {
|
if (track.wayPoints.isNotEmpty()) {
|
||||||
mapView.overlays.add(trackOverlay)
|
mapView.overlays.add(trackOverlay)
|
||||||
}
|
}
|
||||||
|
@ -161,6 +161,19 @@ data class TrackFragmentLayoutHolder(var context: Context, var inflater: LayoutI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Updates map overlay */
|
||||||
|
fun updateTrackOverlay(newTrack: Track) {
|
||||||
|
track = newTrack
|
||||||
|
if (trackOverlay != null) {
|
||||||
|
mapView.overlays.remove(trackOverlay)
|
||||||
|
}
|
||||||
|
if (track.wayPoints.isNotEmpty()) {
|
||||||
|
trackOverlay = MapOverlay(markerListener).createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT)
|
||||||
|
mapView.overlays.add(trackOverlay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Saves zoom level and center of this map */
|
/* Saves zoom level and center of this map */
|
||||||
fun saveViewStateToTrack() {
|
fun saveViewStateToTrack() {
|
||||||
val center: IGeoPoint = mapView.mapCenter
|
val center: IGeoPoint = mapView.mapCenter
|
||||||
|
|
9
app/src/main/res/drawable/ic_star_blue_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_star_blue_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/trackbook_blue"
|
||||||
|
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_star_red_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_star_red_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/trackbook_red"
|
||||||
|
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
||||||
|
</vector>
|
|
@ -57,7 +57,7 @@
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:srcCompat="@drawable/ic_star_border_24dp" />
|
app:srcCompat="@drawable/ic_star_outline_24dp" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
<string name="toast_message_copied_to_clipboard">Copied to clipboard.</string>
|
<string name="toast_message_copied_to_clipboard">Copied to clipboard.</string>
|
||||||
<string name="toast_message_elevation_info">Hint: The accuracy of elevation data depends on your device. The uphill and downhill elevation of the whole route is measured.</string>
|
<string name="toast_message_elevation_info">Hint: The accuracy of elevation data depends on your device. The uphill and downhill elevation of the whole route is measured.</string>
|
||||||
<string name="toast_message_install_file_helper">Unable to save. Please install a file manager first.</string>
|
<string name="toast_message_install_file_helper">Unable to save. Please install a file manager first.</string>
|
||||||
|
<string name="toast_message_poi_added">Point of interest marker added.</string>
|
||||||
|
<string name="toast_message_poi_removed">Point of interest marker removed.</string>
|
||||||
<string name="toast_message_save_gpx">Saving recording as GPX.</string>
|
<string name="toast_message_save_gpx">Saving recording as GPX.</string>
|
||||||
<!-- Map Markers -->
|
<!-- Map Markers -->
|
||||||
<string name="marker_description_time">Time</string>
|
<string name="marker_description_time">Time</string>
|
||||||
|
|
Loading…
Reference in a new issue