checkpoint

master
voussoir 2023-03-12 17:49:03 -07:00
parent 2c33cc88f7
commit 8cbfa729f0
9 changed files with 57 additions and 205 deletions

View File

@ -33,7 +33,6 @@ import android.view.WindowManager
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
@ -71,7 +70,6 @@ class MapFragment : Fragment()
private var trackingState: Int = Keys.STATE_TRACKING_STOPPED
private var gpsProviderActive: Boolean = false
private var networkProviderActive: Boolean = false
private lateinit var track: Track
private lateinit var currentBestLocation: Location
private lateinit var trackerService: TrackerService
@ -185,12 +183,13 @@ class MapFragment : Fragment()
dialog.cancel()
}
save_button.setOnClickListener {
val radius = radius_input.text.toString().toDoubleOrNull() ?: 25.0
trackbook.database.insert_homepoint(
id=random_long(),
name=name_input.text.toString(),
latitude=point.latitude,
longitude=point.longitude,
radius=radius_input.text.toString().toDouble(),
radius=radius,
)
trackbook.load_homepoints()
create_homepoint_overlays(requireContext(), mapView, trackbook.homepoints)
@ -221,6 +220,7 @@ class MapFragment : Fragment()
mapView.setOnTouchListener { v, event ->
continuous_auto_center = false
zoomLevel = mapView.zoomLevelDouble
false
}
@ -232,10 +232,12 @@ class MapFragment : Fragment()
centerMap(currentBestLocation, animated=true)
}
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 {
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)
@ -247,7 +249,8 @@ class MapFragment : Fragment()
{
super.onStart()
// 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)
}
// bind to TrackerService
@ -556,7 +559,7 @@ class MapFragment : Fragment()
}
/* 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) {
mapView.overlays.remove(currentTrackOverlay)
@ -564,9 +567,9 @@ class MapFragment : Fragment()
if (currentTrackSpecialMarkerOverlay != null) {
mapView.overlays.remove(currentTrackSpecialMarkerOverlay)
}
if (track.trkpts.isNotEmpty()) {
createTrackOverlay(requireContext(), mapView, track, trackingState)
createSpecialMakersTrackOverlay(requireContext(), mapView, track, trackingState)
if (trkpts.isNotEmpty()) {
createTrackOverlay(requireContext(), mapView, trkpts, trackingState)
createSpecialMakersTrackOverlay(requireContext(), mapView, trkpts, trackingState)
}
}
@ -608,7 +611,8 @@ class MapFragment : Fragment()
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
override fun onServiceConnected(className: ComponentName, service: IBinder)
{
bound = true
// get reference to tracker service
val binder = service as TrackerService.LocalBinder
@ -622,7 +626,8 @@ class MapFragment : Fragment()
handler.removeCallbacks(periodicLocationRequestRunnable)
handler.postDelayed(periodicLocationRequestRunnable, 0)
}
override fun onServiceDisconnected(arg0: ComponentName) {
override fun onServiceDisconnected(arg0: ComponentName)
{
// service has crashed, or was killed by the system
handleServiceUnbind()
}
@ -632,17 +637,16 @@ class MapFragment : Fragment()
override fun run()
{
currentBestLocation = trackerService.currentBestLocation
track = trackerService.track
gpsProviderActive = trackerService.gpsProviderActive
networkProviderActive = trackerService.networkProviderActive
trackingState = trackerService.trackingState
// update location and track
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
if (continuous_auto_center)
{
centerMap(currentBestLocation, true)
centerMap(currentBestLocation, animated=false)
}
handler.postDelayed(this, Keys.REQUEST_CURRENT_LOCATION_INTERVAL)
}

View File

@ -26,8 +26,6 @@ import android.widget.Toast
import org.y20k.trackbook.helpers.iso8601_format
import java.text.SimpleDateFormat
import java.util.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
data class Track (
val database: Database,

View File

@ -29,14 +29,13 @@ import android.widget.Toast
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.fragment.app.Fragment
import org.y20k.trackbook.dialogs.RenameTrackDialog
import org.y20k.trackbook.helpers.LogHelper
import org.y20k.trackbook.helpers.iso8601_format
import org.y20k.trackbook.ui.TrackFragmentLayoutHolder
import java.text.SimpleDateFormat
import java.util.*
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDialog.YesNoDialogListener
class TrackFragment : Fragment(), YesNoDialog.YesNoDialogListener
{
/* Define log tag */
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)
// set up share button
layout.shareButton.setOnClickListener {
layout.save_track_button.setOnClickListener {
openSaveGpxDialog()
}
// set up delete button
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(
context = activity as Context,
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
)
}
// set up rename button
layout.editButton.setOnClickListener {
RenameTrackDialog(this as RenameTrackDialog.RenameTrackListener).show(activity as Context, layout.trackNameView.text.toString())
}
return layout.rootView
}
@ -135,7 +130,6 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
/* Opens up a file picker to select the save location */
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 intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)

