display distance and duration on map while Trackbook is recording.
This commit is contained in:
parent
264f704124
commit
9a7fc6fcb9
7 changed files with 128 additions and 12 deletions
|
@ -361,6 +361,7 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlayHelpe
|
||||||
// update location and track
|
// update location and track
|
||||||
layout.markCurrentPosition(currentBestLocation, trackingState)
|
layout.markCurrentPosition(currentBestLocation, trackingState)
|
||||||
layout.overlayCurrentTrack(track, trackingState)
|
layout.overlayCurrentTrack(track, trackingState)
|
||||||
|
layout.updateLiveStatics(length = track.length, duration = track.duration, trackingState = trackingState)
|
||||||
// center map, if it had not been dragged/zoomed before
|
// center map, if it had not been dragged/zoomed before
|
||||||
if (!layout.userInteraction) { layout.centerMap(currentBestLocation, true)}
|
if (!layout.userInteraction) { layout.centerMap(currentBestLocation, true)}
|
||||||
// show error snackbar if necessary
|
// show error snackbar if necessary
|
||||||
|
|
|
@ -33,25 +33,51 @@ import java.util.concurrent.TimeUnit
|
||||||
object DateTimeHelper {
|
object DateTimeHelper {
|
||||||
|
|
||||||
/* Converts milliseconds to mm:ss or hh:mm:ss */
|
/* Converts milliseconds to mm:ss or hh:mm:ss */
|
||||||
fun convertToReadableTime(context: Context, milliseconds: Long): String {
|
fun convertToReadableTime(context: Context, milliseconds: Long, compactFormat: Boolean = false): String {
|
||||||
val timeString: String
|
|
||||||
val hours: Long = TimeUnit.MILLISECONDS.toHours(milliseconds)
|
val hours: Long = TimeUnit.MILLISECONDS.toHours(milliseconds)
|
||||||
val minutes: Long = TimeUnit.MILLISECONDS.toMinutes(milliseconds) % TimeUnit.HOURS.toMinutes(1)
|
val minutes: Long = TimeUnit.MILLISECONDS.toMinutes(milliseconds) % TimeUnit.HOURS.toMinutes(1)
|
||||||
val seconds: Long = TimeUnit.MILLISECONDS.toSeconds(milliseconds) % TimeUnit.MINUTES.toSeconds(1)
|
val seconds: Long = TimeUnit.MILLISECONDS.toSeconds(milliseconds) % TimeUnit.MINUTES.toSeconds(1)
|
||||||
val h: String = context.getString(R.string.abbreviation_hours)
|
val timeString: String
|
||||||
val m: String = context.getString(R.string.abbreviation_minutes)
|
|
||||||
val s: String = context.getString(R.string.abbreviation_seconds)
|
|
||||||
|
|
||||||
when (milliseconds >= Keys.ONE_HOUR_IN_MILLISECONDS) {
|
when (compactFormat) {
|
||||||
// CASE: format hh:mm:ss
|
|
||||||
|
// Compact tine format
|
||||||
true -> {
|
true -> {
|
||||||
timeString = "$hours $h $minutes $m $seconds $s"
|
if (milliseconds < Keys.ONE_HOUR_IN_MILLISECONDS) {
|
||||||
|
// example: 23:45
|
||||||
|
val minutesString: String = minutes.toString()
|
||||||
|
val secondsString: String = seconds.toString().padStart(2, '0')
|
||||||
|
timeString = "$minutesString:$secondsString"
|
||||||
|
} else {
|
||||||
|
// example: 1:23
|
||||||
|
val hoursString: String = hours.toString()
|
||||||
|
val minutesString: String = minutes.toString()
|
||||||
|
timeString = "$hoursString:$minutesString"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// CASE: format mm:ss
|
|
||||||
|
// Long time format
|
||||||
false -> {
|
false -> {
|
||||||
timeString = "$minutes $m $seconds $s"
|
if (milliseconds < Keys.ONE_HOUR_IN_MILLISECONDS) {
|
||||||
|
// example: 23 min 45 sec
|
||||||
|
val minutesString: String = minutes.toString()
|
||||||
|
val secondsString: String = seconds.toString()
|
||||||
|
val m: String = context.getString(R.string.abbreviation_minutes)
|
||||||
|
val s: String = context.getString(R.string.abbreviation_seconds)
|
||||||
|
timeString = "$minutesString $m $secondsString $s"
|
||||||
|
} else {
|
||||||
|
// example: 1 hrs 23 min 45 sec
|
||||||
|
val hoursString: String = hours.toString()
|
||||||
|
val minutesString: String = minutes.toString()
|
||||||
|
val secondsString: String = seconds.toString()
|
||||||
|
val h: String = context.getString(R.string.abbreviation_hours)
|
||||||
|
val m: String = context.getString(R.string.abbreviation_minutes)
|
||||||
|
val s: String = context.getString(R.string.abbreviation_seconds)
|
||||||
|
timeString = "$hoursString $h $minutesString $m $secondsString $s"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return timeString
|
return timeString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,16 +22,19 @@ import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.Paint
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.constraintlayout.widget.Group
|
import androidx.constraintlayout.widget.Group
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
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 com.google.android.material.textview.MaterialTextView
|
||||||
import org.osmdroid.api.IMapController
|
import org.osmdroid.api.IMapController
|
||||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||||
import org.osmdroid.util.GeoPoint
|
import org.osmdroid.util.GeoPoint
|
||||||
|
@ -70,6 +73,11 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
||||||
private var currentPositionOverlay: ItemizedIconOverlay<OverlayItem>
|
private var currentPositionOverlay: ItemizedIconOverlay<OverlayItem>
|
||||||
private var currentTrackOverlay: SimpleFastPointOverlay?
|
private var currentTrackOverlay: SimpleFastPointOverlay?
|
||||||
private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay<OverlayItem>?
|
private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay<OverlayItem>?
|
||||||
|
private val liveStatisticsDistanceView: MaterialTextView
|
||||||
|
private val liveStatisticsDistanceOutlineView: MaterialTextView
|
||||||
|
private val liveStatisticsDurationView: MaterialTextView
|
||||||
|
private val liveStatisticsDurationOutlineView: MaterialTextView
|
||||||
|
private val useImperial: Boolean = PreferencesHelper.loadUseImperialUnits()
|
||||||
private var locationErrorBar: Snackbar
|
private var locationErrorBar: Snackbar
|
||||||
private var controller: IMapController
|
private var controller: IMapController
|
||||||
private var zoomLevel: Double
|
private var zoomLevel: Double
|
||||||
|
@ -86,6 +94,10 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
||||||
saveButton = rootView.findViewById(R.id.fab_sub_menu_button_save)
|
saveButton = rootView.findViewById(R.id.fab_sub_menu_button_save)
|
||||||
clearButton = rootView.findViewById(R.id.fab_sub_menu_button_clear)
|
clearButton = rootView.findViewById(R.id.fab_sub_menu_button_clear)
|
||||||
resumeButton = rootView.findViewById(R.id.fab_sub_menu_button_resume)
|
resumeButton = rootView.findViewById(R.id.fab_sub_menu_button_resume)
|
||||||
|
liveStatisticsDistanceView = rootView.findViewById(R.id.live_statistics_distance)
|
||||||
|
liveStatisticsDistanceOutlineView = rootView.findViewById(R.id.live_statistics_distance_outline)
|
||||||
|
liveStatisticsDurationView = rootView.findViewById(R.id.live_statistics_duration)
|
||||||
|
liveStatisticsDurationOutlineView = rootView.findViewById(R.id.live_statistics_duration_outline)
|
||||||
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
|
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
|
||||||
|
|
||||||
// basic map setup
|
// basic map setup
|
||||||
|
@ -102,13 +114,20 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
||||||
mapView.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS)
|
mapView.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// store Density Scaling Factor
|
||||||
|
val densityScalingFactor: Float = UiHelper.getDensityScalingFactor(context)
|
||||||
|
|
||||||
// add compass to map
|
// add compass to map
|
||||||
val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), mapView)
|
val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), mapView)
|
||||||
compassOverlay.enableCompass()
|
compassOverlay.enableCompass()
|
||||||
compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / UiHelper.getDensityScalingFactor(context)))
|
compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / densityScalingFactor))
|
||||||
|
|
||||||
mapView.overlays.add(compassOverlay)
|
mapView.overlays.add(compassOverlay)
|
||||||
|
|
||||||
|
// position the live statistics
|
||||||
|
(liveStatisticsDistanceView.layoutParams as ConstraintLayout.LayoutParams).apply {
|
||||||
|
topMargin = (12 * densityScalingFactor).toInt() + statusBarHeight
|
||||||
|
}
|
||||||
|
|
||||||
// add my location overlay
|
// add my location overlay
|
||||||
currentPositionOverlay = MapOverlayHelper(markerListener).createMyLocationOverlay(context, startLocation, trackingState)
|
currentPositionOverlay = MapOverlayHelper(markerListener).createMyLocationOverlay(context, startLocation, trackingState)
|
||||||
mapView.overlays.add(currentPositionOverlay)
|
mapView.overlays.add(currentPositionOverlay)
|
||||||
|
@ -182,6 +201,27 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Update live statics */
|
||||||
|
fun updateLiveStatics(length: Float, duration: Long, trackingState: Int) {
|
||||||
|
// toggle visibility
|
||||||
|
val trackingActive: Boolean = trackingState != Keys.STATE_TRACKING_NOT
|
||||||
|
liveStatisticsDistanceView.isVisible = trackingActive
|
||||||
|
liveStatisticsDurationView.isVisible = trackingActive
|
||||||
|
// update distance and duration (and add outline)
|
||||||
|
val distanceString: String = LengthUnitHelper.convertDistanceToString(length, useImperial)
|
||||||
|
liveStatisticsDistanceView.text = distanceString
|
||||||
|
liveStatisticsDistanceOutlineView.text = distanceString
|
||||||
|
liveStatisticsDistanceOutlineView.paint.strokeWidth = 5f
|
||||||
|
liveStatisticsDistanceOutlineView.paint.style = Paint.Style.STROKE
|
||||||
|
val durationString: String = DateTimeHelper.convertToReadableTime(context, duration, compactFormat = true)
|
||||||
|
liveStatisticsDurationView.text = durationString
|
||||||
|
liveStatisticsDurationOutlineView.text = durationString
|
||||||
|
liveStatisticsDurationOutlineView.paint.strokeWidth = 5f
|
||||||
|
liveStatisticsDurationOutlineView.paint.style = Paint.Style.STROKE
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Toggles state of recording button and sub menu_bottom_navigation */
|
/* Toggles state of recording button and sub menu_bottom_navigation */
|
||||||
fun updateRecordingButton(trackingState: Int) {
|
fun updateRecordingButton(trackingState: Int) {
|
||||||
when (trackingState) {
|
when (trackingState) {
|
||||||
|
|
|
@ -201,6 +201,50 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:tint="@color/location_button_icon" />
|
app:tint="@color/location_button_icon" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/live_statistics_distance_outline"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAlignment="textEnd"
|
||||||
|
android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||||
|
android:textColor="@color/text_outline_default"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/live_statistics_distance"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/live_statistics_distance" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/live_statistics_distance"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="64dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:textAlignment="textEnd"
|
||||||
|
android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||||
|
android:textColor="@color/text_default"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="@string/sample_text_default_live_statistics_distance" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/live_statistics_duration_outline"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAlignment="textEnd"
|
||||||
|
android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||||
|
android:textColor="@color/text_outline_default"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/live_statistics_duration"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/live_statistics_duration" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/live_statistics_duration"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAlignment="textEnd"
|
||||||
|
android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||||
|
android:textColor="@color/text_default"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/live_statistics_distance"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/live_statistics_distance"
|
||||||
|
tools:text="@string/sample_text_default_live_statistics_duration" />
|
||||||
|
|
||||||
<!-- GROUPS -->
|
<!-- GROUPS -->
|
||||||
<androidx.constraintlayout.widget.Group
|
<androidx.constraintlayout.widget.Group
|
||||||
android:id="@+id/fab_sub_menu"
|
android:id="@+id/fab_sub_menu"
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
<color name="text_default">@color/trackbook_neutral_white</color>
|
<color name="text_default">@color/trackbook_neutral_white</color>
|
||||||
<color name="text_lightweight">@color/trackbook_neutral_very_light</color>
|
<color name="text_lightweight">@color/trackbook_neutral_very_light</color>
|
||||||
|
<color name="text_outline_default">@color/trackbook_neutral_darker</color>
|
||||||
|
|
||||||
<color name="icon_default">@color/trackbook_neutral_white</color>
|
<color name="icon_default">@color/trackbook_neutral_white</color>
|
||||||
<color name="icon_lightweight">@color/trackbook_neutral_very_light</color>
|
<color name="icon_lightweight">@color/trackbook_neutral_very_light</color>
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
<color name="text_default">@color/trackbook_neutral_darker</color>
|
<color name="text_default">@color/trackbook_neutral_darker</color>
|
||||||
<color name="text_lightweight">@color/trackbook_neutral_dark</color>
|
<color name="text_lightweight">@color/trackbook_neutral_dark</color>
|
||||||
|
<color name="text_outline_default">@color/trackbook_neutral_white</color>
|
||||||
|
|
||||||
|
|
||||||
<color name="icon_default">@color/trackbook_neutral_dark</color>
|
<color name="icon_default">@color/trackbook_neutral_dark</color>
|
||||||
<color name="icon_lightweight">@color/trackbook_neutral_medium_light</color>
|
<color name="icon_lightweight">@color/trackbook_neutral_medium_light</color>
|
||||||
|
|
|
@ -128,4 +128,6 @@
|
||||||
<string name="sample_text_track_name" translatable="false">July 20, 1969</string>
|
<string name="sample_text_track_name" translatable="false">July 20, 1969</string>
|
||||||
<string name="sample_text_default_data" translatable="false">track data missing</string>
|
<string name="sample_text_default_data" translatable="false">track data missing</string>
|
||||||
<string name="sample_text_default_total_distance" translatable="false">6357.23 km</string>
|
<string name="sample_text_default_total_distance" translatable="false">6357.23 km</string>
|
||||||
|
<string name="sample_text_default_live_statistics_distance" translatable="false">23.0 km</string>
|
||||||
|
<string name="sample_text_default_live_statistics_duration" translatable="false">5:23</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue