checkpoint

master
voussoir 2023-03-09 20:24:06 -08:00
parent 2568af3bb1
commit df77b089ac
36 changed files with 406 additions and 804 deletions

View File

@ -1,6 +1,4 @@
package org.y20k.trackbook.core package org.y20k.trackbook
import android.content.Context
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteDatabase.openOrCreateDatabase import android.database.sqlite.SQLiteDatabase.openOrCreateDatabase
import android.util.Log import android.util.Log
@ -11,6 +9,7 @@ class Database()
var ready: Boolean = false var ready: Boolean = false
lateinit var file: File lateinit var file: File
lateinit var connection: SQLiteDatabase lateinit var connection: SQLiteDatabase
fun close() fun close()
{ {
this.connection.close() this.connection.close()
@ -19,6 +18,7 @@ class Database()
fun connect(file: File) fun connect(file: File)
{ {
Log.i("VOUSSOIR", "Connecting to database " + file.absolutePath)
this.file = file this.file = file
this.connection = openOrCreateDatabase(file, null) this.connection = openOrCreateDatabase(file, null)
this.initialize_tables() this.initialize_tables()
@ -44,7 +44,7 @@ class Database()
{ {
this.connection.beginTransaction() this.connection.beginTransaction()
this.connection.execSQL("CREATE TABLE IF NOT EXISTS meta(name TEXT PRIMARY KEY, value TEXT)") this.connection.execSQL("CREATE TABLE IF NOT EXISTS meta(name TEXT PRIMARY KEY, value TEXT)")
this.connection.execSQL("CREATE TABLE IF NOT EXISTS trkpt(lat REAL NOT NULL, lon REAL NOT NULL, time TEXT NOT NULL, accuracy REAL, device_id INTEGER NOT NULL, ele INTEGER, sat INTEGER, star INTEGER, PRIMARY KEY(lat, lon, time, device_id))") this.connection.execSQL("CREATE TABLE IF NOT EXISTS trkpt(lat REAL NOT NULL, lon REAL NOT NULL, time INTEGER NOT NULL, accuracy REAL, device_id INTEGER NOT NULL, ele INTEGER, sat INTEGER, star INTEGER, PRIMARY KEY(lat, lon, time, device_id))")
this.connection.execSQL("CREATE TABLE IF NOT EXISTS homepoints(lat REAL NOT NULL, lon REAL NOT NULL, radius REAL NOT NULL, name TEXT, PRIMARY KEY(lat, lon))") this.connection.execSQL("CREATE TABLE IF NOT EXISTS homepoints(lat REAL NOT NULL, lon REAL NOT NULL, radius REAL NOT NULL, name TEXT, PRIMARY KEY(lat, lon))")
this.connection.setTransactionSuccessful() this.connection.setTransactionSuccessful()
this.connection.endTransaction() this.connection.endTransaction()

View File

@ -1,5 +1,4 @@
package org.y20k.trackbook.core package org.y20k.trackbook
import android.location.Location import android.location.Location
import java.util.* import java.util.*

View File

@ -14,12 +14,10 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook package org.y20k.trackbook
import java.util.* import java.util.*
/* /*
* Keys object * Keys object
*/ */
@ -116,7 +114,7 @@ object Keys {
const val COMMIT_INTERVAL: Int = 30 const val COMMIT_INTERVAL: Int = 30
const val DEFAULT_ALTITUDE_SMOOTHING_VALUE: Int = 13 const val DEFAULT_ALTITUDE_SMOOTHING_VALUE: Int = 13
const val DEFAULT_THRESHOLD_LOCATION_ACCURACY: Int = 30 // 30 meters const val DEFAULT_THRESHOLD_LOCATION_ACCURACY: Int = 30 // 30 meters
const val DEFAULT_THRESHOLD_LOCATION_AGE: Long = 60_000_000_000L // one minute 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 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

View File

@ -14,19 +14,21 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook 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.SharedPreferences import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.StrictMode import android.os.StrictMode
import android.os.StrictMode.VmPolicy import android.os.StrictMode.VmPolicy
import android.util.Log
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
@ -35,55 +37,22 @@ import org.y20k.trackbook.helpers.AppThemeHelper
import org.y20k.trackbook.helpers.LogHelper import org.y20k.trackbook.helpers.LogHelper
import org.y20k.trackbook.helpers.PreferencesHelper import org.y20k.trackbook.helpers.PreferencesHelper
private const val REQUEST_EXTERNAL_STORAGE = 1 class MainActivity: AppCompatActivity()
private val PERMISSIONS_STORAGE = arrayOf<String>(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
/**
* Checks if the app has permission to write to device storage
*
* If the app does not has permission then the user will be prompted to grant permissions
*
* @param activity
*/
fun verifyStoragePermissions(activity: Activity?)
{ {
// Check if we have write permission
val permission = ActivityCompat.checkSelfPermission(activity!!,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (permission != PackageManager.PERMISSION_GRANTED)
{
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
)
}
}
/*
* MainActivity class
*/
class MainActivity : AppCompatActivity() {
/* Define log tag */
private val TAG: String = LogHelper.makeLogTag(MainActivity::class.java)
/* Main class variables */ /* Main class variables */
lateinit var trackbook: Trackbook
private lateinit var navHostFragment: NavHostFragment private lateinit var navHostFragment: NavHostFragment
private lateinit var bottomNavigationView: BottomNavigationView private lateinit var bottomNavigationView: BottomNavigationView
/* Overrides onCreate from AppCompatActivity */ /* Overrides onCreate from AppCompatActivity */
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?)
{
trackbook = (applicationContext as Trackbook)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
verifyStoragePermissions(this) request_permissions(this)
// todo: remove after testing finished // todo: remove after testing finished
if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
StrictMode.setVmPolicy( StrictMode.setVmPolicy(
VmPolicy.Builder() VmPolicy.Builder()
.detectNonSdkApiUsage() .detectNonSdkApiUsage()
@ -109,8 +78,10 @@ class MainActivity : AppCompatActivity() {
// listen for navigation changes // listen for navigation changes
navHostFragment.navController.addOnDestinationChangedListener { _, destination, _ -> navHostFragment.navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) { when (destination.id)
R.id.fragment_track -> { {
R.id.fragment_track ->
{
runOnUiThread { runOnUiThread {
run { run {
// mark menu item "Tracks" as checked // mark menu item "Tracks" as checked
@ -118,7 +89,8 @@ class MainActivity : AppCompatActivity() {
} }
} }
} }
else -> { else ->
{
// do nothing // do nothing
} }
} }
@ -128,26 +100,62 @@ class MainActivity : AppCompatActivity() {
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener) PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
} }
private fun request_permissions(activity: Activity)
{
Log.i("VOUSSOIR", "MainActivity requests permissions.")
val permissions_wanted = arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
val permissions_needed = ArrayList<String>()
for (permission in permissions_wanted)
{
if (ContextCompat.checkSelfPermission(applicationContext, permission) != PackageManager.PERMISSION_GRANTED)
{
Log.i("VOUSSOIR", "We need " + permission)
permissions_needed.add(permission)
}
}
val result = requestPermissions(permissions_wanted, 1);
Log.i("VOUSSOIR", "Permissions result " + result)
}
/* Overrides onDestroy from AppCompatActivity */ /* Overrides onDestroy from AppCompatActivity */
override fun onDestroy() { override fun onDestroy()
{
super.onDestroy() super.onDestroy()
// unregister listener for changes in shared preferences // unregister listener for changes in shared preferences
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener) PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
} }
/* /*
* Defines the listener for changes in shared preferences * Defines the listener for changes in shared preferences
*/ */
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
when (key) { when (key)
Keys.PREF_THEME_SELECTION -> { {
Keys.PREF_THEME_SELECTION ->
{
AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection()) AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection())
} }
Keys.PREF_DEVICE_ID ->
{
Log.i("VOUSSOIR", "MainActivity: device_id has changed.")
trackbook.load_database()
} }
} }
/* }
* End of declaration
*/ override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
Log.i("VOUSSOIR", "MainActivity tries to load the database.")
trackbook.load_database()
}
} }

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook package org.y20k.trackbook
import android.Manifest import android.Manifest
@ -25,17 +24,18 @@ import android.os.*
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 android.view.WindowManager
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.y20k.trackbook.core.Track import org.y20k.trackbook.Track
import org.y20k.trackbook.helpers.* import org.y20k.trackbook.helpers.*
import org.y20k.trackbook.ui.MapFragmentLayoutHolder import org.y20k.trackbook.ui.MapFragmentLayoutHolder
/* /*
* MapFragment class * MapFragment class
*/ */
class MapFragment : Fragment(), MapOverlayHelper.MarkerListener class MapFragment : Fragment()
{ {
/* Define log tag */ /* Define log tag */
private val TAG: String = LogHelper.makeLogTag(MapFragment::class.java) private val TAG: String = LogHelper.makeLogTag(MapFragment::class.java)
@ -61,13 +61,14 @@ class MapFragment : Fragment(), MapOverlayHelper.MarkerListener
currentBestLocation = LocationHelper.getLastKnownLocation(activity as Context) currentBestLocation = LocationHelper.getLastKnownLocation(activity as Context)
// get saved tracking state // get saved tracking state
trackingState = PreferencesHelper.loadTrackingState() trackingState = PreferencesHelper.loadTrackingState()
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} }
/* Overrides onStop from Fragment */ /* Overrides onStop from Fragment */
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
// initialize layout // initialize layout
val statusBarHeight: Int = UiHelper.getStatusBarHeight(activity as Context) val statusBarHeight: Int = UiHelper.getStatusBarHeight(activity as Context)
layout = MapFragmentLayoutHolder(activity as Context, this as MapOverlayHelper.MarkerListener, inflater, container, statusBarHeight, currentBestLocation, trackingState) layout = MapFragmentLayoutHolder(activity as Context, inflater, container, statusBarHeight, currentBestLocation, trackingState)
// set up buttons // set up buttons
layout.currentLocationButton.setOnClickListener { layout.currentLocationButton.setOnClickListener {

View File

@ -14,33 +14,21 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook package org.y20k.trackbook
import YesNoDialog import YesNoDialog
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.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.EditText
import android.widget.Toast import android.widget.Toast
import androidx.preference.* import androidx.preference.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import org.y20k.trackbook.helpers.AppThemeHelper import org.y20k.trackbook.helpers.AppThemeHelper
import org.y20k.trackbook.helpers.FileHelper
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_int import org.y20k.trackbook.helpers.random_device_id
import kotlin.random.Random
/* /*
* SettingsFragment class * SettingsFragment class
@ -50,7 +38,6 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
/* Define log tag */ /* Define log tag */
private val TAG: String = LogHelper.makeLogTag(SettingsFragment::class.java) private val TAG: String = LogHelper.makeLogTag(SettingsFragment::class.java)
/* Overrides onViewCreated from PreferenceFragmentCompat */ /* Overrides onViewCreated from PreferenceFragmentCompat */
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -58,7 +45,6 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
view.setBackgroundColor(resources.getColor(R.color.app_window_background, null)) view.setBackgroundColor(resources.getColor(R.color.app_window_background, null))
} }
/* Overrides onCreatePreferences from PreferenceFragmentCompat */ /* Overrides onCreatePreferences from PreferenceFragmentCompat */
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@ -130,7 +116,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
preferenceDeviceID.setIcon(R.drawable.ic_smartphone_24dp) preferenceDeviceID.setIcon(R.drawable.ic_smartphone_24dp)
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_int().toString()) preferenceDeviceID.setDefaultValue(random_device_id())
preferenceCategoryGeneral.contains(preferenceDeviceID) preferenceCategoryGeneral.contains(preferenceDeviceID)
screen.addPreference(preferenceDeviceID) screen.addPreference(preferenceDeviceID)
@ -142,7 +128,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
val preferenceAppVersion: Preference = Preference(context) val preferenceAppVersion: Preference = Preference(context)
preferenceAppVersion.title = getString(R.string.pref_app_version_title) preferenceAppVersion.title = getString(R.string.pref_app_version_title)
preferenceAppVersion.setIcon(R.drawable.ic_info_24dp) preferenceAppVersion.setIcon(R.drawable.ic_info_24dp)
preferenceAppVersion.summary = "${getString(R.string.pref_app_version_summary)} ${BuildConfig.VERSION_NAME}" preferenceAppVersion.summary = getString(R.string.pref_app_version_summary)
preferenceAppVersion.setOnPreferenceClickListener { preferenceAppVersion.setOnPreferenceClickListener {
// copy to clipboard // copy to clipboard
val clip: ClipData = ClipData.newPlainText("simple text", preferenceAppVersion.summary) val clip: ClipData = ClipData.newPlainText("simple text", preferenceAppVersion.summary)
@ -158,7 +144,6 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
preferenceScreen = screen preferenceScreen = screen
} }
/* Overrides onYesNoDialog from YesNoDialogListener */ /* Overrides onYesNoDialog from YesNoDialogListener */
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String) { override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String) {
when (type) { when (type) {

View File

@ -14,48 +14,37 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.core package org.y20k.trackbook
import android.content.Context import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.location.Location
import android.net.Uri import android.net.Uri
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.net.toUri
import org.y20k.trackbook.Keys
import org.y20k.trackbook.helpers.DateTimeHelper
import org.y20k.trackbook.helpers.LocationHelper
import org.y20k.trackbook.helpers.iso8601
import org.y20k.trackbook.helpers.iso8601_format import org.y20k.trackbook.helpers.iso8601_format
import java.io.File
import java.net.URI
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
/*
* Track data class
*/
data class Track ( data class Track (
val database: Database, val database: Database,
val device_id: String, val device_id: String,
val start_time: Date, val start_time: Date,
val stop_time: Date, val stop_time: Date,
var name: String = "", var name: String = "",
var dequelimit: Int = 7200, val trkpts: ArrayDeque<Trkpt> = ArrayDeque<Trkpt>(),
var view_latitude: Double = Keys.DEFAULT_LATITUDE, var view_latitude: Double = Keys.DEFAULT_LATITUDE,
var view_longitude: Double = Keys.DEFAULT_LONGITUDE, var view_longitude: Double = Keys.DEFAULT_LONGITUDE,
var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMAT_VERSION, var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMAT_VERSION,
val trkpts: ArrayDeque<Trkpt> = ArrayDeque<Trkpt>(dequelimit),
var zoomLevel: Double = Keys.DEFAULT_ZOOM_LEVEL, var zoomLevel: Double = Keys.DEFAULT_ZOOM_LEVEL,
) )
{ {
fun delete() fun delete()
{ {
Log.i("VOUSSOIR", "Track.delete.")
} }
suspend fun delete_suspended(context: Context) suspend fun delete_suspended(context: Context)
@ -65,12 +54,6 @@ data class Track (
} }
} }
fun get_export_gpx_file(context: Context): File
{
val basename: String = DateTimeHelper.convertToSortableDateString(this.start_time) + " " + DateTimeHelper.convertToSortableDateString(this.start_time) + Keys.GPX_FILE_EXTENSION
return File(File("/storage/emulated/0/Syncthing/GPX"), basename)
}
fun export_gpx(context: Context, fileuri: Uri): Uri? fun export_gpx(context: Context, fileuri: Uri): Uri?
{ {
if (! database.ready) if (! database.ready)
@ -153,7 +136,8 @@ data class Track (
stats.min_altitude = trkpt.altitude stats.min_altitude = trkpt.altitude
continue continue
} }
stats.distance += LocationHelper.calculateDistance(previous.toLocation(), trkpt.toLocation()) stats.distance += previous.toLocation().distanceTo(trkpt.toLocation())
Log.i("VOUSSOIR", previous.toLocation().distanceTo(trkpt.toLocation()).toString())
val ascentdiff = trkpt.altitude - previous.altitude val ascentdiff = trkpt.altitude - previous.altitude
if (ascentdiff > 0) if (ascentdiff > 0)
{ {
@ -171,28 +155,23 @@ data class Track (
{ {
stats.min_altitude = trkpt.altitude stats.min_altitude = trkpt.altitude
} }
previous = trkpt
last = trkpt last = trkpt
} }
if (first == null || last == null) if (first == null || last == null)
{ {
return stats return stats
} }
stats.duration = last.time.time - first.time.time stats.duration = last.time - first.time
stats.velocity = stats.distance / stats.duration stats.velocity = stats.distance / stats.duration
return stats return stats
} }
fun trkpt_generator() = iterator<Trkpt> fun trkpt_generator() = iterator<Trkpt>
{ {
val cursor: Cursor = database.connection.query( val cursor: Cursor = database.connection.rawQuery(
"trkpt", "SELECT lat, lon, time, ele, accuracy, sat FROM trkpt WHERE device_id = ? AND time > ? AND time < ? ORDER BY time ASC",
arrayOf("lat", "lon", "time", "ele", "accuracy", "sat"), arrayOf(device_id, start_time.time.toString(), stop_time.time.toString())
"device_id = ? AND time > ? AND time < ?",
arrayOf(device_id, iso8601(start_time), iso8601(stop_time)),
null,
null,
"time ASC",
null,
) )
val COLUMN_LAT = cursor.getColumnIndex("lat") val COLUMN_LAT = cursor.getColumnIndex("lat")
val COLUMN_LON = cursor.getColumnIndex("lon") val COLUMN_LON = cursor.getColumnIndex("lon")
@ -204,14 +183,13 @@ data class Track (
{ {
while (cursor.moveToNext()) while (cursor.moveToNext())
{ {
val trkpt: Trkpt = Trkpt( val trkpt = Trkpt(
provider="", provider="",
latitude=cursor.getDouble(COLUMN_LAT), latitude=cursor.getDouble(COLUMN_LAT),
longitude=cursor.getDouble(COLUMN_LON), longitude=cursor.getDouble(COLUMN_LON),
altitude=cursor.getDouble(COLUMN_ELE), altitude=cursor.getDouble(COLUMN_ELE),
accuracy=cursor.getFloat(COLUMN_ACCURACY), accuracy=cursor.getFloat(COLUMN_ACCURACY),
time=iso8601_format.parse(cursor.getString(COLUMN_TIME)), time=cursor.getLong(COLUMN_TIME),
distanceToStartingPoint=0F,
numberSatellites=cursor.getInt(COLUMN_SAT), numberSatellites=cursor.getInt(COLUMN_SAT),
) )
yield(trkpt) yield(trkpt)

View File

@ -14,15 +14,12 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook package org.y20k.trackbook
import YesNoDialog import YesNoDialog
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -32,23 +29,18 @@ import android.widget.Toast
import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.y20k.trackbook.core.Database
import org.y20k.trackbook.core.Track
import org.y20k.trackbook.dialogs.RenameTrackDialog import org.y20k.trackbook.dialogs.RenameTrackDialog
import org.y20k.trackbook.helpers.LogHelper import org.y20k.trackbook.helpers.LogHelper
import org.y20k.trackbook.helpers.MapOverlayHelper
import org.y20k.trackbook.helpers.TrackHelper
import org.y20k.trackbook.helpers.iso8601_format import org.y20k.trackbook.helpers.iso8601_format
import org.y20k.trackbook.ui.TrackFragmentLayoutHolder import org.y20k.trackbook.ui.TrackFragmentLayoutHolder
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDialog.YesNoDialogListener, MapOverlayHelper.MarkerListener { class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDialog.YesNoDialogListener
{
/* Define log tag */ /* Define log tag */
private val TAG: String = LogHelper.makeLogTag(TrackFragment::class.java) private val TAG: String = LogHelper.makeLogTag(TrackFragment::class.java)
/* Main class variables */ /* Main class variables */
private lateinit var layout: TrackFragmentLayoutHolder private lateinit var layout: TrackFragmentLayoutHolder
@ -58,12 +50,13 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
val database: Database = (requireActivity().applicationContext as Trackbook).database val database: Database = (requireActivity().applicationContext as Trackbook).database
val track: Track = Track( val track: Track = Track(
database=database, database=database,
name=this.requireArguments().getString(Keys.ARG_TRACK_TITLE, ""),
device_id= this.requireArguments().getString(Keys.ARG_TRACK_DEVICE_ID, ""), device_id= this.requireArguments().getString(Keys.ARG_TRACK_DEVICE_ID, ""),
start_time= iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_START_TIME)!!), start_time= iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_START_TIME)!!),
stop_time=iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_STOP_TIME)!!), stop_time=iso8601_format.parse(this.requireArguments().getString(Keys.ARG_TRACK_STOP_TIME)!!),
) )
track.load_trkpts() track.load_trkpts()
layout = TrackFragmentLayoutHolder(activity as Context, this as MapOverlayHelper.MarkerListener, inflater, container, track) layout = TrackFragmentLayoutHolder(activity as Context, inflater, container, track)
// set up share button // set up share button
layout.shareButton.setOnClickListener { layout.shareButton.setOnClickListener {
@ -101,11 +94,9 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
layout.saveViewStateToTrack() layout.saveViewStateToTrack()
} }
/* Register the ActivityResultLauncher for saving GPX */ /* Register the ActivityResultLauncher for saving GPX */
private val requestSaveGpxLauncher = registerForActivityResult(StartActivityForResult(), this::requestSaveGpxResult) private val requestSaveGpxLauncher = registerForActivityResult(StartActivityForResult(), this::requestSaveGpxResult)
private fun requestSaveGpxResult(result: ActivityResult) private fun requestSaveGpxResult(result: ActivityResult)
{ {
if (result.resultCode != Activity.RESULT_OK || result.data == null) if (result.resultCode != Activity.RESULT_OK || result.data == null)
@ -149,19 +140,11 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
} }
} }
/* Overrides onMarkerTapped from MarkerListener */
override fun onMarkerTapped(latitude: Double, longitude: Double)
{
super.onMarkerTapped(latitude, longitude)
TrackHelper.toggle_waypoint_starred(activity as Context, layout.track, latitude, longitude)
layout.updateTrackOverlay()
}
/* Opens up a file picker to select the save location */ /* Opens up a file picker to select the save location */
private fun openSaveGpxDialog() private fun openSaveGpxDialog()
{ {
val context = this.activity as Context val context = this.activity as Context
val export_name: String = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(layout.track.start_time) + Keys.GPX_FILE_EXTENSION 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 { val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE) addCategory(Intent.CATEGORY_OPENABLE)
type = Keys.MIME_TYPE_GPX type = Keys.MIME_TYPE_GPX

View File

@ -14,27 +14,21 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook package org.y20k.trackbook
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE import android.Manifest
import android.app.Activity
import android.app.Application import android.app.Application
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.database.Cursor import android.database.Cursor
import android.util.Log import android.util.Log
import androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import org.y20k.trackbook.core.Database
import org.y20k.trackbook.core.Homepoint
import org.y20k.trackbook.core.Trkpt
import org.y20k.trackbook.helpers.AppThemeHelper import org.y20k.trackbook.helpers.AppThemeHelper
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.PreferencesHelper.initPreferences import org.y20k.trackbook.helpers.PreferencesHelper.initPreferences
import org.y20k.trackbook.helpers.iso8601
import org.y20k.trackbook.helpers.iso8601_format
import java.io.File import java.io.File
/* /*
@ -54,17 +48,25 @@ class Trackbook(): Application() {
// set Dark / Light theme state // set Dark / Light theme state
AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection()) AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection())
ContextCompat.checkSelfPermission(applicationContext, android.Manifest.permission.ACCESS_COARSE_LOCATION)
ContextCompat.checkSelfPermission(applicationContext, android.Manifest.permission.ACCESS_FINE_LOCATION)
Log.i("VOUSSOIR", "Device ID = ${PreferencesHelper.load_device_id()}") Log.i("VOUSSOIR", "Device ID = ${PreferencesHelper.load_device_id()}")
if (ContextCompat.checkSelfPermission(applicationContext, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
{
this.database.connect(File("/storage/emulated/0/Syncthing/GPX/trkpt_${PreferencesHelper.load_device_id()}.db"))
}
} }
fun load_database()
{
Log.i("VOUSSOIR", "Trackbook.load_database")
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.load_homepoints()
}
else
{
this.database.ready = false
}
}
fun load_homepoints() fun load_homepoints()
{ {
Log.i("VOUSSOIR", "Trackbook.load_homepoints")
this.homepoints.clear() this.homepoints.clear()
homepoint_generator().forEach { homepoint -> this.homepoints.add(homepoint) } homepoint_generator().forEach { homepoint -> this.homepoints.add(homepoint) }
} }
@ -73,18 +75,14 @@ class Trackbook(): Application() {
{ {
if (! database.ready) if (! database.ready)
{ {
Log.i("VOUSSOIR", "Trackbook.homepoint_generator: database is not ready.")
return@iterator return@iterator
} }
val cursor: Cursor = database.connection.query( val cursor: Cursor = database.connection.rawQuery(
"homepoints", "SELECT lat, lon, radius, name FROM homepoints",
arrayOf("lat", "lon", "radius", "name"), arrayOf()
null,
null,
null,
null,
null,
null,
) )
Log.i("VOUSSOIR", "Trackbook.homepoint_generator: Got ${cursor.count} homepoints.")
val COLUMN_LAT = cursor.getColumnIndex("lat") val COLUMN_LAT = cursor.getColumnIndex("lat")
val COLUMN_LON = cursor.getColumnIndex("lon") val COLUMN_LON = cursor.getColumnIndex("lon")
val COLUMN_RADIUS = cursor.getColumnIndex("radius") val COLUMN_RADIUS = cursor.getColumnIndex("radius")

View File

@ -37,11 +37,7 @@ import android.util.Log
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import java.util.* import java.util.*
import kotlinx.coroutines.Runnable import kotlinx.coroutines.Runnable
import org.y20k.trackbook.core.Track
import org.y20k.trackbook.core.Database
import org.y20k.trackbook.core.Trkpt
import org.y20k.trackbook.helpers.* import org.y20k.trackbook.helpers.*
import java.text.SimpleDateFormat
/* /*
* TrackerService class * TrackerService class
@ -58,7 +54,7 @@ class TrackerService: Service(), SensorEventListener
var useImperial: Boolean = false var useImperial: Boolean = false
var gpsOnly: Boolean = false var gpsOnly: Boolean = false
var omitRests: Boolean = true var omitRests: Boolean = true
var device_id: String = random_int().toString() var device_id: String = random_device_id()
var recording_started: Date = GregorianCalendar.getInstance().time var recording_started: Date = GregorianCalendar.getInstance().time
var commitInterval: Int = Keys.COMMIT_INTERVAL var commitInterval: Int = Keys.COMMIT_INTERVAL
var currentBestLocation: Location = LocationHelper.getDefaultLocation() var currentBestLocation: Location = LocationHelper.getDefaultLocation()
@ -150,21 +146,11 @@ class TrackerService: Service(), SensorEventListener
LogHelper.v(TAG, "Added Network location listener.") LogHelper.v(TAG, "Added Network location listener.")
} }
fun clearTrack()
{
trackingState = Keys.STATE_TRACKING_STOPPED
PreferencesHelper.saveTrackingState(trackingState)
track.delete()
track = Track(trackbook.database, device_id, start_time=GregorianCalendar.getInstance().time, stop_time=Date(GregorianCalendar.getInstance().time.time + 86400))
stopForeground(STOP_FOREGROUND_REMOVE)
notificationManager.cancel(Keys.TRACKER_SERVICE_NOTIFICATION_ID) // this call was not necessary prior to Android 12
}
private fun createLocationListener(): LocationListener private fun createLocationListener(): LocationListener
{ {
return object : LocationListener { return object : LocationListener {
override fun onLocationChanged(location: Location) { override fun onLocationChanged(location: Location)
// update currentBestLocation if a better location is available {
if (LocationHelper.isBetterLocation(location, currentBestLocation)) { if (LocationHelper.isBetterLocation(location, currentBestLocation)) {
currentBestLocation = location currentBestLocation = location
} }
@ -173,26 +159,16 @@ class TrackerService: Service(), SensorEventListener
{ {
LogHelper.v(TAG, "onProviderEnabled $provider") LogHelper.v(TAG, "onProviderEnabled $provider")
when (provider) { when (provider) {
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled( LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
locationManager LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
)
LocationManager.NETWORK_PROVIDER -> networkProviderActive =
LocationHelper.isNetworkEnabled(
locationManager
)
} }
} }
override fun onProviderDisabled(provider: String) override fun onProviderDisabled(provider: String)
{ {
LogHelper.v(TAG, "onProviderDisabled $provider") LogHelper.v(TAG, "onProviderDisabled $provider")
when (provider) { when (provider) {
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled( LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
locationManager LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
)
LocationManager.NETWORK_PROVIDER -> networkProviderActive =
LocationHelper.isNetworkEnabled(
locationManager
)
} }
} }
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?)
@ -278,7 +254,7 @@ class TrackerService: Service(), SensorEventListener
/* Overrides onSensorChanged from SensorEventListener */ /* Overrides onSensorChanged from SensorEventListener */
override fun onSensorChanged(sensorEvent: SensorEvent?) { override fun onSensorChanged(sensorEvent: SensorEvent?) {
var steps: Float = 0f var steps = 0f
if (sensorEvent != null) { if (sensorEvent != null) {
if (stepCountOffset == 0f) { if (stepCountOffset == 0f) {
// store steps previously recorded by the system // store steps previously recorded by the system
@ -454,10 +430,10 @@ class TrackerService: Service(), SensorEventListener
} }
for (homepoint in trackbook.homepoints) for (homepoint in trackbook.homepoints)
{ {
if (LocationHelper.calculateDistance(homepoint.location, location) < homepoint.radius) if (homepoint.location.distanceTo(location) < homepoint.radius)
{ {
Log.i("VOUSSOIR", "Omitting due to homepoint ${homepoint}.") Log.i("VOUSSOIR", "Omitting due to homepoint ${homepoint.name}.")
return false; return false
} }
} }
if (track.trkpts.isEmpty()) if (track.trkpts.isEmpty())
@ -478,16 +454,15 @@ class TrackerService: Service(), SensorEventListener
{ {
override fun run() { override fun run() {
val now: Date = GregorianCalendar.getInstance().time val now: Date = GregorianCalendar.getInstance().time
val nowstr: String = iso8601(now)
val trkpt: Trkpt = Trkpt(location=currentBestLocation) val trkpt: Trkpt = Trkpt(location=currentBestLocation)
Log.i("VOUSSOIR", "Processing point ${currentBestLocation.latitude}, ${currentBestLocation.longitude} ${nowstr}.") Log.i("VOUSSOIR", "Processing point ${currentBestLocation.latitude}, ${currentBestLocation.longitude} ${now.time}.")
if (should_keep_point((currentBestLocation))) if (should_keep_point((currentBestLocation)))
{ {
val values = ContentValues().apply { val values = ContentValues().apply {
put("device_id", device_id) put("device_id", device_id)
put("lat", trkpt.latitude) put("lat", trkpt.latitude)
put("lon", trkpt.longitude) put("lon", trkpt.longitude)
put("time", nowstr) put("time", now.time)
put("accuracy", trkpt.accuracy) put("accuracy", trkpt.accuracy)
put("sat", trkpt.numberSatellites) put("sat", trkpt.numberSatellites)
put("ele", trkpt.altitude) put("ele", trkpt.altitude)
@ -499,7 +474,7 @@ class TrackerService: Service(), SensorEventListener
} }
trackbook.database.connection.insert("trkpt", null, values) trackbook.database.connection.insert("trkpt", null, values)
track.trkpts.add(trkpt) track.trkpts.add(trkpt)
if (track.trkpts.size > track.dequelimit) while (track.trkpts.size > 7200)
{ {
track.trkpts.removeFirst() track.trkpts.removeFirst()
} }

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook package org.y20k.trackbook
import android.content.Intent import android.content.Intent
@ -26,7 +25,6 @@ import android.service.quicksettings.TileService
import org.y20k.trackbook.helpers.LogHelper import org.y20k.trackbook.helpers.LogHelper
import org.y20k.trackbook.helpers.PreferencesHelper import org.y20k.trackbook.helpers.PreferencesHelper
/* /*
* TrackingToggleTileService class * TrackingToggleTileService class
*/ */
@ -35,13 +33,11 @@ class TrackingToggleTileService: TileService() {
/* Define log tag */ /* Define log tag */
private val TAG: String = LogHelper.makeLogTag(TrackingToggleTileService::class.java) private val TAG: String = LogHelper.makeLogTag(TrackingToggleTileService::class.java)
/* Main class variables */ /* Main class variables */
private var bound: Boolean = false private var bound: Boolean = false
private var trackingState: Int = Keys.STATE_TRACKING_STOPPED private var trackingState: Int = Keys.STATE_TRACKING_STOPPED
private lateinit var trackerService: TrackerService private lateinit var trackerService: TrackerService
/* Overrides onTileAdded from TileService */ /* Overrides onTileAdded from TileService */
override fun onTileAdded() { override fun onTileAdded() {
super.onTileAdded() super.onTileAdded()
@ -56,7 +52,6 @@ class TrackingToggleTileService: TileService() {
super.onTileRemoved() super.onTileRemoved()
} }
/* Overrides onStartListening from TileService (tile becomes visible) */ /* Overrides onStartListening from TileService (tile becomes visible) */
override fun onStartListening() { override fun onStartListening() {
super.onStartListening() super.onStartListening()
@ -68,7 +63,6 @@ class TrackingToggleTileService: TileService() {
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener) PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
} }
/* Overrides onClick from TileService */ /* Overrides onClick from TileService */
override fun onClick() { override fun onClick() {
super.onClick() super.onClick()
@ -78,7 +72,6 @@ class TrackingToggleTileService: TileService() {
} }
} }
/* Overrides onStopListening from TileService (tile no longer visible) */ /* Overrides onStopListening from TileService (tile no longer visible) */
override fun onStopListening() { override fun onStopListening() {
super.onStopListening() super.onStopListening()
@ -86,13 +79,11 @@ class TrackingToggleTileService: TileService() {
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener) PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
} }
/* Overrides onDestroy from Service */ /* Overrides onDestroy from Service */
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
} }
/* Update quick settings tile */ /* Update quick settings tile */
private fun updateTile() { private fun updateTile() {
val tile: Tile = qsTile val tile: Tile = qsTile
@ -112,7 +103,6 @@ class TrackingToggleTileService: TileService() {
tile.updateTile() tile.updateTile()
} }
/* Start tracking */ /* Start tracking */
private fun startTracking() { private fun startTracking() {
val intent = Intent(application, TrackerService::class.java) val intent = Intent(application, TrackerService::class.java)
@ -125,7 +115,6 @@ class TrackingToggleTileService: TileService() {
} }
} }
/* Stop tracking */ /* Stop tracking */
private fun stopTracking() { private fun stopTracking() {
val intent = Intent(application, TrackerService::class.java) val intent = Intent(application, TrackerService::class.java)
@ -133,7 +122,6 @@ class TrackingToggleTileService: TileService() {
application.startService(intent) application.startService(intent)
} }
/* /*
* Defines the listener for changes in shared preferences * Defines the listener for changes in shared preferences
*/ */
@ -151,5 +139,4 @@ class TrackingToggleTileService: TileService() {
} }

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook package org.y20k.trackbook
import YesNoDialog import YesNoDialog
@ -34,13 +33,11 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import org.y20k.trackbook.core.Track
import org.y20k.trackbook.helpers.LogHelper import org.y20k.trackbook.helpers.LogHelper
import org.y20k.trackbook.helpers.UiHelper import org.y20k.trackbook.helpers.UiHelper
import org.y20k.trackbook.helpers.iso8601_format import org.y20k.trackbook.helpers.iso8601_format
import org.y20k.trackbook.tracklist.TracklistAdapter import org.y20k.trackbook.tracklist.TracklistAdapter
/* /*
* TracklistFragment class * TracklistFragment class
*/ */

View File

@ -14,33 +14,26 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook
package org.y20k.trackbook.core
import android.location.Location import android.location.Location
import android.os.Parcelable
import androidx.annotation.Keep
import com.google.gson.annotations.Expose
import kotlinx.parcelize.Parcelize
import org.y20k.trackbook.helpers.LocationHelper import org.y20k.trackbook.helpers.LocationHelper
import java.util.* import java.util.*
/* /*
* WayPoint data class * WayPoint data class
*/ */
@Keep data class Trkpt(
@Parcelize val provider: String,
data class Trkpt(@Expose val provider: String, val latitude: Double,
@Expose val latitude: Double, val longitude: Double,
@Expose val longitude: Double, val altitude: Double,
@Expose val altitude: Double, val accuracy: Float,
@Expose val accuracy: Float, val time: Long,
@Expose val time: Date, val numberSatellites: Int = 0,
@Expose val distanceToStartingPoint: Float = 0f, var starred: Boolean = false
@Expose val numberSatellites: Int = 0, )
@Expose var isStopOver: Boolean = false, {
@Expose var starred: Boolean = false): Parcelable {
/* Constructor using just Location */ /* Constructor using just Location */
constructor(location: Location) : this ( constructor(location: Location) : this (
provider=location.provider.toString(), provider=location.provider.toString(),
@ -48,19 +41,18 @@ data class Trkpt(@Expose val provider: String,
longitude=location.longitude, longitude=location.longitude,
altitude=location.altitude, altitude=location.altitude,
accuracy=location.accuracy, accuracy=location.accuracy,
time=Date(location.time), time=location.time,
distanceToStartingPoint=0F,
numberSatellites=LocationHelper.getNumberOfSatellites(location), numberSatellites=LocationHelper.getNumberOfSatellites(location),
) )
/* Converts WayPoint into Location */ /* Converts WayPoint into Location */
fun toLocation(): Location { fun toLocation(): Location {
val location: Location = Location(provider) val location = Location(provider)
location.latitude = latitude location.latitude = latitude
location.longitude = longitude location.longitude = longitude
location.altitude = altitude location.altitude = altitude
location.accuracy = accuracy location.accuracy = accuracy
location.time = this.time.time location.time = this.time
return location return location
} }

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.dialogs package org.y20k.trackbook.dialogs
import android.content.Context import android.content.Context
@ -29,7 +28,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.helpers.LogHelper import org.y20k.trackbook.helpers.LogHelper
/* /*
* ErrorDialog object * ErrorDialog object
*/ */
@ -38,7 +36,6 @@ object ErrorDialog {
/* Define log tag */ /* Define log tag */
private val TAG: String = LogHelper.makeLogTag(ErrorDialog::class.java) private val TAG: String = LogHelper.makeLogTag(ErrorDialog::class.java)
/* Construct and show dialog */ /* Construct and show dialog */
fun show(context: Context, errorTitle: Int, errorMessage: Int, errorDetails: String = String()) { fun show(context: Context, errorTitle: Int, errorMessage: Int, errorDetails: String = String()) {
// prepare dialog builder // prepare dialog builder

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.dialogs package org.y20k.trackbook.dialogs
import android.content.Context import android.content.Context
@ -26,7 +25,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.helpers.LogHelper import org.y20k.trackbook.helpers.LogHelper
/* /*
* RenameTrackDialog class * RenameTrackDialog class
*/ */
@ -41,7 +39,6 @@ class RenameTrackDialog (private var renameTrackListener: RenameTrackListener) {
/* Define log tag */ /* Define log tag */
private val TAG = LogHelper.makeLogTag(RenameTrackDialog::class.java.simpleName) private val TAG = LogHelper.makeLogTag(RenameTrackDialog::class.java.simpleName)
/* Construct and show dialog */ /* Construct and show dialog */
fun show(context: Context, trackName: String) { fun show(context: Context, trackName: String) {
// prepare dialog builder // prepare dialog builder

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
import android.content.Context import android.content.Context
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
@ -32,11 +31,9 @@ class YesNoDialog (private var yesNoDialogListener: YesNoDialogListener) {
} }
} }
/* Define log tag */ /* Define log tag */
private val TAG = LogHelper.makeLogTag(YesNoDialog::class.java.simpleName) private val TAG = LogHelper.makeLogTag(YesNoDialog::class.java.simpleName)
/* Construct and show dialog - variant: message from string */ /* Construct and show dialog - variant: message from string */
fun show(context: Context, fun show(context: Context,
type: Int, type: Int,
@ -50,7 +47,6 @@ class YesNoDialog (private var yesNoDialogListener: YesNoDialogListener) {
show(context, type, title, context.getString(message), yesButton, noButton, payload, payloadString) show(context, type, title, context.getString(message), yesButton, noButton, payload, payloadString)
} }
/* Construct and show dialog */ /* Construct and show dialog */
fun show(context: Context, fun show(context: Context,
type: Int, type: Int,
@ -70,7 +66,6 @@ class YesNoDialog (private var yesNoDialogListener: YesNoDialogListener) {
builder.setTitle(context.getString(title)) builder.setTitle(context.getString(title))
} }
// add yes button // add yes button
builder.setPositiveButton(yesButton) { _, _ -> builder.setPositiveButton(yesButton) { _, _ ->
// listen for click on yes button // listen for click on yes button

View File

@ -14,16 +14,12 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.extensions package org.y20k.trackbook.extensions
import android.content.SharedPreferences import android.content.SharedPreferences
/* Puts a Double value in SharedPreferences */ /* Puts a Double value in SharedPreferences */
fun SharedPreferences.Editor.putDouble(key: String, double: Double) = putLong(key, java.lang.Double.doubleToRawLongBits(double)) fun SharedPreferences.Editor.putDouble(key: String, double: Double) = putLong(key, java.lang.Double.doubleToRawLongBits(double))
/* gets a Double value from SharedPreferences */ /* gets a Double value from SharedPreferences */
fun SharedPreferences.getDouble(key: String, default: Double) = java.lang.Double.longBitsToDouble(getLong(key, java.lang.Double.doubleToRawLongBits(default))) fun SharedPreferences.getDouble(key: String, default: Double) = java.lang.Double.longBitsToDouble(getLong(key, java.lang.Double.doubleToRawLongBits(default)))

View File

@ -1,22 +1,24 @@
package org.y20k.trackbook.helpers package org.y20k.trackbook.helpers
import java.lang.Math.abs
import java.security.SecureRandom
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.random.Random 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()
fun iso8601(datetime: Date): String fun iso8601(datetime: Date): String
{ {
return iso8601_format.format(datetime) return iso8601_format.format(datetime)
} }
fun random_long(): Long
{
return (Random.nextBits(31).toLong() shl 32) + Random.nextBits(32)
}
fun random_int(): Int fun random_int(): Int
{ {
return Random.nextBits(31) return abs(RNG.nextInt())
}
fun random_device_id(): String
{
return "myphone" + random_int()
} }

View File

@ -14,18 +14,14 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.helpers package org.y20k.trackbook.helpers
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.view.View
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import org.y20k.trackbook.R import org.y20k.trackbook.R
/* /*
* AppThemeHelper object * AppThemeHelper object
*/ */
@ -34,7 +30,6 @@ object AppThemeHelper {
/* Define log tag */ /* Define log tag */
private val TAG: String = LogHelper.makeLogTag(AppThemeHelper::class.java) private val TAG: String = LogHelper.makeLogTag(AppThemeHelper::class.java)
/* Sets app theme */ /* Sets app theme */
fun setTheme(nightModeState: String) { fun setTheme(nightModeState: String) {
when (nightModeState) { when (nightModeState) {
@ -67,14 +62,12 @@ object AppThemeHelper {
} }
} }
/* Return weather Night Mode is on, or not */ /* Return weather Night Mode is on, or not */
fun isDarkModeOn(context: Context): Boolean { fun isDarkModeOn(context: Context): Boolean {
val nightMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK val nightMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
return nightMode == Configuration.UI_MODE_NIGHT_YES return nightMode == Configuration.UI_MODE_NIGHT_YES
} }
/* Returns a readable String for currently selected App Theme */ /* Returns a readable String for currently selected App Theme */
fun getCurrentTheme(context: Context): String { fun getCurrentTheme(context: Context): String {
return when (PreferencesHelper.loadThemeSelection()) { return when (PreferencesHelper.loadThemeSelection()) {
@ -83,22 +76,4 @@ object AppThemeHelper {
else -> context.getString(R.string.pref_theme_selection_mode_device_default) else -> context.getString(R.string.pref_theme_selection_mode_device_default)
} }
} }
/* Displays the default status bar */
private fun displayDefaultStatusBar(activity: Activity) {
val decorView = activity.window.decorView
decorView.systemUiVisibility = 0
}
/* Displays the light (inverted) status bar */
private fun displayLightStatusBar(activity: Activity) {
val decorView = activity.window.decorView
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
} }

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.helpers package org.y20k.trackbook.helpers
import android.content.Context import android.content.Context
@ -26,7 +25,6 @@ import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
/* /*
* DateTimeHelper object * DateTimeHelper object
*/ */
@ -81,26 +79,22 @@ object DateTimeHelper {
return timeString return timeString
} }
/* Create sortable string for date - used for filenames */ /* Create sortable string for date - used for filenames */
fun convertToSortableDateString(date: Date): String { fun convertToSortableDateString(date: Date): String {
val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US) val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US)
return dateFormat.format(date) return dateFormat.format(date)
} }
/* Creates a readable string for date - used in the UI */ /* Creates a readable string for date - used in the UI */
fun convertToReadableDate(date: Date, dateStyle: Int = DateFormat.LONG): String { fun convertToReadableDate(date: Date, dateStyle: Int = DateFormat.LONG): String {
return DateFormat.getDateInstance(dateStyle, Locale.getDefault()).format(date) return DateFormat.getDateInstance(dateStyle, Locale.getDefault()).format(date)
} }
/* Creates a readable string date and time - used in the UI */ /* Creates a readable string date and time - used in the UI */
fun convertToReadableDateAndTime(date: Date, dateStyle: Int = DateFormat.SHORT, timeStyle: Int = DateFormat.SHORT): String { fun convertToReadableDateAndTime(date: Date, dateStyle: Int = DateFormat.SHORT, timeStyle: Int = DateFormat.SHORT): String {
return "${DateFormat.getDateInstance(dateStyle, Locale.getDefault()).format(date)} ${DateFormat.getTimeInstance(timeStyle, Locale.getDefault()).format(date)}" return "${DateFormat.getDateInstance(dateStyle, Locale.getDefault()).format(date)} ${DateFormat.getTimeInstance(timeStyle, Locale.getDefault()).format(date)}"
} }
/* Calculates time difference between two locations */ /* Calculates time difference between two locations */
fun calculateTimeDistance(previousLocation: Location?, location: Location): Long { fun calculateTimeDistance(previousLocation: Location?, location: Location): Long {
var timeDifference: Long = 0L var timeDifference: Long = 0L
@ -112,5 +106,4 @@ object DateTimeHelper {
return timeDifference return timeDifference
} }
} }

View File

@ -1,98 +0,0 @@
/*
* FileHelper.kt
* Implements the FileHelper object
* A FileHelper provides helper methods for reading and writing files from and to device storage
*
* 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.helpers
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import androidx.core.net.toUri
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.io.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import org.y20k.trackbook.Keys
import org.y20k.trackbook.core.Track
/*
* FileHelper object
*/
object FileHelper {
/* Define log tag */
private val TAG: String = LogHelper.makeLogTag(FileHelper::class.java)
/* Suspend function: Wrapper for copyFile */
suspend fun saveCopyOfFileSuspended(context: Context, originalFileUri: Uri, targetFileUri: Uri, deleteOriginal: Boolean = false) {
return suspendCoroutine { cont ->
cont.resume(copyFile(context, originalFileUri, targetFileUri, deleteOriginal))
}
}
/* Copies file to specified target */
fun copyFile(context: Context, originalFileUri: Uri, targetFileUri: Uri, deleteOriginal: Boolean = false) {
val inputStream = context.contentResolver.openInputStream(originalFileUri)
val outputStream = context.contentResolver.openOutputStream(targetFileUri)
if (outputStream != null) {
inputStream?.copyTo(outputStream)
}
if (deleteOriginal) {
context.contentResolver.delete(originalFileUri, null, null)
}
}
fun getCustomGson(): Gson
{
val gsonBuilder = GsonBuilder()
gsonBuilder.setDateFormat("yyyy-MM-dd-HH-mm-ss")
gsonBuilder.excludeFieldsWithoutExposeAnnotation()
return gsonBuilder.create()
}
/* Reads InputStream from file uri and returns it as String */
fun readTextFile(context: Context, file: File): String {
// todo read https://commonsware.com/blog/2016/03/15/how-consume-content-uri.html
// https://developer.android.com/training/secure-file-sharing/retrieve-info
if (!file.exists()) {
return String()
}
// read until last line reached
val stream: InputStream = file.inputStream()
val reader: BufferedReader = BufferedReader(InputStreamReader(stream))
val builder: StringBuilder = StringBuilder()
reader.forEachLine {
builder.append(it)
builder.append("\n") }
stream.close()
return builder.toString()
}
/* Writes given text to file on storage */
fun write_text_file_noblank(text: String, file: File)
{
if (text.isNotEmpty()) {
file.writeText(text)
} else {
LogHelper.w(TAG, "Writing text file ${file.toUri()} failed. Empty text string was provided.")
}
}
}

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.helpers package org.y20k.trackbook.helpers
import java.math.BigDecimal import java.math.BigDecimal
@ -22,19 +21,16 @@ import java.math.RoundingMode
import java.text.NumberFormat import java.text.NumberFormat
import java.util.* import java.util.*
/* /*
* LengthUnitHelper object * LengthUnitHelper object
*/ */
object LengthUnitHelper { object LengthUnitHelper {
/* Converts for the given unit system a distance value to a readable string */ /* Converts for the given unit system a distance value to a readable string */
fun convertDistanceToString(distance: Float, useImperial: Boolean = false): String { fun convertDistanceToString(distance: Float, useImperial: Boolean = false): String {
return convertDistanceToString(distance.toDouble(), useImperial) return convertDistanceToString(distance.toDouble(), useImperial)
} }
/* Converts for the given unit system a distance value to a readable string */ /* Converts for the given unit system a distance value to a readable string */
fun convertDistanceToString(distance: Double, useImperial: Boolean = false): String { fun convertDistanceToString(distance: Double, useImperial: Boolean = false): String {
val readableDistance: Double val readableDistance: Double
@ -85,7 +81,6 @@ object LengthUnitHelper {
return "${numberFormat.format(readableDistance)} $unit" return "${numberFormat.format(readableDistance)} $unit"
} }
/* Determines which unit system the device is using (metric or imperial) */ /* Determines which unit system the device is using (metric or imperial) */
fun useImperialUnits(): Boolean { fun useImperialUnits(): Boolean {
// America (US), Liberia (LR), Myanmar(MM) use the imperial system // America (US), Liberia (LR), Myanmar(MM) use the imperial system
@ -94,7 +89,6 @@ object LengthUnitHelper {
return imperialSystemCountries.contains(countryCode) return imperialSystemCountries.contains(countryCode)
} }
/* Converts for the given unit System distance and duration values to a readable velocity string */ /* Converts for the given unit System distance and duration values to a readable velocity string */
fun convertToVelocityString(velocity: Double, useImperialUnits: Boolean = false) : String { fun convertToVelocityString(velocity: Double, useImperialUnits: Boolean = false) : String {
var speed: String = "0" var speed: String = "0"
@ -114,7 +108,6 @@ object LengthUnitHelper {
} }
} }
/* Coverts meters per second to either km/h or mph */ /* Coverts meters per second to either km/h or mph */
fun convertMetersPerSecond(metersPerSecond: Double, useImperial: Boolean = false): Double { fun convertMetersPerSecond(metersPerSecond: Double, useImperial: Boolean = false): Double {
if (useImperial) { if (useImperial) {
@ -126,5 +119,4 @@ object LengthUnitHelper {
} }
} }
} }

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.helpers package org.y20k.trackbook.helpers
import android.Manifest import android.Manifest
@ -26,11 +25,8 @@ import android.os.Bundle
import android.os.SystemClock import android.os.SystemClock
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import org.y20k.trackbook.core.Track
import java.util.*
import kotlin.math.pow import kotlin.math.pow
/* /*
* Keys object * Keys object
*/ */
@ -39,7 +35,6 @@ object LocationHelper {
/* Define log tag */ /* Define log tag */
private val TAG: String = LogHelper.makeLogTag(LocationHelper::class.java) private val TAG: String = LogHelper.makeLogTag(LocationHelper::class.java)
/* Get default location */ /* Get default location */
fun getDefaultLocation(): Location { fun getDefaultLocation(): Location {
val defaultLocation: Location = Location(LocationManager.NETWORK_PROVIDER) val defaultLocation: Location = Location(LocationManager.NETWORK_PROVIDER)
@ -68,9 +63,9 @@ object LocationHelper {
return lastKnownLocation return lastKnownLocation
} }
/* Determines whether one location reading is better than the current location fix */ /* Determines whether one location reading is better than the current location fix */
fun isBetterLocation(location: Location, currentBestLocation: Location?): Boolean { fun isBetterLocation(location: Location, currentBestLocation: Location?): Boolean
{
// Credit: https://developer.android.com/guide/topics/location/strategies.html#BestEstimate // Credit: https://developer.android.com/guide/topics/location/strategies.html#BestEstimate
if (currentBestLocation == null) { if (currentBestLocation == null) {
@ -110,15 +105,18 @@ object LocationHelper {
} }
/* Checks if GPS location provider is available and enabled */ /* Checks if GPS location provider is available and enabled */
fun isGpsEnabled(locationManager: LocationManager): Boolean { fun isGpsEnabled(locationManager: LocationManager): Boolean
if (locationManager.allProviders.contains(LocationManager.GPS_PROVIDER)) { {
if (locationManager.allProviders.contains(LocationManager.GPS_PROVIDER))
{
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
} else { }
else
{
return false return false
} }
} }
/* Checks if Network location provider is available and enabled */ /* Checks if Network location provider is available and enabled */
fun isNetworkEnabled(locationManager: LocationManager): Boolean { fun isNetworkEnabled(locationManager: LocationManager): Boolean {
if (locationManager.allProviders.contains(LocationManager.NETWORK_PROVIDER)) { if (locationManager.allProviders.contains(LocationManager.NETWORK_PROVIDER)) {
@ -129,14 +127,12 @@ object LocationHelper {
} }
/* Checks if given location is new */ /* Checks if given location is new */
fun isRecentEnough(location: Location): Boolean { fun isRecentEnough(location: Location): Boolean {
val locationAge: Long = SystemClock.elapsedRealtimeNanos() - location.elapsedRealtimeNanos val locationAge: Long = SystemClock.elapsedRealtimeNanos() - location.elapsedRealtimeNanos
return locationAge < Keys.DEFAULT_THRESHOLD_LOCATION_AGE return locationAge < Keys.DEFAULT_THRESHOLD_LOCATION_AGE
} }
/* Checks if given location is accurate */ /* Checks if given location is accurate */
fun isAccurateEnough(location: Location, locationAccuracyThreshold: Int): Boolean { fun isAccurateEnough(location: Location, locationAccuracyThreshold: Int): Boolean {
val isAccurate: Boolean val isAccurate: Boolean
@ -166,7 +162,7 @@ object LocationHelper {
val accuracy: Float = if (location.accuracy != 0.0f) location.accuracy else Keys.DEFAULT_THRESHOLD_DISTANCE val accuracy: Float = if (location.accuracy != 0.0f) location.accuracy else Keys.DEFAULT_THRESHOLD_DISTANCE
val previousAccuracy: Float = if (previousLocation.accuracy != 0.0f) previousLocation.accuracy else Keys.DEFAULT_THRESHOLD_DISTANCE val previousAccuracy: Float = if (previousLocation.accuracy != 0.0f) previousLocation.accuracy else Keys.DEFAULT_THRESHOLD_DISTANCE
val accuracyDelta: Double = Math.sqrt((accuracy.pow(2) + previousAccuracy.pow(2)).toDouble()) val accuracyDelta: Double = Math.sqrt((accuracy.pow(2) + previousAccuracy.pow(2)).toDouble())
val distance: Float = calculateDistance(previousLocation, location) val distance: Float = previousLocation.distanceTo(location)
// With 1*accuracyDelta we have 68% confidence that the points are // With 1*accuracyDelta we have 68% confidence that the points are
// different. We can multiply this number to increase confidence but // different. We can multiply this number to increase confidence but
@ -174,18 +170,6 @@ object LocationHelper {
return distance > accuracyDelta return distance > accuracyDelta
} }
/* Calculates distance in meters between two locations */
fun calculateDistance(previousLocation: Location?, location: Location): Float {
var distance: Float = 0f
// two data points needed to calculate distance
if (previousLocation != null) {
// add up distance
distance = previousLocation.distanceTo(location)
}
return distance
}
/* Get number of satellites from Location extras */ /* Get number of satellites from Location extras */
fun getNumberOfSatellites(location: Location): Int { fun getNumberOfSatellites(location: Location): Int {
val numberOfSatellites: Int val numberOfSatellites: Int
@ -198,5 +182,4 @@ object LocationHelper {
return numberOfSatellites return numberOfSatellites
} }
} }

View File

@ -14,13 +14,11 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.helpers package org.y20k.trackbook.helpers
import android.util.Log import android.util.Log
import org.y20k.trackbook.BuildConfig import org.y20k.trackbook.BuildConfig
/* /*
* LogHelper object * LogHelper object
*/ */

View File

@ -14,113 +14,58 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.helpers package org.y20k.trackbook.helpers
import android.content.Context import android.content.Context
import android.graphics.Color
import android.graphics.Paint import android.graphics.Paint
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.location.Location import android.location.Location
import android.os.Vibrator import android.os.Vibrator
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import org.osmdroid.api.IGeoPoint import org.osmdroid.api.IGeoPoint
import org.osmdroid.util.GeoPoint import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.ItemizedIconOverlay import org.osmdroid.views.overlay.ItemizedIconOverlay
import org.osmdroid.views.overlay.OverlayItem import org.osmdroid.views.overlay.OverlayItem
import org.osmdroid.views.overlay.Polygon
import org.osmdroid.views.overlay.simplefastpoint.LabelledGeoPoint import org.osmdroid.views.overlay.simplefastpoint.LabelledGeoPoint
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
import org.y20k.trackbook.Homepoint
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.core.Track import org.y20k.trackbook.Track
import org.y20k.trackbook.core.Trkpt import org.y20k.trackbook.Trkpt
import java.text.DecimalFormat import java.text.DecimalFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
//private var currentPositionOverlay: ItemizedIconOverlay<OverlayItem>
/*
* MapOverlayHelper class
*/
class MapOverlayHelper (private var markerListener: MarkerListener) {
/* Interface used to communicate back to activity/fragment */
interface MarkerListener {
fun onMarkerTapped(latitude: Double, longitude: Double) {
}
}
/* Define log tag */
private val TAG = MapOverlayHelper::class.java.simpleName
/* Creates icon overlay for current position (used in MapFragment) */ /* Creates icon overlay for current position (used in MapFragment) */
fun createMyLocationOverlay(context: Context, location: Location, trackingState: Int): ItemizedIconOverlay<OverlayItem> fun createMyLocationOverlay(context: Context, map_view: MapView, currentPositionOverlay: ItemizedIconOverlay<OverlayItem>, location: Location, trackingState: Int)
{ {
val overlayItems: ArrayList<OverlayItem> = ArrayList<OverlayItem>()
val locationIsOld: Boolean = !(LocationHelper.isRecentEnough(location))
// create marker
val newMarker: Drawable
when (trackingState) {
// CASE: Tracking active
Keys.STATE_TRACKING_ACTIVE -> {
when (locationIsOld) {
true -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_red_grey_24dp)!!
false -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_red_24dp)!!
}
}
// CASE. Tracking is NOT active
else -> {
when (locationIsOld) {
true -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_blue_grey_24dp)!!
false -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_blue_24dp)!!
}
}
}
// add marker to list of overlay items
val overlayItem: OverlayItem = createOverlayItem(context, location.latitude, location.longitude, location.accuracy, location.provider.toString(), location.time)
overlayItem.setMarker(newMarker)
overlayItems.add(overlayItem)
// create and return overlay for current position
return createOverlay(context, overlayItems, enableStarring = false)
}
/* Creates icon overlay for current position (used in MapFragment) */
fun createHomepointOverlay(context: Context, location: Location): ItemizedIconOverlay<OverlayItem>
{
val overlayItems: ArrayList<OverlayItem> = ArrayList<OverlayItem>()
// create marker
val newMarker: Drawable = ContextCompat.getDrawable(context, R.drawable.ic_homepoint_24dp)!!
// add marker to list of overlay items
val overlayItem: OverlayItem = createOverlayItem(context, location.latitude, location.longitude, location.accuracy, location.provider.toString(), location.time)
overlayItem.setMarker(newMarker)
overlayItems.add(overlayItem)
// create and return overlay for current position
return createOverlay(context, overlayItems, enableStarring = false)
} }
/* Creates icon overlay for track */ /* Creates icon overlay for track */
fun createTrackOverlay(context: Context, track: Track, trackingState: Int): SimpleFastPointOverlay fun createTrackOverlay(context: Context, map_view: MapView, track: Track, trackingState: Int)
{ {
// get marker color // get marker color
val color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.default_red) val color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.default_red) else context.getColor(R.color.default_blue)
else context.getColor(R.color.default_blue)
// gather points for overlay // gather points for overlay
val points: MutableList<IGeoPoint> = mutableListOf() val points: MutableList<IGeoPoint> = mutableListOf()
track.trkpts.forEach { wayPoint -> 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})" 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})"
// only add normal points // only add normal points
if (!wayPoint.starred && !wayPoint.isStopOver) { if (!wayPoint.starred)
{
points.add(LabelledGeoPoint(wayPoint.latitude, wayPoint.longitude, wayPoint.altitude, label)) points.add(LabelledGeoPoint(wayPoint.latitude, wayPoint.longitude, wayPoint.altitude, label))
} }
} }
@ -137,22 +82,13 @@ class MapOverlayHelper (private var markerListener: MarkerListener) {
.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(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.
// create and return overlay val overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
val overlay: SimpleFastPointOverlay = SimpleFastPointOverlay(pointTheme, overlayOptions) map_view.overlays.add(overlay)
overlay.setOnClickListener(object : SimpleFastPointOverlay.OnClickListener {
override fun onClick(points: SimpleFastPointOverlay.PointAdapter?, point: Int?) {
if (points != null && point != null) {
val markerPoint: IGeoPoint = points.get(point)
markerListener.onMarkerTapped(markerPoint.latitude, markerPoint.longitude)
}
}
})
return overlay
} }
/* Creates overlay containing start, stop, stopover and starred markers for track */ /* Creates overlay containing start, stop, stopover and starred markers for track */
fun createSpecialMakersTrackOverlay(context: Context, track: Track, trackingState: Int, displayStartEndMarker: Boolean = false): ItemizedIconOverlay<OverlayItem> fun createSpecialMakersTrackOverlay(context: Context, map_view: MapView, track: Track, trackingState: Int, displayStartEndMarker: Boolean = false)
{ {
val overlayItems: ArrayList<OverlayItem> = ArrayList<OverlayItem>() val overlayItems: ArrayList<OverlayItem> = ArrayList<OverlayItem>()
val trackingActive: Boolean = trackingState == Keys.STATE_TRACKING_ACTIVE val trackingActive: Boolean = trackingState == Keys.STATE_TRACKING_ACTIVE
@ -162,51 +98,46 @@ class MapOverlayHelper (private var markerListener: MarkerListener) {
var overlayItem: OverlayItem? = null var overlayItem: OverlayItem? = null
if (!trackingActive && index == 0 && displayStartEndMarker && trkpt.starred) if (!trackingActive && index == 0 && displayStartEndMarker && trkpt.starred)
{ {
overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time.time) overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_start_starred_blue_48dp)!!) overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_start_starred_blue_48dp)!!)
} }
else if (!trackingActive && index == 0 && displayStartEndMarker && !trkpt.starred) else if (!trackingActive && index == 0 && displayStartEndMarker && !trkpt.starred)
{ {
overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time.time) overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_start_blue_48dp)!!) overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_start_blue_48dp)!!)
} }
else if (!trackingActive && index == maxIndex && displayStartEndMarker && trkpt.starred) else if (!trackingActive && index == maxIndex && displayStartEndMarker && trkpt.starred)
{ {
overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time.time) overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_end_starred_blue_48dp)!!) overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_end_starred_blue_48dp)!!)
} }
else if (!trackingActive && index == maxIndex && displayStartEndMarker && !trkpt.starred) else if (!trackingActive && index == maxIndex && displayStartEndMarker && !trkpt.starred)
{ {
overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time.time) overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_end_blue_48dp)!!) overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_end_blue_48dp)!!)
} }
else if (!trackingActive && trkpt.starred) else if (!trackingActive && trkpt.starred)
{ {
overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time.time) overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_star_blue_24dp)!!) overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_star_blue_24dp)!!)
} }
else if (trackingActive && trkpt.starred) else if (trackingActive && trkpt.starred)
{ {
overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time.time) overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_star_red_24dp)!!) overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_star_red_24dp)!!)
} }
else if (trkpt.isStopOver)
{
overlayItem = createOverlayItem(context, trkpt.latitude, trkpt.longitude, trkpt.accuracy, trkpt.provider, trkpt.time.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_grey_24dp)!!)
}
// add overlay item, if it was created
if (overlayItem != null) if (overlayItem != null)
{ {
overlayItems.add(overlayItem) overlayItems.add(overlayItem)
} }
} }
// create and return overlay for current position map_view.overlays.add(createOverlay(context, overlayItems))
return createOverlay(context, overlayItems, enableStarring = true)
} }
/* Creates a marker overlay item */
private fun createOverlayItem(context: Context, latitude: Double, longitude: Double, accuracy: Float, provider: String, time: Long): OverlayItem {
fun createOverlayItem(context: Context, latitude: Double, longitude: Double, accuracy: Float, provider: String, time: Long): OverlayItem
{
val title: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)}" val title: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)}"
//val description: String = "${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(accuracy)} (${provider})" //val description: String = "${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(accuracy)} (${provider})"
val description: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(accuracy)} (${provider})" val description: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(accuracy)} (${provider})"
@ -216,25 +147,19 @@ class MapOverlayHelper (private var markerListener: MarkerListener) {
return item return item
} }
/* Creates an overlay */ fun createOverlay(context: Context, overlayItems: ArrayList<OverlayItem>): ItemizedIconOverlay<OverlayItem>
private fun createOverlay(context: Context, overlayItems: ArrayList<OverlayItem>, enableStarring: Boolean): ItemizedIconOverlay<OverlayItem> { {
return ItemizedIconOverlay<OverlayItem>(context, overlayItems, return ItemizedIconOverlay<OverlayItem>(context, overlayItems,
object : ItemizedIconOverlay.OnItemGestureListener<OverlayItem> { object : ItemizedIconOverlay.OnItemGestureListener<OverlayItem> {
override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean { override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean {
if (enableStarring) {
markerListener.onMarkerTapped(item.point.latitude, item.point.longitude)
return true
} else {
return false return false
} }
}
override fun onItemLongPress(index: Int, item: OverlayItem): Boolean { override fun onItemLongPress(index: Int, item: OverlayItem): Boolean {
val v = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator val v = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
v.vibrate(50) v.vibrate(50)
Toast.makeText(context, item.snippet, Toast.LENGTH_LONG).show() Toast.makeText(context, item.snippet, Toast.LENGTH_LONG).show()
return true return true
} }
})
} }
)
} }

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.helpers package org.y20k.trackbook.helpers
import android.app.* import android.app.*
@ -30,7 +29,6 @@ import org.y20k.trackbook.MainActivity
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.TrackerService import org.y20k.trackbook.TrackerService
/* /*
* NotificationHelper class * NotificationHelper class
*/ */
@ -39,11 +37,9 @@ class NotificationHelper(private val trackerService: TrackerService) {
/* Define log tag */ /* Define log tag */
private val TAG: String = LogHelper.makeLogTag(NotificationHelper::class.java) private val TAG: String = LogHelper.makeLogTag(NotificationHelper::class.java)
/* Main class variables */ /* Main class variables */
private val notificationManager: NotificationManager = trackerService.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager private val notificationManager: NotificationManager = trackerService.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
/* Creates notification */ /* Creates notification */
fun createNotification(trackingState: Int, timestamp: String): Notification { fun createNotification(trackingState: Int, timestamp: String): Notification {

View File

@ -46,16 +46,23 @@ object PreferencesHelper {
fun load_device_id(): String fun load_device_id(): String
{ {
val v = sharedPreferences.getString(Keys.PREF_DEVICE_ID, random_int().toString()).toString(); val fallback = random_device_id()
Log.i("VOUSSOIR", "Loaded device_id ${v}.") val v = sharedPreferences.getString(Keys.PREF_DEVICE_ID, fallback).toString()
if (v == fallback)
{
sharedPreferences.edit { putString(Keys.PREF_DEVICE_ID, fallback) }
}
Log.i("VOUSSOIR", "PreferencesHelper.load_device_id: Got ${v}.")
return v return v
} }
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)
} }
fun saveZoomLevel(zoomLevel: Double) { fun saveZoomLevel(zoomLevel: Double)
{
sharedPreferences.edit { putDouble(Keys.PREF_MAP_ZOOM_LEVEL, zoomLevel) } sharedPreferences.edit { putDouble(Keys.PREF_MAP_ZOOM_LEVEL, zoomLevel) }
} }

View File

@ -1,47 +0,0 @@
/*
* TrackHelper.kt
* Implements the TrackHelper object
* A TrackHelper offers helper methods for dealing with track objects
*
* 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.helpers
import android.content.Context
import android.widget.Toast
import org.y20k.trackbook.R
import org.y20k.trackbook.core.Track
/*
* TrackHelper object
*/
object TrackHelper {
/* Define log tag */
private val TAG: String = LogHelper.makeLogTag(TrackHelper::class.java)
/* Adds given locatiom as waypoint to track */
/* Toggles starred flag for given position */
fun toggle_waypoint_starred(context: Context, track: Track, latitude: Double, longitude: Double)
{
track.trkpts.forEach { waypoint ->
if (waypoint.latitude == latitude && waypoint.longitude == longitude) {
waypoint.starred = !waypoint.starred
when (waypoint.starred) {
true -> Toast.makeText(context, R.string.toast_message_poi_added, Toast.LENGTH_LONG).show()
false -> Toast.makeText(context, R.string.toast_message_poi_removed, Toast.LENGTH_LONG).show()
}
}
}
}
}

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.helpers package org.y20k.trackbook.helpers
import android.content.Context import android.content.Context
@ -31,7 +30,6 @@ import androidx.recyclerview.widget.RecyclerView
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.tracklist.TracklistAdapter import org.y20k.trackbook.tracklist.TracklistAdapter
/* /*
* UiHelper object * UiHelper object
*/ */
@ -40,7 +38,6 @@ object UiHelper {
/* Define log tag */ /* Define log tag */
private val TAG: String = LogHelper.makeLogTag(UiHelper::class.java) private val TAG: String = LogHelper.makeLogTag(UiHelper::class.java)
/* Sets layout margins for given view in DP */ /* Sets layout margins for given view in DP */
fun setViewMargins(context: Context, view: View, left: Int = 0, right: Int = 0, top: Int= 0, bottom: Int = 0) { fun setViewMargins(context: Context, view: View, left: Int = 0, right: Int = 0, top: Int= 0, bottom: Int = 0) {
val scalingFactor: Float = context.resources.displayMetrics.density val scalingFactor: Float = context.resources.displayMetrics.density
@ -55,7 +52,6 @@ object UiHelper {
} }
} }
/* Sets layout margins for given view in percent */ /* Sets layout margins for given view in percent */
fun setViewMarginsPercentage(context: Context, view: View, height: Int, width: Int, left: Int = 0, right: Int = 0, top: Int= 0, bottom: Int = 0) { fun setViewMarginsPercentage(context: Context, view: View, height: Int, width: Int, left: Int = 0, right: Int = 0, top: Int= 0, bottom: Int = 0) {
val l: Int = ((width / 100.0f) * left).toInt() val l: Int = ((width / 100.0f) * left).toInt()
@ -65,7 +61,6 @@ object UiHelper {
setViewMargins(context, view, l, r, t, b) setViewMargins(context, view, l, r, t, b)
} }
/* Get the height of the system's top status bar */ /* Get the height of the system's top status bar */
fun getStatusBarHeight(context: Context): Int { fun getStatusBarHeight(context: Context): Int {
var result: Int = 0 var result: Int = 0
@ -76,14 +71,12 @@ object UiHelper {
return result return result
} }
/* Get scaling factor from display density */ /* Get scaling factor from display density */
fun getDensityScalingFactor(context: Context): Float { fun getDensityScalingFactor(context: Context): Float {
return context.resources.displayMetrics.density return context.resources.displayMetrics.density
} }
/* /*
* Inner class: Callback that detects a left swipe * Inner class: Callback that detects a left swipe
* Credit: https://github.com/kitek/android-rv-swipe-delete/blob/master/app/src/main/java/pl/kitek/rvswipetodelete/SwipeToDeleteCallback.kt * Credit: https://github.com/kitek/android-rv-swipe-delete/blob/master/app/src/main/java/pl/kitek/rvswipetodelete/SwipeToDeleteCallback.kt

View File

@ -14,25 +14,22 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.tracklist package org.y20k.trackbook.tracklist
import android.content.Context import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.util.Log import android.util.Log
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 android.widget.ImageButton
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.core.Database import org.y20k.trackbook.Database
import org.y20k.trackbook.core.Track import org.y20k.trackbook.Track
import org.y20k.trackbook.helpers.* import org.y20k.trackbook.helpers.*
import java.text.DateFormat import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -65,27 +62,25 @@ class TracklistAdapter(val fragment: Fragment, val database: Database) : Recycle
{ {
return return
} }
val cursor: Cursor = database.connection.query( val cursor: Cursor = database.connection.rawQuery(
"trkpt", "SELECT distinct(date(time/1000, 'unixepoch', 'localtime')) as thedate, device_id FROM trkpt ORDER BY thedate DESC",
arrayOf("distinct strftime('%Y-%m-%d', time)", "device_id"), arrayOf()
null,
null,
null,
null,
"time DESC",
null,
) )
try try
{ {
val df: DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US) val df: DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")
while (cursor.moveToNext()) while (cursor.moveToNext())
{ {
val start_time: Date? = df.parse(cursor.getString(0) + "T00:00:00.000") val trackdate = cursor.getString(0)
val stop_time: Date? = df.parse(cursor.getString(0) + "T23:59:59.999") val device_id = cursor.getString(1)
Log.i("VOUSSOIR", "TracklistAdapter prep track ${cursor.getString(0)}") val start_time: Date? = df.parse(trackdate + "T00:00:00.000")
val stop_time: Date? = df.parse(trackdate + "T23:59:59.999")
Log.i("VOUSSOIR", "TracklistAdapter prep track ${trackdate}")
if (start_time != null && stop_time != null) if (start_time != null && stop_time != null)
{ {
tracks.add(Track(database=database, device_id=cursor.getString(1), start_time=start_time, stop_time=stop_time)) val track = Track(database=database, device_id=device_id, start_time=start_time, stop_time=stop_time)
track.name = "$trackdate $device_id"
tracks.add(track)
} }
} }
} }
@ -95,7 +90,6 @@ class TracklistAdapter(val fragment: Fragment, val database: Database) : Recycle
} }
} }
/* Overrides onCreateViewHolder from RecyclerView.Adapter */ /* Overrides onCreateViewHolder from RecyclerView.Adapter */
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder
{ {
@ -103,21 +97,18 @@ class TracklistAdapter(val fragment: Fragment, val database: Database) : Recycle
return ElementTrackViewHolder(v) return ElementTrackViewHolder(v)
} }
/* Overrides getItemViewType */ /* Overrides getItemViewType */
override fun getItemViewType(position: Int): Int override fun getItemViewType(position: Int): Int
{ {
return Keys.VIEW_TYPE_TRACK return Keys.VIEW_TYPE_TRACK
} }
/* Overrides getItemCount from RecyclerView.Adapter */ /* Overrides getItemCount from RecyclerView.Adapter */
override fun getItemCount(): Int override fun getItemCount(): Int
{ {
return tracks.size return tracks.size
} }
/* Overrides onBindViewHolder from RecyclerView.Adapter */ /* Overrides onBindViewHolder from RecyclerView.Adapter */
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)
{ {
@ -130,7 +121,6 @@ class TracklistAdapter(val fragment: Fragment, val database: Database) : Recycle
} }
} }
/* Get track name for given position */ /* Get track name for given position */
fun getTrackName(positionInRecyclerView: Int): String fun getTrackName(positionInRecyclerView: Int): String
{ {
@ -188,7 +178,6 @@ class TracklistAdapter(val fragment: Fragment, val database: Database) : Recycle
// return trackDataString // return trackDataString
} }
/* /*
* Inner class: ViewHolder for a track element * Inner class: ViewHolder for a track element
*/ */

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.ui package org.y20k.trackbook.ui
import android.Manifest import android.Manifest
@ -23,53 +22,57 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Color
import android.graphics.Paint import android.graphics.Paint
import android.graphics.drawable.Drawable
import android.location.Location import android.location.Location
import android.util.Log
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.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
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
import org.osmdroid.views.MapView import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.ItemizedIconOverlay import org.osmdroid.views.overlay.ItemizedIconOverlay
import org.osmdroid.views.overlay.OverlayItem import org.osmdroid.views.overlay.OverlayItem
import org.osmdroid.views.overlay.Polygon
import org.osmdroid.views.overlay.TilesOverlay import org.osmdroid.views.overlay.TilesOverlay
import org.osmdroid.views.overlay.compass.CompassOverlay import org.osmdroid.views.overlay.compass.CompassOverlay
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
import org.y20k.trackbook.Homepoint
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.Trackbook import org.y20k.trackbook.Trackbook
import org.y20k.trackbook.core.Track import org.y20k.trackbook.Track
import org.y20k.trackbook.helpers.* import org.y20k.trackbook.helpers.*
/* /*
* MapFragmentLayoutHolder class * MapFragmentLayoutHolder class
*/ */
data class MapFragmentLayoutHolder(private var context: Context, private var markerListener: MapOverlayHelper.MarkerListener, private var inflater: LayoutInflater, private var container: ViewGroup?, private var statusBarHeight: Int, private val startLocation: Location, private val trackingState: Int) { data class MapFragmentLayoutHolder(
private var context: Context,
/* Define log tag */ private var inflater: LayoutInflater,
private val TAG: String = LogHelper.makeLogTag(MapFragmentLayoutHolder::class.java) private var container: ViewGroup?,
private var statusBarHeight: Int,
private val startLocation: Location,
private val trackingState: Int
)
{
/* Main class variables */ /* Main class variables */
val rootView: View val rootView: View
var userInteraction: Boolean = false var userInteraction: Boolean = false
val currentLocationButton: FloatingActionButton val currentLocationButton: FloatingActionButton
val mainButton: ExtendedFloatingActionButton val mainButton: ExtendedFloatingActionButton
private val mapView: MapView private val mapView: MapView
private val homepoint_overlays: ArrayList<ItemizedIconOverlay<OverlayItem>> = ArrayList()
private var currentPositionOverlay: ItemizedIconOverlay<OverlayItem> private var currentPositionOverlay: ItemizedIconOverlay<OverlayItem>
private var current_location_radius: Polygon = Polygon()
private var currentTrackOverlay: SimpleFastPointOverlay? private var currentTrackOverlay: SimpleFastPointOverlay?
private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay<OverlayItem>? private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay<OverlayItem>?
private val useImperial: Boolean = PreferencesHelper.loadUseImperialUnits() private val useImperial: Boolean = PreferencesHelper.loadUseImperialUnits()
@ -77,8 +80,6 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
private var controller: IMapController private var controller: IMapController
private var zoomLevel: Double private var zoomLevel: Double
/* Init block */
init { init {
// find views // find views
rootView = inflater.inflate(R.layout.fragment_map, container, false) rootView = inflater.inflate(R.layout.fragment_map, container, false)
@ -113,10 +114,11 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
mapView.overlays.add(compassOverlay) mapView.overlays.add(compassOverlay)
val app: Trackbook = (context.applicationContext as Trackbook) val app: Trackbook = (context.applicationContext as Trackbook)
app.homepoint_generator().forEach { homepoint -> mapView.overlays.add(MapOverlayHelper(markerListener).createHomepointOverlay(context, homepoint.location))} app.load_homepoints()
createHomepointOverlays(context, mapView, app.homepoints)
// add my location overlay // add my location overlay
currentPositionOverlay = MapOverlayHelper(markerListener).createMyLocationOverlay(context, startLocation, trackingState) currentPositionOverlay = createOverlay(context, ArrayList<OverlayItem>())
mapView.overlays.add(currentPositionOverlay) mapView.overlays.add(currentPositionOverlay)
centerMap(startLocation) centerMap(startLocation)
@ -131,7 +133,6 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
addInteractionListener() addInteractionListener()
} }
/* Listen for user interaction */ /* Listen for user interaction */
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
private fun addInteractionListener() { private fun addInteractionListener() {
@ -141,7 +142,6 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
} }
} }
/* Set map center */ /* Set map center */
fun centerMap(location: Location, animated: Boolean = false) { fun centerMap(location: Location, animated: Boolean = false) {
val position = GeoPoint(location.latitude, location.longitude) val position = GeoPoint(location.latitude, location.longitude)
@ -152,7 +152,6 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
userInteraction = false userInteraction = false
} }
/* Save current best location and state of map to shared preferences */ /* Save current best location and state of map to shared preferences */
fun saveState(currentBestLocation: Location) { fun saveState(currentBestLocation: Location) {
PreferencesHelper.saveCurrentBestLocation(currentBestLocation) PreferencesHelper.saveCurrentBestLocation(currentBestLocation)
@ -161,14 +160,78 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
userInteraction = false userInteraction = false
} }
/* Mark current position on map */ /* Mark current position on map */
fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED) { fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED)
mapView.overlays.remove(currentPositionOverlay) {
currentPositionOverlay = MapOverlayHelper(markerListener).createMyLocationOverlay(context, location, trackingState) Log.i("VOUSSOIR", "MapFragmentLayoutHolder.markCurrentPosition")
mapView.overlays.add(currentPositionOverlay) val locationIsOld: Boolean = !(LocationHelper.isRecentEnough(location))
// create marker
val newMarker: Drawable
val fillcolor: Int
if (trackingState == Keys.STATE_TRACKING_ACTIVE)
{
fillcolor = Color.argb(64, 220, 61, 51)
if (locationIsOld)
{
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_black_24dp)!!
}
else
{
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_red_24dp)!!
}
}
else
{
fillcolor = Color.argb(64, 60, 152, 219)
if(locationIsOld)
{
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_black_24dp)!!
}
else
{
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_blue_24dp)!!
}
} }
// add marker to list of overlay items
val overlayItem: OverlayItem = createOverlayItem(context, location.latitude, location.longitude, location.accuracy, location.provider.toString(), location.time)
overlayItem.setMarker(newMarker)
currentPositionOverlay.removeAllItems()
currentPositionOverlay.addItem(overlayItem)
if (current_location_radius in mapView.overlays)
{
mapView.overlays.remove(current_location_radius)
}
current_location_radius = Polygon()
current_location_radius.points = Polygon.pointsAsCircle(GeoPoint(location.latitude, location.longitude), location.accuracy.toDouble())
current_location_radius.fillPaint.color = fillcolor
current_location_radius.outlinePaint.color = Color.argb(0, 0, 0, 0)
mapView.overlays.add(current_location_radius)
}
fun createHomepointOverlays(context: Context, map_view: MapView, homepoints: List<Homepoint>)
{
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.createHomepointOverlays")
val overlayItems: java.util.ArrayList<OverlayItem> = java.util.ArrayList<OverlayItem>()
val newMarker: Drawable = ContextCompat.getDrawable(context, R.drawable.ic_homepoint_24dp)!!
for (homepoint in homepoints)
{
val overlayItem: OverlayItem = createOverlayItem(context, homepoint.location.latitude, homepoint.location.longitude, homepoint.location.accuracy, homepoint.location.provider.toString(), homepoint.location.time)
overlayItem.setMarker(newMarker)
overlayItems.add(overlayItem)
map_view.overlays.add(createOverlay(context, overlayItems))
val p = Polygon()
p.points = Polygon.pointsAsCircle(GeoPoint(homepoint.location.latitude, homepoint.location.longitude), homepoint.location.accuracy.toDouble())
p.fillPaint.color = Color.argb(64, 255, 193, 7)
p.outlinePaint.color = Color.argb(128, 255, 193, 7)
map_view.overlays.add(p)
}
}
/* Overlay current track on map */ /* Overlay current track on map */
fun overlayCurrentTrack(track: Track, trackingState: Int) { fun overlayCurrentTrack(track: Track, trackingState: Int) {
@ -179,11 +242,8 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
mapView.overlays.remove(currentTrackSpecialMarkerOverlay) mapView.overlays.remove(currentTrackSpecialMarkerOverlay)
} }
if (track.trkpts.isNotEmpty()) { if (track.trkpts.isNotEmpty()) {
val mapOverlayHelper: MapOverlayHelper = MapOverlayHelper(markerListener) createTrackOverlay(context, mapView, track, trackingState)
currentTrackOverlay = mapOverlayHelper.createTrackOverlay(context, track, trackingState) createSpecialMakersTrackOverlay(context, mapView, track, trackingState)
currentTrackSpecialMarkerOverlay = mapOverlayHelper.createSpecialMakersTrackOverlay(context, track, trackingState)
mapView.overlays.add(currentTrackSpecialMarkerOverlay)
mapView.overlays.add(currentTrackOverlay)
} }
} }
@ -206,7 +266,6 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
} }
} }
/* Toggles content and visibility of the location error snackbar */ /* Toggles content and visibility of the location error snackbar */
fun toggleLocationErrorBar(gpsProviderActive: Boolean, networkProviderActive: Boolean) { fun toggleLocationErrorBar(gpsProviderActive: Boolean, networkProviderActive: Boolean) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) {
@ -221,5 +280,4 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
if (locationErrorBar.isShown) locationErrorBar.dismiss() if (locationErrorBar.isShown) locationErrorBar.dismiss()
} }
} }
} }