View File

@ -31,7 +31,6 @@ import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.Manifest
import android.content.ContentValues
import android.os.*
import android.util.Log
import androidx.core.content.ContextCompat
@ -60,8 +59,9 @@ class TrackerService: Service(), SensorEventListener
var currentBestLocation: Location = getDefaultLocation()
var lastCommit: Date = Keys.DEFAULT_DATE
var location_min_time_ms: Long = 0
private val RECENT_TRKPT_COUNT = 7200
var stepCountOffset: Float = 0f
lateinit var track: Track
lateinit var recent_trkpts: Deque<Trkpt>
var gpsLocationListenerRegistered: Boolean = false
var networkLocationListenerRegistered: Boolean = false
var bound: Boolean = false
@ -170,10 +170,6 @@ class TrackerService: Service(), SensorEventListener
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()
trackbook = (applicationContext as Trackbook)
trackbook.load_homepoints()
recent_trkpts = ArrayDeque<Trkpt>(RECENT_TRKPT_COUNT)
gpsOnly = PreferencesHelper.loadGpsOnly()
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()
omitRests = PreferencesHelper.loadOmitRests()
commitInterval = PreferencesHelper.loadCommitInterval()
@ -445,11 +441,11 @@ class TrackerService: Service(), SensorEventListener
return false
}
}
if (track.trkpts.isEmpty())
if (recent_trkpts.isEmpty())
{
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.")
return false
@ -466,11 +462,11 @@ class TrackerService: Service(), SensorEventListener
if (should_keep_point((currentBestLocation)))
{
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)

View File

@ -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()
}
}

View File

