display distance and duration on map while Trackbook is recording.

This commit is contained in:
y20k 2022-01-11 17:54:49 +01:00
parent 264f704124
commit 9a7fc6fcb9
No known key found for this signature in database
GPG key ID: 824D4259F41FAFF6
7 changed files with 128 additions and 12 deletions

View file

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

View file

@ -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 timeString: String
when (compactFormat) {
// Compact tine format
true -> {
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"
}
}
// Long time format
false -> {
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 h: String = context.getString(R.string.abbreviation_hours)
val m: String = context.getString(R.string.abbreviation_minutes) val m: String = context.getString(R.string.abbreviation_minutes)
val s: String = context.getString(R.string.abbreviation_seconds) val s: String = context.getString(R.string.abbreviation_seconds)
timeString = "$hoursString $h $minutesString $m $secondsString $s"
}
}
}
when (milliseconds >= Keys.ONE_HOUR_IN_MILLISECONDS) {
// CASE: format hh:mm:ss
true -> {
timeString = "$hours $h $minutes $m $seconds $s"
}
// CASE: format mm:ss
false -> {
timeString = "$minutes $m $seconds $s"
}
}
return timeString return timeString
} }

View file

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

View file

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

View file

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

View file

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

View file

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