View File

@ -14,7 +14,6 @@
* https://github.com/osmdroid/osmdroid * https://github.com/osmdroid/osmdroid
*/ */
package org.y20k.trackbook.ui package org.y20k.trackbook.ui
import android.app.Activity import android.app.Activity
@ -46,19 +45,17 @@ import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.core.Track import org.y20k.trackbook.Track
import org.y20k.trackbook.core.TrackStatistics import org.y20k.trackbook.TrackStatistics
import org.y20k.trackbook.helpers.* import org.y20k.trackbook.helpers.*
import kotlin.math.roundToInt import kotlin.math.roundToInt
/* /*
* TrackFragmentLayoutHolder class * TrackFragmentLayoutHolder class
*/ */
//data class TrackFragmentLayoutHolder(private var context: Context, private var markerListener: MapOverlayHelper.MarkerListener, private var inflater: LayoutInflater, private var statusBarHeight: Int, private var container: ViewGroup?, var track: Track): MapListener { TODO REMOVE //data class TrackFragmentLayoutHolder(private var context: Context, private var markerListener: MapOverlayHelper.MarkerListener, private var inflater: LayoutInflater, private var statusBarHeight: Int, private var container: ViewGroup?, var track: Track): MapListener { TODO REMOVE
data class TrackFragmentLayoutHolder( data class TrackFragmentLayoutHolder(
private var context: Context, private var context: Context,
private var markerListener: MapOverlayHelper.MarkerListener,
private var inflater: LayoutInflater, private var inflater: LayoutInflater,
private var container: ViewGroup?, private var container: ViewGroup?,
var track: Track var track: Track
@ -71,8 +68,6 @@ data class TrackFragmentLayoutHolder(
val editButton: ImageButton val editButton: ImageButton
val trackNameView: MaterialTextView val trackNameView: MaterialTextView
private val mapView: MapView private val mapView: MapView
private var trackSpecialMarkersOverlay: ItemizedIconOverlay<OverlayItem>?
private var trackOverlay: SimpleFastPointOverlay?
private var controller: IMapController private var controller: IMapController
//private var zoomLevel: Double //private var zoomLevel: Double
private val statisticsSheetBehavior: BottomSheetBehavior<View> private val statisticsSheetBehavior: BottomSheetBehavior<View>
@ -96,7 +91,6 @@ data class TrackFragmentLayoutHolder(
private val trackManagementViews: Group private val trackManagementViews: Group
private val useImperialUnits: Boolean private val useImperialUnits: Boolean
/* Init block */ /* Init block */
init { init {
// find views // find views
@ -152,13 +146,9 @@ data class TrackFragmentLayoutHolder(
// compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / UiHelper.getDensityScalingFactor(context))) TODO REMOVE // compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / UiHelper.getDensityScalingFactor(context))) TODO REMOVE
mapView.overlays.add(compassOverlay) mapView.overlays.add(compassOverlay)
// create map overlay
val mapOverlayHelper: MapOverlayHelper = MapOverlayHelper(markerListener)
trackOverlay = mapOverlayHelper.createTrackOverlay(context, track, Keys.STATE_TRACKING_STOPPED)
trackSpecialMarkersOverlay = mapOverlayHelper.createSpecialMakersTrackOverlay(context, track, Keys.STATE_TRACKING_STOPPED, displayStartEndMarker = true)
if (track.trkpts.isNotEmpty()) { if (track.trkpts.isNotEmpty()) {
mapView.overlays.add(trackSpecialMarkersOverlay) createSpecialMakersTrackOverlay(context, mapView, track, Keys.STATE_TRACKING_STOPPED, displayStartEndMarker = true)
mapView.overlays.add(trackOverlay) createTrackOverlay(context, mapView, track, Keys.STATE_TRACKING_STOPPED)
} }
// set up and show statistics sheet // set up and show statistics sheet
@ -168,25 +158,6 @@ data class TrackFragmentLayoutHolder(
setupStatisticsViews() setupStatisticsViews()
} }
/* Updates map overlay */
fun updateTrackOverlay()
{
if (trackOverlay != null) {
mapView.overlays.remove(trackOverlay)
}
if (trackSpecialMarkersOverlay != null) {
mapView.overlays.remove(trackSpecialMarkersOverlay)
}
if (track.trkpts.isNotEmpty()) {
val mapOverlayHelper: MapOverlayHelper = MapOverlayHelper(markerListener)
trackOverlay = mapOverlayHelper.createTrackOverlay(context, track, Keys.STATE_TRACKING_STOPPED)
trackSpecialMarkersOverlay = mapOverlayHelper.createSpecialMakersTrackOverlay(context, track, Keys.STATE_TRACKING_STOPPED, displayStartEndMarker = true)
mapView.overlays.add(trackOverlay)
mapView.overlays.add(trackSpecialMarkersOverlay)
}
}
/* Saves zoom level and center of this map */ /* Saves zoom level and center of this map */
fun saveViewStateToTrack() fun saveViewStateToTrack()
{ {
@ -195,7 +166,6 @@ data class TrackFragmentLayoutHolder(
} }
} }
/* Sets up the statistics sheet */ /* Sets up the statistics sheet */
private fun setupStatisticsViews() private fun setupStatisticsViews()
{ {
@ -225,7 +195,6 @@ data class TrackFragmentLayoutHolder(
} }
} }
/* Shows/hides the statistics sheet */ /* Shows/hides the statistics sheet */
private fun toggleStatisticsSheetVisibility() { private fun toggleStatisticsSheetVisibility() {
when (statisticsSheetBehavior.state) { when (statisticsSheetBehavior.state) {
@ -263,7 +232,6 @@ data class TrackFragmentLayoutHolder(
} }
} }
/* Overrides onZoom from MapListener */ /* Overrides onZoom from MapListener */
override fun onZoom(event: ZoomEvent?): Boolean { override fun onZoom(event: ZoomEvent?): Boolean {
if (event == null) { if (event == null) {
@ -274,7 +242,6 @@ data class TrackFragmentLayoutHolder(
} }
} }
/* Overrides onScroll from MapListener */ /* Overrides onScroll from MapListener */
override fun onScroll(event: ScrollEvent?): Boolean { override fun onScroll(event: ScrollEvent?): Boolean {
if (event == null) { if (event == null) {

View File

@ -4,10 +4,10 @@
android:viewportWidth="96.0" android:viewportWidth="96.0"
android:width="24dp"> android:width="24dp">
<path <path
android:fillAlpha="0.33" android:fillAlpha="0.0"
android:fillColor="@color/default_red" android:fillColor="@color/default_red"
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/> android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
<path <path
android:fillColor="@color/default_neutral_medium_light" android:fillColor="#ff000000"
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/> android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
</vector> </vector>

View File

@ -3,10 +3,6 @@
android:viewportHeight="96.0" android:viewportHeight="96.0"
android:viewportWidth="96.0" android:viewportWidth="96.0"
android:width="24dp"> android:width="24dp">
<path
android:fillAlpha="0.33"
android:fillColor="@color/default_blue"
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
<path <path
android:fillColor="@color/default_blue" android:fillColor="@color/default_blue"
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/> android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>

View File

@ -1,13 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="96.0"
android:viewportWidth="96.0"
android:width="24dp">
<path
android:fillAlpha="0.33"
android:fillColor="@color/default_blue"
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
<path
android:fillColor="@color/default_neutral_medium_light"
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
</vector>

View File

@ -4,7 +4,7 @@
android:viewportWidth="96.0" android:viewportWidth="96.0"
android:width="24dp"> android:width="24dp">
<path <path
android:fillAlpha="0.33" android:fillAlpha="0.0"
android:fillColor="@color/default_red" android:fillColor="@color/default_red"
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/> android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
<path <path