@ -31,30 +31,26 @@ import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
import org.y20k.trackbook.Keys
import org.y20k.trackbook.R
import org.y20k.trackbook.Track
import org.y20k.trackbook.Trkpt
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.*
/* 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)
// gather points for overlay
val points: MutableList<IGeoPoint> = mutableListOf()
track.trkpts.forEach { wayPoint ->
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})"
trkpts.forEach { trkpt ->
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
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)
// set styling for overlay
val style: Paint = Paint()
val style = Paint()
style.style = Paint.Style.FILL
style.color = color
style.flags = Paint.ANTI_ALIAS_FLAG
@ -64,20 +60,20 @@ fun createTrackOverlay(context: Context, map_view: MapView, track: Track, tracki
.setSymbol(SimpleFastPointOverlayOptions.Shape.CIRCLE)
.setPointStyle(style)
.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.
val overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
map_view.overlays.add(overlay)
}
/* 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 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
if (!trackingActive && index == 0 && displayStartEndMarker && trkpt.starred)
{

View File

@ -37,18 +37,14 @@ import org.osmdroid.events.ZoomEvent
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
import org.osmdroid.util.GeoPoint
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.compass.CompassOverlay
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
import org.y20k.trackbook.Keys
import org.y20k.trackbook.R
import org.y20k.trackbook.Track
import org.y20k.trackbook.TrackStatistics
import org.y20k.trackbook.helpers.*
import kotlin.math.roundToInt
/*
* TrackFragmentLayoutHolder class
@ -63,9 +59,8 @@ data class TrackFragmentLayoutHolder(
{
/* Main class variables */
val rootView: View
val shareButton: ImageButton
val save_track_button: ImageButton
val deleteButton: ImageButton
val editButton: ImageButton
val trackNameView: MaterialTextView
private val mapView: MapView
private var controller: IMapController
@ -87,17 +82,15 @@ data class TrackFragmentLayoutHolder(
private val positiveElevationView: MaterialTextView
private val negativeElevationView: MaterialTextView
private val elevationDataViews: Group
private val trackManagementViews: Group
private val useImperialUnits: Boolean
/* Init block */
init {
init
{
// find views
rootView = inflater.inflate(R.layout.fragment_track, container, false)
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)
editButton = rootView.findViewById(R.id.edit_button)
trackNameView = rootView.findViewById(R.id.statistics_track_name_headline)
// basic map setup
@ -128,7 +121,6 @@ data class TrackFragmentLayoutHolder(
positiveElevationView = rootView.findViewById(R.id.statistics_data_positive_elevation)
negativeElevationView = rootView.findViewById(R.id.statistics_data_negative_elevation)
elevationDataViews = rootView.findViewById(R.id.elevation_data)
trackManagementViews = rootView.findViewById(R.id.management_icons)
// get measurement unit system
useImperialUnits = PreferencesHelper.loadUseImperialUnits()
@ -146,14 +138,13 @@ data class TrackFragmentLayoutHolder(
mapView.overlays.add(compassOverlay)
if (track.trkpts.isNotEmpty()) {
createSpecialMakersTrackOverlay(context, mapView, track, Keys.STATE_TRACKING_STOPPED, displayStartEndMarker = true)
createTrackOverlay(context, mapView, track, Keys.STATE_TRACKING_STOPPED)
createSpecialMakersTrackOverlay(context, mapView, track.trkpts, Keys.STATE_TRACKING_STOPPED, displayStartEndMarker = true)
createTrackOverlay(context, mapView, track.trkpts, Keys.STATE_TRACKING_STOPPED)
}
// set up and show statistics sheet
statisticsSheetBehavior = BottomSheetBehavior.from<View>(statisticsSheet)
statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
statisticsSheetBehavior.addBottomSheetCallback(getStatisticsSheetCallback())
setupStatisticsViews()
}
@ -187,44 +178,17 @@ data class TrackFragmentLayoutHolder(
}
/* Shows/hides the statistics sheet */
private fun toggleStatisticsSheetVisibility() {
private fun toggleStatisticsSheetVisibility()
{
when (statisticsSheetBehavior.state) {
BottomSheetBehavior.STATE_EXPANDED -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
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 */
override fun onZoom(event: ZoomEvent?): Boolean {
override fun onZoom(event: ZoomEvent?): Boolean
{
if (event == null) {
return false
} else {
@ -234,7 +198,8 @@ data class TrackFragmentLayoutHolder(
}
/* Overrides onScroll from MapListener */
override fun onScroll(event: ScrollEvent?): Boolean {
override fun onScroll(event: ScrollEvent?): Boolean
{
if (event == null) {
return false
} else {

View File

@ -15,14 +15,6 @@
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" />
<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
android:id="@+id/statistics_track_name_headline"
android:layout_width="0dp"
@ -35,7 +27,7 @@
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
android:textColor="@color/text_default"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/edit_button"
app:layout_constraintEnd_toStartOf="@+id/delete_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/sample_text_track_name" />
@ -46,21 +38,10 @@
android:layout_height="wrap_content"
android:backgroundTint="@color/default_transparent"
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_constraintEnd_toStartOf="@+id/delete_button"
app:layout_constraintEnd_toStartOf="@+id/save_button"
app:layout_constraintTop_toTopOf="@+id/statistics_track_name_headline"
app:srcCompat="@drawable/ic_edit_24dp" />
app:srcCompat="@drawable/ic_delete_24dp" />
<ImageButton
android:id="@+id/save_button"
@ -68,7 +49,6 @@
android:layout_height="wrap_content"
android:backgroundTint="@color/default_transparent"
android:contentDescription="@string/descr_statistics_sheet_save_button"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="@+id/statistics_track_name_headline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/statistics_track_name_headline"

View File

@ -5,7 +5,7 @@
<!-- please do not translate app_name - transcription into different alphabet types is fine though -->
<!-- Tabs -->
<string name="tab_map">Map</string>
<string name="tab_tracks">Tracks</string>
<string name="tab_tracks">History</string>
<string name="tab_settings">Settings</string>
<!-- Notification -->
<string name="notification_title_trackbook_running">Trackbook running</string>