checkpoint
This commit is contained in:
parent
ffd5fb6af3
commit
172ca703a9
14 changed files with 325 additions and 13 deletions
|
@ -58,6 +58,7 @@ object Keys {
|
|||
const val PREF_OMIT_RESTS: String = "prefOmitRests"
|
||||
const val PREF_COMMIT_INTERVAL: String = "prefCommitInterval"
|
||||
const val PREF_DEVICE_ID: String = "prefDeviceID"
|
||||
const val PREF_DATABASE_DIRECTORY: String = "prefDatabaseDirectory"
|
||||
|
||||
// states
|
||||
const val STATE_TRACKING_STOPPED: Int = 0
|
||||
|
@ -117,6 +118,7 @@ object Keys {
|
|||
const val DEFAULT_THRESHOLD_LOCATION_AGE: Long = 5_000_000_000L // 5s in nanoseconds
|
||||
const val DEFAULT_THRESHOLD_DISTANCE: Float = 15f // 15 meters
|
||||
const val DEFAULT_ZOOM_LEVEL: Double = 16.0
|
||||
const val DEFAULT_OMIT_RESTS: Boolean = true
|
||||
const val ALTITUDE_MEASUREMENT_ERROR_THRESHOLD = 10 // altitude changes of 10 meter or more (per 15 seconds) are being discarded
|
||||
|
||||
// notification
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.y20k.trackbook
|
|||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
|
@ -145,6 +146,11 @@ class MainActivity: AppCompatActivity()
|
|||
Log.i("VOUSSOIR", "MainActivity: device_id has changed.")
|
||||
trackbook.load_database()
|
||||
}
|
||||
|
||||
Keys.PREF_DATABASE_DIRECTORY ->
|
||||
{
|
||||
trackbook.load_database()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,8 @@ class MapFragment : Fragment()
|
|||
lateinit var rootView: View
|
||||
var userInteraction: Boolean = false
|
||||
lateinit var currentLocationButton: FloatingActionButton
|
||||
lateinit var zoom_in_button: FloatingActionButton
|
||||
lateinit var zoom_out_button: FloatingActionButton
|
||||
lateinit var mainButton: ExtendedFloatingActionButton
|
||||
private lateinit var mapView: MapView
|
||||
private var current_position_overlays = ArrayList<Overlay>()
|
||||
|
@ -117,6 +119,8 @@ class MapFragment : Fragment()
|
|||
rootView = inflater.inflate(R.layout.fragment_map, container, false)
|
||||
mapView = rootView.findViewById(R.id.map)
|
||||
currentLocationButton = rootView.findViewById(R.id.location_button)
|
||||
zoom_in_button = rootView.findViewById(R.id.zoom_in_button)
|
||||
zoom_out_button = rootView.findViewById(R.id.zoom_out_button)
|
||||
mainButton = rootView.findViewById(R.id.main_button)
|
||||
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
|
||||
|
||||
|
@ -167,11 +171,17 @@ class MapFragment : Fragment()
|
|||
addInteractionListener()
|
||||
|
||||
// set up buttons
|
||||
mainButton.setOnClickListener {
|
||||
handleTrackingManagementMenu()
|
||||
}
|
||||
currentLocationButton.setOnClickListener {
|
||||
centerMap(currentBestLocation, animated = true)
|
||||
}
|
||||
mainButton.setOnClickListener {
|
||||
handleTrackingManagementMenu()
|
||||
zoom_in_button.setOnClickListener {
|
||||
controller.zoomTo(mapView.zoomLevelDouble + 0.5, 250)
|
||||
}
|
||||
zoom_out_button.setOnClickListener {
|
||||
controller.zoomTo(mapView.zoomLevelDouble - 0.5, 250)
|
||||
}
|
||||
|
||||
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
|
@ -481,7 +491,7 @@ class MapFragment : Fragment()
|
|||
currentLocationButton.isVisible = true
|
||||
if (! trackbook.database.ready)
|
||||
{
|
||||
mainButton.text = "Database not ready"
|
||||
mainButton.text = requireContext().getString(R.string.button_not_ready)
|
||||
mainButton.icon = null
|
||||
}
|
||||
else if (trackingState == Keys.STATE_TRACKING_STOPPED)
|
||||
|
|
|
@ -17,21 +17,35 @@
|
|||
package org.y20k.trackbook
|
||||
|
||||
import YesNoDialog
|
||||
import android.app.Activity
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.DocumentsContract
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.preference.*
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import androidx.preference.contains
|
||||
import get_path_from_uri
|
||||
import org.y20k.trackbook.helpers.AppThemeHelper
|
||||
import org.y20k.trackbook.helpers.LengthUnitHelper
|
||||
import org.y20k.trackbook.helpers.LogHelper
|
||||
import org.y20k.trackbook.helpers.PreferencesHelper
|
||||
import org.y20k.trackbook.helpers.random_device_id
|
||||
|
||||
const val INTENT_DATABASE_DIRECTORY_PICKER = 12121
|
||||
|
||||
/*
|
||||
* SettingsFragment class
|
||||
*/
|
||||
|
@ -101,7 +115,6 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
screen.addPreference(preferenceThemeSelection)
|
||||
|
||||
// set up "Recording Accuracy" preference
|
||||
val DEFAULT_OMIT_RESTS = true
|
||||
val preferenceOmitRests: SwitchPreferenceCompat = SwitchPreferenceCompat(activity as Context)
|
||||
preferenceOmitRests.isSingleLineTitle = false
|
||||
preferenceOmitRests.title = getString(R.string.pref_omit_rests_title)
|
||||
|
@ -109,7 +122,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
preferenceOmitRests.key = Keys.PREF_OMIT_RESTS
|
||||
preferenceOmitRests.summaryOn = getString(R.string.pref_omit_rests_on)
|
||||
preferenceOmitRests.summaryOff = getString(R.string.pref_omit_rests_off)
|
||||
preferenceOmitRests.setDefaultValue(DEFAULT_OMIT_RESTS)
|
||||
preferenceOmitRests.setDefaultValue(Keys.DEFAULT_OMIT_RESTS)
|
||||
preferenceCategoryGeneral.contains(preferenceOmitRests)
|
||||
screen.addPreference(preferenceOmitRests)
|
||||
|
||||
|
@ -119,13 +132,56 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
preferenceDeviceID.key = Keys.PREF_DEVICE_ID
|
||||
preferenceDeviceID.summary = getString(R.string.pref_device_id_summary) + "\n" + PreferencesHelper.load_device_id()
|
||||
preferenceDeviceID.setDefaultValue(random_device_id())
|
||||
preferenceCategoryGeneral.contains(preferenceDeviceID)
|
||||
preferenceDeviceID.setOnPreferenceChangeListener { preference, newValue ->
|
||||
preferenceDeviceID.summary = getString(R.string.pref_device_id_summary) + "\n" + newValue
|
||||
return@setOnPreferenceChangeListener true
|
||||
}
|
||||
preferenceCategoryGeneral.contains(preferenceDeviceID)
|
||||
screen.addPreference(preferenceDeviceID)
|
||||
|
||||
val preferenceDatabaseFolder: Preference = Preference(context)
|
||||
var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
Log.i("VOUSSOIR", "I'm not dead yet.")
|
||||
if (result.resultCode != Activity.RESULT_OK)
|
||||
{
|
||||
return@registerForActivityResult
|
||||
}
|
||||
if (result.data == null)
|
||||
{
|
||||
return@registerForActivityResult
|
||||
}
|
||||
if (result.data!!.data == null)
|
||||
{
|
||||
return@registerForActivityResult
|
||||
}
|
||||
val uri: Uri = result.data!!.data!!
|
||||
val docUri = DocumentsContract.buildDocumentUriUsingTree(uri, DocumentsContract.getTreeDocumentId(uri))
|
||||
val path: String = get_path_from_uri(context, docUri) ?: ""
|
||||
Log.i("VOUSSOIR", "We got " + path)
|
||||
PreferencesHelper.save_database_folder(path)
|
||||
preferenceDatabaseFolder.summary = (getString(R.string.pref_database_folder_summary) + "\n" + path).trim()
|
||||
}
|
||||
preferenceDatabaseFolder.title = "Database Directory"
|
||||
preferenceDatabaseFolder.setIcon(R.drawable.ic_save_to_storage_24dp)
|
||||
preferenceDatabaseFolder.key = Keys.PREF_DATABASE_DIRECTORY
|
||||
preferenceDatabaseFolder.summary = (getString(R.string.pref_database_folder_summary) + "\n" + PreferencesHelper.load_database_folder()).trim()
|
||||
preferenceDatabaseFolder.setOnPreferenceClickListener {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
{
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
resultLauncher.launch(intent)
|
||||
}
|
||||
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
preferenceDatabaseFolder.setOnPreferenceChangeListener { preference, newValue ->
|
||||
preferenceDatabaseFolder.summary = "Directory to contain your database file." + "\n" + newValue
|
||||
return@setOnPreferenceChangeListener true
|
||||
}
|
||||
|
||||
preferenceCategoryGeneral.contains(preferenceDatabaseFolder)
|
||||
screen.addPreference(preferenceDatabaseFolder)
|
||||
|
||||
val preferenceCategoryAbout: PreferenceCategory = PreferenceCategory(context)
|
||||
preferenceCategoryAbout.title = getString(R.string.pref_about_title)
|
||||
screen.addPreference(preferenceCategoryAbout)
|
||||
|
|
|
@ -66,9 +66,20 @@ class Trackbook(): Application() {
|
|||
fun load_database()
|
||||
{
|
||||
Log.i("VOUSSOIR", "Trackbook.load_database")
|
||||
val folder = PreferencesHelper.load_database_folder()
|
||||
this.database.commit()
|
||||
if (this.database.ready)
|
||||
{
|
||||
this.database.close()
|
||||
}
|
||||
if (folder == "")
|
||||
{
|
||||
this.database.ready = false
|
||||
return
|
||||
}
|
||||
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
|
||||
{
|
||||
this.database.connect(File("/storage/emulated/0/Syncthing/GPX/trkpt_${PreferencesHelper.load_device_id()}.db"))
|
||||
this.database.connect(File(folder + "/trkpt_${PreferencesHelper.load_device_id()}.db"))
|
||||
this.load_homepoints()
|
||||
}
|
||||
else
|
||||
|
@ -77,6 +88,7 @@ class Trackbook(): Application() {
|
|||
}
|
||||
this.call_database_changed_listeners()
|
||||
}
|
||||
|
||||
fun load_homepoints()
|
||||
{
|
||||
Log.i("VOUSSOIR", "Trackbook.load_homepoints")
|
||||
|
|
|
@ -59,6 +59,7 @@ class TrackerService: Service(), SensorEventListener
|
|||
var commitInterval: Int = Keys.COMMIT_INTERVAL
|
||||
var currentBestLocation: Location = getDefaultLocation()
|
||||
var lastCommit: Date = Keys.DEFAULT_DATE
|
||||
var location_min_time_ms: Long = 0
|
||||
var stepCountOffset: Float = 0f
|
||||
lateinit var track: Track
|
||||
var gpsLocationListenerRegistered: Boolean = false
|
||||
|
@ -98,7 +99,7 @@ class TrackerService: Service(), SensorEventListener
|
|||
|
||||
locationManager.requestLocationUpdates(
|
||||
LocationManager.GPS_PROVIDER,
|
||||
0,
|
||||
location_min_time_ms,
|
||||
0f,
|
||||
gpsLocationListener,
|
||||
)
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
package org.y20k.trackbook.helpers
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import java.lang.Math.abs
|
||||
import java.security.SecureRandom
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.random.Random
|
||||
|
||||
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US)
|
||||
private val RNG = SecureRandom()
|
||||
|
|
144
app/src/main/java/org/y20k/trackbook/get_path_from_uri.kt
Normal file
144
app/src/main/java/org/y20k/trackbook/get_path_from_uri.kt
Normal file
|
@ -0,0 +1,144 @@
|
|||
// Thank you @asifmujteba!
|
||||
// https://gist.github.com/asifmujteba/d89ba9074bc941de1eaa
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
fun get_path_from_uri(context: Context, uri: Uri): String?
|
||||
{
|
||||
val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
|
||||
|
||||
// DocumentProvider
|
||||
if (isKitKat && DocumentsContract.isDocumentUri(context, uri))
|
||||
{
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri))
|
||||
{
|
||||
val docId = DocumentsContract.getDocumentId(uri)
|
||||
val split = docId.split(":").toTypedArray()
|
||||
val type = split[0]
|
||||
if ("primary".equals(type, ignoreCase = true))
|
||||
{
|
||||
return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
|
||||
}
|
||||
|
||||
// TODO handle non-primary volumes
|
||||
}
|
||||
else if (isDownloadsDocument(uri))
|
||||
{
|
||||
val id = DocumentsContract.getDocumentId(uri)
|
||||
val contentUri: Uri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
|
||||
return getDataColumn(context, contentUri, null, null)
|
||||
}
|
||||
else if (isMediaDocument(uri))
|
||||
{
|
||||
val docId = DocumentsContract.getDocumentId(uri)
|
||||
val split = docId.split(":").toTypedArray()
|
||||
val type = split[0]
|
||||
var contentUri: Uri? = null
|
||||
if ("image" == type)
|
||||
{
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
}
|
||||
else if ("video" == type)
|
||||
{
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
||||
}
|
||||
else if ("audio" == type)
|
||||
{
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
||||
}
|
||||
val selection = "_id=?"
|
||||
val selectionArgs = arrayOf(
|
||||
split[1]
|
||||
)
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs)
|
||||
}
|
||||
}
|
||||
else if ("content".equals(uri.getScheme(), ignoreCase = true))
|
||||
{
|
||||
|
||||
// Return the remote address
|
||||
return if (isGooglePhotosUri(uri)) uri.getLastPathSegment()
|
||||
else getDataColumn(context,
|
||||
uri,
|
||||
null,
|
||||
null)
|
||||
}
|
||||
else if ("file".equals(uri.getScheme(), ignoreCase = true))
|
||||
{
|
||||
return uri.getPath()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getDataColumn(
|
||||
context: Context, uri: Uri?, selection: String?,
|
||||
selectionArgs: Array<String>?,
|
||||
): String?
|
||||
{
|
||||
var cursor: Cursor? = null
|
||||
val column = "_data"
|
||||
val projection = arrayOf(
|
||||
column
|
||||
)
|
||||
try
|
||||
{
|
||||
cursor = context.getContentResolver().query(uri!!, projection, selection, selectionArgs,
|
||||
null)
|
||||
if (cursor != null && cursor.moveToFirst())
|
||||
{
|
||||
val index: Int = cursor.getColumnIndexOrThrow(column)
|
||||
return cursor.getString(index)
|
||||
}
|
||||
} finally
|
||||
{
|
||||
if (cursor != null) cursor.close()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
fun isExternalStorageDocument(uri: Uri): Boolean
|
||||
{
|
||||
return "com.android.externalstorage.documents" == uri.getAuthority()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
fun isDownloadsDocument(uri: Uri): Boolean
|
||||
{
|
||||
return "com.android.providers.downloads.documents" == uri.getAuthority()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
fun isMediaDocument(uri: Uri): Boolean
|
||||
{
|
||||
return "com.android.providers.media.documents" == uri.getAuthority()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is Google Photos.
|
||||
*/
|
||||
fun isGooglePhotosUri(uri: Uri): Boolean
|
||||
{
|
||||
return "com.google.android.apps.photos.content" == uri.getAuthority()
|
||||
}
|
|
@ -71,7 +71,7 @@ fun createTrackOverlay(context: Context, map_view: MapView, track: Track, tracki
|
|||
.setPointStyle(style)
|
||||
.setRadius(6F * scalingFactor) // radius is set in px - scaling factor makes that display density independent (= dp)
|
||||
.setIsClickable(true)
|
||||
.setCellSize(15) // 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.
|
||||
.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)
|
||||
}
|
||||
|
|
|
@ -56,6 +56,16 @@ object PreferencesHelper {
|
|||
return v
|
||||
}
|
||||
|
||||
fun load_database_folder(): String
|
||||
{
|
||||
return sharedPreferences.getString(Keys.PREF_DATABASE_DIRECTORY, "") ?: ""
|
||||
}
|
||||
|
||||
fun save_database_folder(path: String)
|
||||
{
|
||||
sharedPreferences.edit { putString(Keys.PREF_DATABASE_DIRECTORY, path) }
|
||||
}
|
||||
|
||||
fun loadZoomLevel(): Double
|
||||
{
|
||||
return sharedPreferences.getDouble(Keys.PREF_MAP_ZOOM_LEVEL, Keys.DEFAULT_ZOOM_LEVEL)
|
||||
|
|
16
app/src/main/res/drawable/ic_zoom_in_24dp.xml
Normal file
16
app/src/main/res/drawable/ic_zoom_in_24dp.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:name="vector"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:name="path"
|
||||
android:fillColor="@color/location_button_background"
|
||||
android:pathData="M 1.98 11 L 22.02 11 L 22.02 13 L 1.98 13 Z"/>
|
||||
<path
|
||||
android:name="path_1"
|
||||
android:fillColor="@color/location_button_background"
|
||||
android:pathData="M 11 22.02 L 11 1.98 L 13 1.98 L 13 22.02 Z"/>
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_zoom_out_24dp.xml
Normal file
12
app/src/main/res/drawable/ic_zoom_out_24dp.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:name="vector"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:name="path"
|
||||
android:fillColor="@color/location_button_background"
|
||||
android:pathData="M 1.98 11 L 22.02 11 L 22.02 13 L 1.98 13 Z"/>
|
||||
</vector>
|
|
@ -58,6 +58,36 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:tint="@color/location_button_icon" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/zoom_out_button"
|
||||
style="@style/Widget.MaterialComponents.FloatingActionButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:contentDescription="@string/descr_button_zoom_out"
|
||||
android:src="@drawable/ic_zoom_out_24dp"
|
||||
app:backgroundTint="@color/location_button_background"
|
||||
app:fabSize="mini"
|
||||
app:layout_constraintBottom_toTopOf="@+id/location_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:tint="@color/location_button_icon" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/zoom_in_button"
|
||||
style="@style/Widget.MaterialComponents.FloatingActionButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:contentDescription="@string/descr_button_zoom_in"
|
||||
android:src="@drawable/ic_zoom_in_24dp"
|
||||
app:backgroundTint="@color/location_button_background"
|
||||
app:fabSize="mini"
|
||||
app:layout_constraintBottom_toTopOf="@+id/zoom_out_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:tint="@color/location_button_icon" />
|
||||
|
||||
<!-- GROUPS -->
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<string name="button_pause">Stop</string>
|
||||
<string name="button_save">Save</string>
|
||||
<string name="button_start">Record</string>
|
||||
<string name="button_not_ready">Database not set up.</string>
|
||||
<!-- Dialogs -->
|
||||
<string name="dialog_delete_current_recording_message">Discard current recording?</string>
|
||||
<string name="dialog_delete_current_recording_button_discard">Discard</string>
|
||||
|
@ -84,7 +85,8 @@
|
|||
<string name="pref_altitude_smoothing_value_summary" translatable="false">Number of waypoints used to smooth the elevation curve.</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_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_auto_export_interval_title">Auto Export Interval</string>
|
||||
<string name="pref_device_id">Device ID</string>
|
||||
<string name="pref_advanced_title">Advanced</string>
|
||||
|
@ -117,6 +119,8 @@
|
|||
<!-- Descriptions -->
|
||||
<string name="descr_button_delete">Discard recording</string>
|
||||
<string name="descr_button_location">Center on current location</string>
|
||||
<string name="descr_button_zoom_out">Zoom out</string>
|
||||
<string name="descr_button_zoom_in">Zoom in</string>
|
||||
<string name="descr_button_pause">Stop recording</string>
|
||||
<string name="descr_button_resume">Resume recording</string>
|
||||
<string name="descr_button_save">Save recording</string>
|
||||
|
|
Loading…
Reference in a new issue