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_OMIT_RESTS: String = "prefOmitRests"
|
||||||
const val PREF_COMMIT_INTERVAL: String = "prefCommitInterval"
|
const val PREF_COMMIT_INTERVAL: String = "prefCommitInterval"
|
||||||
const val PREF_DEVICE_ID: String = "prefDeviceID"
|
const val PREF_DEVICE_ID: String = "prefDeviceID"
|
||||||
|
const val PREF_DATABASE_DIRECTORY: String = "prefDatabaseDirectory"
|
||||||
|
|
||||||
// states
|
// states
|
||||||
const val STATE_TRACKING_STOPPED: Int = 0
|
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_LOCATION_AGE: Long = 5_000_000_000L // 5s in nanoseconds
|
||||||
const val DEFAULT_THRESHOLD_DISTANCE: Float = 15f // 15 meters
|
const val DEFAULT_THRESHOLD_DISTANCE: Float = 15f // 15 meters
|
||||||
const val DEFAULT_ZOOM_LEVEL: Double = 16.0
|
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
|
const val ALTITUDE_MEASUREMENT_ERROR_THRESHOLD = 10 // altitude changes of 10 meter or more (per 15 seconds) are being discarded
|
||||||
|
|
||||||
// notification
|
// notification
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.y20k.trackbook
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
@ -145,6 +146,11 @@ class MainActivity: AppCompatActivity()
|
||||||
Log.i("VOUSSOIR", "MainActivity: device_id has changed.")
|
Log.i("VOUSSOIR", "MainActivity: device_id has changed.")
|
||||||
trackbook.load_database()
|
trackbook.load_database()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.PREF_DATABASE_DIRECTORY ->
|
||||||
|
{
|
||||||
|
trackbook.load_database()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,8 @@ class MapFragment : Fragment()
|
||||||
lateinit var rootView: View
|
lateinit var rootView: View
|
||||||
var userInteraction: Boolean = false
|
var userInteraction: Boolean = false
|
||||||
lateinit var currentLocationButton: FloatingActionButton
|
lateinit var currentLocationButton: FloatingActionButton
|
||||||
|
lateinit var zoom_in_button: FloatingActionButton
|
||||||
|
lateinit var zoom_out_button: FloatingActionButton
|
||||||
lateinit var mainButton: ExtendedFloatingActionButton
|
lateinit var mainButton: ExtendedFloatingActionButton
|
||||||
private lateinit var mapView: MapView
|
private lateinit var mapView: MapView
|
||||||
private var current_position_overlays = ArrayList<Overlay>()
|
private var current_position_overlays = ArrayList<Overlay>()
|
||||||
|
@ -117,6 +119,8 @@ class MapFragment : Fragment()
|
||||||
rootView = inflater.inflate(R.layout.fragment_map, container, false)
|
rootView = inflater.inflate(R.layout.fragment_map, container, false)
|
||||||
mapView = rootView.findViewById(R.id.map)
|
mapView = rootView.findViewById(R.id.map)
|
||||||
currentLocationButton = rootView.findViewById(R.id.location_button)
|
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)
|
mainButton = rootView.findViewById(R.id.main_button)
|
||||||
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
|
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
|
||||||
|
|
||||||
|
@ -167,11 +171,17 @@ class MapFragment : Fragment()
|
||||||
addInteractionListener()
|
addInteractionListener()
|
||||||
|
|
||||||
// set up buttons
|
// set up buttons
|
||||||
|
mainButton.setOnClickListener {
|
||||||
|
handleTrackingManagementMenu()
|
||||||
|
}
|
||||||
currentLocationButton.setOnClickListener {
|
currentLocationButton.setOnClickListener {
|
||||||
centerMap(currentBestLocation, animated = true)
|
centerMap(currentBestLocation, animated = true)
|
||||||
}
|
}
|
||||||
mainButton.setOnClickListener {
|
zoom_in_button.setOnClickListener {
|
||||||
handleTrackingManagementMenu()
|
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)
|
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
@ -481,7 +491,7 @@ class MapFragment : Fragment()
|
||||||
currentLocationButton.isVisible = true
|
currentLocationButton.isVisible = true
|
||||||
if (! trackbook.database.ready)
|
if (! trackbook.database.ready)
|
||||||
{
|
{
|
||||||
mainButton.text = "Database not ready"
|
mainButton.text = requireContext().getString(R.string.button_not_ready)
|
||||||
mainButton.icon = null
|
mainButton.icon = null
|
||||||
}
|
}
|
||||||
else if (trackingState == Keys.STATE_TRACKING_STOPPED)
|
else if (trackingState == Keys.STATE_TRACKING_STOPPED)
|
||||||
|
|
|
@ -17,21 +17,35 @@
|
||||||
package org.y20k.trackbook
|
package org.y20k.trackbook
|
||||||
|
|
||||||
import YesNoDialog
|
import YesNoDialog
|
||||||
|
import android.app.Activity
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
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.os.Bundle
|
||||||
|
import android.provider.DocumentsContract
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
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.AppThemeHelper
|
||||||
import org.y20k.trackbook.helpers.LengthUnitHelper
|
import org.y20k.trackbook.helpers.LengthUnitHelper
|
||||||
import org.y20k.trackbook.helpers.LogHelper
|
import org.y20k.trackbook.helpers.LogHelper
|
||||||
import org.y20k.trackbook.helpers.PreferencesHelper
|
import org.y20k.trackbook.helpers.PreferencesHelper
|
||||||
import org.y20k.trackbook.helpers.random_device_id
|
import org.y20k.trackbook.helpers.random_device_id
|
||||||
|
|
||||||
|
const val INTENT_DATABASE_DIRECTORY_PICKER = 12121
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SettingsFragment class
|
* SettingsFragment class
|
||||||
*/
|
*/
|
||||||
|
@ -101,7 +115,6 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
screen.addPreference(preferenceThemeSelection)
|
screen.addPreference(preferenceThemeSelection)
|
||||||
|
|
||||||
// set up "Recording Accuracy" preference
|
// set up "Recording Accuracy" preference
|
||||||
val DEFAULT_OMIT_RESTS = true
|
|
||||||
val preferenceOmitRests: SwitchPreferenceCompat = SwitchPreferenceCompat(activity as Context)
|
val preferenceOmitRests: SwitchPreferenceCompat = SwitchPreferenceCompat(activity as Context)
|
||||||
preferenceOmitRests.isSingleLineTitle = false
|
preferenceOmitRests.isSingleLineTitle = false
|
||||||
preferenceOmitRests.title = getString(R.string.pref_omit_rests_title)
|
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.key = Keys.PREF_OMIT_RESTS
|
||||||
preferenceOmitRests.summaryOn = getString(R.string.pref_omit_rests_on)
|
preferenceOmitRests.summaryOn = getString(R.string.pref_omit_rests_on)
|
||||||
preferenceOmitRests.summaryOff = getString(R.string.pref_omit_rests_off)
|
preferenceOmitRests.summaryOff = getString(R.string.pref_omit_rests_off)
|
||||||
preferenceOmitRests.setDefaultValue(DEFAULT_OMIT_RESTS)
|
preferenceOmitRests.setDefaultValue(Keys.DEFAULT_OMIT_RESTS)
|
||||||
preferenceCategoryGeneral.contains(preferenceOmitRests)
|
preferenceCategoryGeneral.contains(preferenceOmitRests)
|
||||||
screen.addPreference(preferenceOmitRests)
|
screen.addPreference(preferenceOmitRests)
|
||||||
|
|
||||||
|
@ -119,13 +132,56 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
preferenceDeviceID.key = Keys.PREF_DEVICE_ID
|
preferenceDeviceID.key = Keys.PREF_DEVICE_ID
|
||||||
preferenceDeviceID.summary = getString(R.string.pref_device_id_summary) + "\n" + PreferencesHelper.load_device_id()
|
preferenceDeviceID.summary = getString(R.string.pref_device_id_summary) + "\n" + PreferencesHelper.load_device_id()
|
||||||
preferenceDeviceID.setDefaultValue(random_device_id())
|
preferenceDeviceID.setDefaultValue(random_device_id())
|
||||||
preferenceCategoryGeneral.contains(preferenceDeviceID)
|
|
||||||
preferenceDeviceID.setOnPreferenceChangeListener { preference, newValue ->
|
preferenceDeviceID.setOnPreferenceChangeListener { preference, newValue ->
|
||||||
preferenceDeviceID.summary = getString(R.string.pref_device_id_summary) + "\n" + newValue
|
preferenceDeviceID.summary = getString(R.string.pref_device_id_summary) + "\n" + newValue
|
||||||
return@setOnPreferenceChangeListener true
|
return@setOnPreferenceChangeListener true
|
||||||
}
|
}
|
||||||
|
preferenceCategoryGeneral.contains(preferenceDeviceID)
|
||||||
screen.addPreference(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)
|
val preferenceCategoryAbout: PreferenceCategory = PreferenceCategory(context)
|
||||||
preferenceCategoryAbout.title = getString(R.string.pref_about_title)
|
preferenceCategoryAbout.title = getString(R.string.pref_about_title)
|
||||||
screen.addPreference(preferenceCategoryAbout)
|
screen.addPreference(preferenceCategoryAbout)
|
||||||
|
|
|
@ -66,9 +66,20 @@ class Trackbook(): Application() {
|
||||||
fun load_database()
|
fun load_database()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "Trackbook.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)
|
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()
|
this.load_homepoints()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -77,6 +88,7 @@ class Trackbook(): Application() {
|
||||||
}
|
}
|
||||||
this.call_database_changed_listeners()
|
this.call_database_changed_listeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load_homepoints()
|
fun load_homepoints()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "Trackbook.load_homepoints")
|
Log.i("VOUSSOIR", "Trackbook.load_homepoints")
|
||||||
|
|
|
@ -59,6 +59,7 @@ class TrackerService: Service(), SensorEventListener
|
||||||
var commitInterval: Int = Keys.COMMIT_INTERVAL
|
var commitInterval: Int = Keys.COMMIT_INTERVAL
|
||||||
var currentBestLocation: Location = getDefaultLocation()
|
var currentBestLocation: Location = getDefaultLocation()
|
||||||
var lastCommit: Date = Keys.DEFAULT_DATE
|
var lastCommit: Date = Keys.DEFAULT_DATE
|
||||||
|
var location_min_time_ms: Long = 0
|
||||||
var stepCountOffset: Float = 0f
|
var stepCountOffset: Float = 0f
|
||||||
lateinit var track: Track
|
lateinit var track: Track
|
||||||
var gpsLocationListenerRegistered: Boolean = false
|
var gpsLocationListenerRegistered: Boolean = false
|
||||||
|
@ -98,7 +99,7 @@ class TrackerService: Service(), SensorEventListener
|
||||||
|
|
||||||
locationManager.requestLocationUpdates(
|
locationManager.requestLocationUpdates(
|
||||||
LocationManager.GPS_PROVIDER,
|
LocationManager.GPS_PROVIDER,
|
||||||
0,
|
location_min_time_ms,
|
||||||
0f,
|
0f,
|
||||||
gpsLocationListener,
|
gpsLocationListener,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
package org.y20k.trackbook.helpers
|
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.lang.Math.abs
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US)
|
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US)
|
||||||
private val RNG = SecureRandom()
|
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)
|
.setPointStyle(style)
|
||||||
.setRadius(6F * scalingFactor) // radius is set in px - scaling factor makes that display density independent (= dp)
|
.setRadius(6F * scalingFactor) // radius is set in px - scaling factor makes that display density independent (= dp)
|
||||||
.setIsClickable(true)
|
.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)
|
val overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
|
||||||
map_view.overlays.add(overlay)
|
map_view.overlays.add(overlay)
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,16 @@ object PreferencesHelper {
|
||||||
return v
|
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
|
fun loadZoomLevel(): Double
|
||||||
{
|
{
|
||||||
return sharedPreferences.getDouble(Keys.PREF_MAP_ZOOM_LEVEL, Keys.DEFAULT_ZOOM_LEVEL)
|
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:layout_constraintEnd_toEndOf="parent"
|
||||||
app:tint="@color/location_button_icon" />
|
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 -->
|
<!-- GROUPS -->
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
<string name="button_pause">Stop</string>
|
<string name="button_pause">Stop</string>
|
||||||
<string name="button_save">Save</string>
|
<string name="button_save">Save</string>
|
||||||
<string name="button_start">Record</string>
|
<string name="button_start">Record</string>
|
||||||
|
<string name="button_not_ready">Database not set up.</string>
|
||||||
<!-- Dialogs -->
|
<!-- Dialogs -->
|
||||||
<string name="dialog_delete_current_recording_message">Discard current recording?</string>
|
<string name="dialog_delete_current_recording_message">Discard current recording?</string>
|
||||||
<string name="dialog_delete_current_recording_button_discard">Discard</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_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_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_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_auto_export_interval_title">Auto Export Interval</string>
|
||||||
<string name="pref_device_id">Device ID</string>
|
<string name="pref_device_id">Device ID</string>
|
||||||
<string name="pref_advanced_title">Advanced</string>
|
<string name="pref_advanced_title">Advanced</string>
|
||||||
|
@ -117,6 +119,8 @@
|
||||||
<!-- Descriptions -->
|
<!-- Descriptions -->
|
||||||
<string name="descr_button_delete">Discard recording</string>
|
<string name="descr_button_delete">Discard recording</string>
|
||||||
<string name="descr_button_location">Center on current location</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_pause">Stop recording</string>
|
||||||
<string name="descr_button_resume">Resume recording</string>
|
<string name="descr_button_resume">Resume recording</string>
|
||||||
<string name="descr_button_save">Save recording</string>
|
<string name="descr_button_save">Save recording</string>
|
||||||
|
|
Loading…
Reference in a new issue