checkpoint
This commit is contained in:
parent
2568af3bb1
commit
df77b089ac
36 changed files with 406 additions and 804 deletions
|
@ -1,6 +1,4 @@
|
|||
package org.y20k.trackbook.core
|
||||
|
||||
import android.content.Context
|
||||
package org.y20k.trackbook
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteDatabase.openOrCreateDatabase
|
||||
import android.util.Log
|
||||
|
@ -11,6 +9,7 @@ class Database()
|
|||
var ready: Boolean = false
|
||||
lateinit var file: File
|
||||
lateinit var connection: SQLiteDatabase
|
||||
|
||||
fun close()
|
||||
{
|
||||
this.connection.close()
|
||||
|
@ -19,6 +18,7 @@ class Database()
|
|||
|
||||
fun connect(file: File)
|
||||
{
|
||||
Log.i("VOUSSOIR", "Connecting to database " + file.absolutePath)
|
||||
this.file = file
|
||||
this.connection = openOrCreateDatabase(file, null)
|
||||
this.initialize_tables()
|
||||
|
@ -44,7 +44,7 @@ class Database()
|
|||
{
|
||||
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 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.setTransactionSuccessful()
|
||||
this.connection.endTransaction()
|
|
@ -1,5 +1,4 @@
|
|||
package org.y20k.trackbook.core
|
||||
|
||||
package org.y20k.trackbook
|
||||
import android.location.Location
|
||||
import java.util.*
|
||||
|
|
@ -14,12 +14,10 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook
|
||||
|
||||
import java.util.*
|
||||
|
||||
|
||||
/*
|
||||
* Keys object
|
||||
*/
|
||||
|
@ -116,7 +114,7 @@ object Keys {
|
|||
const val COMMIT_INTERVAL: Int = 30
|
||||
const val DEFAULT_ALTITUDE_SMOOTHING_VALUE: Int = 13
|
||||
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_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
|
||||
|
|
|
@ -14,19 +14,21 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.StrictMode
|
||||
import android.os.StrictMode.VmPolicy
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
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.PreferencesHelper
|
||||
|
||||
private const val REQUEST_EXTERNAL_STORAGE = 1
|
||||
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?)
|
||||
class MainActivity: AppCompatActivity()
|
||||
{
|
||||
// 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 */
|
||||
lateinit var trackbook: Trackbook
|
||||
private lateinit var navHostFragment: NavHostFragment
|
||||
private lateinit var bottomNavigationView: BottomNavigationView
|
||||
|
||||
|
||||
/* Overrides onCreate from AppCompatActivity */
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
{
|
||||
trackbook = (applicationContext as Trackbook)
|
||||
super.onCreate(savedInstanceState)
|
||||
verifyStoragePermissions(this)
|
||||
request_permissions(this)
|
||||
// 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(
|
||||
VmPolicy.Builder()
|
||||
.detectNonSdkApiUsage()
|
||||
|
@ -109,8 +78,10 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
// listen for navigation changes
|
||||
navHostFragment.navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
when (destination.id) {
|
||||
R.id.fragment_track -> {
|
||||
when (destination.id)
|
||||
{
|
||||
R.id.fragment_track ->
|
||||
{
|
||||
runOnUiThread {
|
||||
run {
|
||||
// mark menu item "Tracks" as checked
|
||||
|
@ -118,7 +89,8 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
else ->
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
@ -128,26 +100,62 @@ class MainActivity : AppCompatActivity() {
|
|||
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 */
|
||||
override fun onDestroy() {
|
||||
override fun onDestroy()
|
||||
{
|
||||
super.onDestroy()
|
||||
// unregister listener for changes in shared preferences
|
||||
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Defines the listener for changes in shared preferences
|
||||
*/
|
||||
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||
when (key) {
|
||||
Keys.PREF_THEME_SELECTION -> {
|
||||
when (key)
|
||||
{
|
||||
Keys.PREF_THEME_SELECTION ->
|
||||
{
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook
|
||||
|
||||
import android.Manifest
|
||||
|
@ -25,17 +24,18 @@ import android.os.*
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||
import androidx.core.content.ContextCompat
|
||||
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.ui.MapFragmentLayoutHolder
|
||||
|
||||
/*
|
||||
* MapFragment class
|
||||
*/
|
||||
class MapFragment : Fragment(), MapOverlayHelper.MarkerListener
|
||||
class MapFragment : Fragment()
|
||||
{
|
||||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(MapFragment::class.java)
|
||||
|
@ -61,13 +61,14 @@ class MapFragment : Fragment(), MapOverlayHelper.MarkerListener
|
|||
currentBestLocation = LocationHelper.getLastKnownLocation(activity as Context)
|
||||
// get saved tracking state
|
||||
trackingState = PreferencesHelper.loadTrackingState()
|
||||
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
/* Overrides onStop from Fragment */
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
// initialize layout
|
||||
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
|
||||
layout.currentLocationButton.setOnClickListener {
|
||||
|
|
|
@ -14,33 +14,21 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook
|
||||
|
||||
|
||||
import YesNoDialog
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
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.FileHelper
|
||||
import org.y20k.trackbook.helpers.LengthUnitHelper
|
||||
import org.y20k.trackbook.helpers.LogHelper
|
||||
import org.y20k.trackbook.helpers.PreferencesHelper
|
||||
import org.y20k.trackbook.helpers.random_int
|
||||
import kotlin.random.Random
|
||||
import org.y20k.trackbook.helpers.random_device_id
|
||||
|
||||
/*
|
||||
* SettingsFragment class
|
||||
|
@ -50,7 +38,6 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(SettingsFragment::class.java)
|
||||
|
||||
|
||||
/* Overrides onViewCreated from PreferenceFragmentCompat */
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
@ -58,7 +45,6 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
view.setBackgroundColor(resources.getColor(R.color.app_window_background, null))
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onCreatePreferences from PreferenceFragmentCompat */
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
|
||||
|
@ -130,7 +116,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
preferenceDeviceID.setIcon(R.drawable.ic_smartphone_24dp)
|
||||
preferenceDeviceID.key = Keys.PREF_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)
|
||||
screen.addPreference(preferenceDeviceID)
|
||||
|
||||
|
@ -142,7 +128,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
val preferenceAppVersion: Preference = Preference(context)
|
||||
preferenceAppVersion.title = getString(R.string.pref_app_version_title)
|
||||
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 {
|
||||
// copy to clipboard
|
||||
val clip: ClipData = ClipData.newPlainText("simple text", preferenceAppVersion.summary)
|
||||
|
@ -158,7 +144,6 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
preferenceScreen = screen
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onYesNoDialog from YesNoDialogListener */
|
||||
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String) {
|
||||
when (type) {
|
||||
|
|
|
@ -14,48 +14,37 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
package org.y20k.trackbook.core
|
||||
package org.y20k.trackbook
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.location.Location
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
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 java.io.File
|
||||
import java.net.URI
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
/*
|
||||
* Track data class
|
||||
*/
|
||||
data class Track (
|
||||
val database: Database,
|
||||
val device_id: String,
|
||||
val start_time: Date,
|
||||
val stop_time: Date,
|
||||
var name: String = "",
|
||||
var dequelimit: Int = 7200,
|
||||
val trkpts: ArrayDeque<Trkpt> = ArrayDeque<Trkpt>(),
|
||||
var view_latitude: Double = Keys.DEFAULT_LATITUDE,
|
||||
var view_longitude: Double = Keys.DEFAULT_LONGITUDE,
|
||||
var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMAT_VERSION,
|
||||
val trkpts: ArrayDeque<Trkpt> = ArrayDeque<Trkpt>(dequelimit),
|
||||
var zoomLevel: Double = Keys.DEFAULT_ZOOM_LEVEL,
|
||||
)
|
||||
{
|
||||
fun delete()
|
||||
{
|
||||
Log.i("VOUSSOIR", "Track.delete.")
|
||||
}
|
||||
|
||||
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?
|
||||
{
|
||||
if (! database.ready)
|
||||
|
@ -153,7 +136,8 @@ data class Track (
|
|||
stats.min_altitude = trkpt.altitude
|
||||
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
|
||||
if (ascentdiff > 0)
|
||||
{
|
||||
|
@ -171,28 +155,23 @@ data class Track (
|
|||
{
|
||||
stats.min_altitude = trkpt.altitude
|
||||
}
|
||||
previous = trkpt
|
||||
last = trkpt
|
||||
}
|
||||
if (first == null || last == null)
|
||||
{
|
||||
return stats
|
||||
}
|
||||
stats.duration = last.time.time - first.time.time
|
||||
stats.duration = last.time - first.time
|
||||
stats.velocity = stats.distance / stats.duration
|
||||
return stats
|
||||
}
|
||||
|
||||
fun trkpt_generator() = iterator<Trkpt>
|
||||
{
|
||||
val cursor: Cursor = database.connection.query(
|
||||
"trkpt",
|
||||
arrayOf("lat", "lon", "time", "ele", "accuracy", "sat"),
|
||||
"device_id = ? AND time > ? AND time < ?",
|
||||
arrayOf(device_id, iso8601(start_time), iso8601(stop_time)),
|
||||
null,
|
||||
null,
|
||||
"time ASC",
|
||||
null,
|
||||
val cursor: Cursor = database.connection.rawQuery(
|
||||
"SELECT lat, lon, time, ele, accuracy, sat FROM trkpt WHERE device_id = ? AND time > ? AND time < ? ORDER BY time ASC",
|
||||
arrayOf(device_id, start_time.time.toString(), stop_time.time.toString())
|
||||
)
|
||||
val COLUMN_LAT = cursor.getColumnIndex("lat")
|
||||
val COLUMN_LON = cursor.getColumnIndex("lon")
|
||||
|
@ -204,14 +183,13 @@ data class Track (
|
|||
{
|
||||
while (cursor.moveToNext())
|
||||
{
|
||||
val trkpt: Trkpt = Trkpt(
|
||||
val trkpt = Trkpt(
|
||||
provider="",
|
||||
latitude=cursor.getDouble(COLUMN_LAT),
|
||||
longitude=cursor.getDouble(COLUMN_LON),
|
||||
altitude=cursor.getDouble(COLUMN_ELE),
|
||||
accuracy=cursor.getFloat(COLUMN_ACCURACY),
|
||||
time=iso8601_format.parse(cursor.getString(COLUMN_TIME)),
|
||||
distanceToStartingPoint=0F,
|
||||
time=cursor.getLong(COLUMN_TIME),
|
||||
numberSatellites=cursor.getInt(COLUMN_SAT),
|
||||
)
|
||||
yield(trkpt)
|
|
@ -14,15 +14,12 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook
|
||||
|
||||
|
||||
import YesNoDialog
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
|
@ -32,23 +29,18 @@ import android.widget.Toast
|
|||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||
import androidx.fragment.app.Fragment
|
||||
import org.y20k.trackbook.core.Database
|
||||
import org.y20k.trackbook.core.Track
|
||||
import org.y20k.trackbook.dialogs.RenameTrackDialog
|
||||
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.ui.TrackFragmentLayoutHolder
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDialog.YesNoDialogListener, MapOverlayHelper.MarkerListener {
|
||||
|
||||
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDialog.YesNoDialogListener
|
||||
{
|
||||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(TrackFragment::class.java)
|
||||
|
||||
|
||||
/* Main class variables */
|
||||
private lateinit var layout: TrackFragmentLayoutHolder
|
||||
|
||||
|
@ -58,12 +50,13 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
|||
val database: Database = (requireActivity().applicationContext as Trackbook).database
|
||||
val track: Track = Track(
|
||||
database=database,
|
||||
name=this.requireArguments().getString(Keys.ARG_TRACK_TITLE, ""),
|
||||
device_id= this.requireArguments().getString(Keys.ARG_TRACK_DEVICE_ID, ""),
|
||||
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)!!),
|
||||
)
|
||||
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
|
||||
layout.shareButton.setOnClickListener {
|
||||
|
@ -101,11 +94,9 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
|||
layout.saveViewStateToTrack()
|
||||
}
|
||||
|
||||
|
||||
/* Register the ActivityResultLauncher for saving GPX */
|
||||
private val requestSaveGpxLauncher = registerForActivityResult(StartActivityForResult(), this::requestSaveGpxResult)
|
||||
|
||||
|
||||
private fun requestSaveGpxResult(result: ActivityResult)
|
||||
{
|
||||
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 */
|
||||
private fun openSaveGpxDialog()
|
||||
{
|
||||
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 {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = Keys.MIME_TYPE_GPX
|
||||
|
|
|
@ -14,27 +14,21 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package org.y20k.trackbook
|
||||
|
||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.pm.PackageManager
|
||||
import android.database.Cursor
|
||||
import android.util.Log
|
||||
import androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
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.LogHelper
|
||||
import org.y20k.trackbook.helpers.PreferencesHelper
|
||||
import org.y20k.trackbook.helpers.PreferencesHelper.initPreferences
|
||||
import org.y20k.trackbook.helpers.iso8601
|
||||
import org.y20k.trackbook.helpers.iso8601_format
|
||||
import java.io.File
|
||||
|
||||
/*
|
||||
|
@ -54,17 +48,25 @@ class Trackbook(): Application() {
|
|||
// set Dark / Light theme state
|
||||
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()}")
|
||||
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()
|
||||
{
|
||||
Log.i("VOUSSOIR", "Trackbook.load_homepoints")
|
||||
this.homepoints.clear()
|
||||
homepoint_generator().forEach { homepoint -> this.homepoints.add(homepoint) }
|
||||
}
|
||||
|
@ -73,18 +75,14 @@ class Trackbook(): Application() {
|
|||
{
|
||||
if (! database.ready)
|
||||
{
|
||||
Log.i("VOUSSOIR", "Trackbook.homepoint_generator: database is not ready.")
|
||||
return@iterator
|
||||
}
|
||||
val cursor: Cursor = database.connection.query(
|
||||
"homepoints",
|
||||
arrayOf("lat", "lon", "radius", "name"),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
val cursor: Cursor = database.connection.rawQuery(
|
||||
"SELECT lat, lon, radius, name FROM homepoints",
|
||||
arrayOf()
|
||||
)
|
||||
Log.i("VOUSSOIR", "Trackbook.homepoint_generator: Got ${cursor.count} homepoints.")
|
||||
val COLUMN_LAT = cursor.getColumnIndex("lat")
|
||||
val COLUMN_LON = cursor.getColumnIndex("lon")
|
||||
val COLUMN_RADIUS = cursor.getColumnIndex("radius")
|
||||
|
|
|
@ -37,11 +37,7 @@ import android.util.Log
|
|||
import androidx.core.content.ContextCompat
|
||||
import java.util.*
|
||||
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 java.text.SimpleDateFormat
|
||||
|
||||
/*
|
||||
* TrackerService class
|
||||
|
@ -58,7 +54,7 @@ class TrackerService: Service(), SensorEventListener
|
|||
var useImperial: Boolean = false
|
||||
var gpsOnly: Boolean = false
|
||||
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 commitInterval: Int = Keys.COMMIT_INTERVAL
|
||||
var currentBestLocation: Location = LocationHelper.getDefaultLocation()
|
||||
|
@ -150,21 +146,11 @@ class TrackerService: Service(), SensorEventListener
|
|||
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
|
||||
{
|
||||
return object : LocationListener {
|
||||
override fun onLocationChanged(location: Location) {
|
||||
// update currentBestLocation if a better location is available
|
||||
override fun onLocationChanged(location: Location)
|
||||
{
|
||||
if (LocationHelper.isBetterLocation(location, currentBestLocation)) {
|
||||
currentBestLocation = location
|
||||
}
|
||||
|
@ -173,26 +159,16 @@ class TrackerService: Service(), SensorEventListener
|
|||
{
|
||||
LogHelper.v(TAG, "onProviderEnabled $provider")
|
||||
when (provider) {
|
||||
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(
|
||||
locationManager
|
||||
)
|
||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive =
|
||||
LocationHelper.isNetworkEnabled(
|
||||
locationManager
|
||||
)
|
||||
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
|
||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
||||
}
|
||||
}
|
||||
override fun onProviderDisabled(provider: String)
|
||||
{
|
||||
LogHelper.v(TAG, "onProviderDisabled $provider")
|
||||
when (provider) {
|
||||
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(
|
||||
locationManager
|
||||
)
|
||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive =
|
||||
LocationHelper.isNetworkEnabled(
|
||||
locationManager
|
||||
)
|
||||
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
|
||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
||||
}
|
||||
}
|
||||
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?)
|
||||
|
@ -278,7 +254,7 @@ class TrackerService: Service(), SensorEventListener
|
|||
|
||||
/* Overrides onSensorChanged from SensorEventListener */
|
||||
override fun onSensorChanged(sensorEvent: SensorEvent?) {
|
||||
var steps: Float = 0f
|
||||
var steps = 0f
|
||||
if (sensorEvent != null) {
|
||||
if (stepCountOffset == 0f) {
|
||||
// store steps previously recorded by the system
|
||||
|
@ -454,10 +430,10 @@ class TrackerService: Service(), SensorEventListener
|
|||
}
|
||||
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}.")
|
||||
return false;
|
||||
Log.i("VOUSSOIR", "Omitting due to homepoint ${homepoint.name}.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (track.trkpts.isEmpty())
|
||||
|
@ -478,16 +454,15 @@ class TrackerService: Service(), SensorEventListener
|
|||
{
|
||||
override fun run() {
|
||||
val now: Date = GregorianCalendar.getInstance().time
|
||||
val nowstr: String = iso8601(now)
|
||||
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)))
|
||||
{
|
||||
val values = ContentValues().apply {
|
||||
put("device_id", device_id)
|
||||
put("lat", trkpt.latitude)
|
||||
put("lon", trkpt.longitude)
|
||||
put("time", nowstr)
|
||||
put("time", now.time)
|
||||
put("accuracy", trkpt.accuracy)
|
||||
put("sat", trkpt.numberSatellites)
|
||||
put("ele", trkpt.altitude)
|
||||
|
@ -499,7 +474,7 @@ class TrackerService: Service(), SensorEventListener
|
|||
}
|
||||
trackbook.database.connection.insert("trkpt", null, values)
|
||||
track.trkpts.add(trkpt)
|
||||
if (track.trkpts.size > track.dequelimit)
|
||||
while (track.trkpts.size > 7200)
|
||||
{
|
||||
track.trkpts.removeFirst()
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook
|
||||
|
||||
import android.content.Intent
|
||||
|
@ -26,7 +25,6 @@ import android.service.quicksettings.TileService
|
|||
import org.y20k.trackbook.helpers.LogHelper
|
||||
import org.y20k.trackbook.helpers.PreferencesHelper
|
||||
|
||||
|
||||
/*
|
||||
* TrackingToggleTileService class
|
||||
*/
|
||||
|
@ -35,13 +33,11 @@ class TrackingToggleTileService: TileService() {
|
|||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(TrackingToggleTileService::class.java)
|
||||
|
||||
|
||||
/* Main class variables */
|
||||
private var bound: Boolean = false
|
||||
private var trackingState: Int = Keys.STATE_TRACKING_STOPPED
|
||||
private lateinit var trackerService: TrackerService
|
||||
|
||||
|
||||
/* Overrides onTileAdded from TileService */
|
||||
override fun onTileAdded() {
|
||||
super.onTileAdded()
|
||||
|
@ -56,7 +52,6 @@ class TrackingToggleTileService: TileService() {
|
|||
super.onTileRemoved()
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onStartListening from TileService (tile becomes visible) */
|
||||
override fun onStartListening() {
|
||||
super.onStartListening()
|
||||
|
@ -68,7 +63,6 @@ class TrackingToggleTileService: TileService() {
|
|||
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onClick from TileService */
|
||||
override fun onClick() {
|
||||
super.onClick()
|
||||
|
@ -78,7 +72,6 @@ class TrackingToggleTileService: TileService() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onStopListening from TileService (tile no longer visible) */
|
||||
override fun onStopListening() {
|
||||
super.onStopListening()
|
||||
|
@ -86,13 +79,11 @@ class TrackingToggleTileService: TileService() {
|
|||
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onDestroy from Service */
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
|
||||
/* Update quick settings tile */
|
||||
private fun updateTile() {
|
||||
val tile: Tile = qsTile
|
||||
|
@ -112,7 +103,6 @@ class TrackingToggleTileService: TileService() {
|
|||
tile.updateTile()
|
||||
}
|
||||
|
||||
|
||||
/* Start tracking */
|
||||
private fun startTracking() {
|
||||
val intent = Intent(application, TrackerService::class.java)
|
||||
|
@ -125,7 +115,6 @@ class TrackingToggleTileService: TileService() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Stop tracking */
|
||||
private fun stopTracking() {
|
||||
val intent = Intent(application, TrackerService::class.java)
|
||||
|
@ -133,7 +122,6 @@ class TrackingToggleTileService: TileService() {
|
|||
application.startService(intent)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Defines the listener for changes in shared preferences
|
||||
*/
|
||||
|
@ -151,5 +139,4 @@ class TrackingToggleTileService: TileService() {
|
|||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook
|
||||
|
||||
import YesNoDialog
|
||||
|
@ -34,13 +33,11 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import org.y20k.trackbook.core.Track
|
||||
import org.y20k.trackbook.helpers.LogHelper
|
||||
import org.y20k.trackbook.helpers.UiHelper
|
||||
import org.y20k.trackbook.helpers.iso8601_format
|
||||
import org.y20k.trackbook.tracklist.TracklistAdapter
|
||||
|
||||
|
||||
/*
|
||||
* TracklistFragment class
|
||||
*/
|
||||
|
|
|
@ -14,33 +14,26 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.core
|
||||
package org.y20k.trackbook
|
||||
|
||||
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 java.util.*
|
||||
|
||||
/*
|
||||
* WayPoint data class
|
||||
*/
|
||||
@Keep
|
||||
@Parcelize
|
||||
data class Trkpt(@Expose val provider: String,
|
||||
@Expose val latitude: Double,
|
||||
@Expose val longitude: Double,
|
||||
@Expose val altitude: Double,
|
||||
@Expose val accuracy: Float,
|
||||
@Expose val time: Date,
|
||||
@Expose val distanceToStartingPoint: Float = 0f,
|
||||
@Expose val numberSatellites: Int = 0,
|
||||
@Expose var isStopOver: Boolean = false,
|
||||
@Expose var starred: Boolean = false): Parcelable {
|
||||
|
||||
data class Trkpt(
|
||||
val provider: String,
|
||||
val latitude: Double,
|
||||
val longitude: Double,
|
||||
val altitude: Double,
|
||||
val accuracy: Float,
|
||||
val time: Long,
|
||||
val numberSatellites: Int = 0,
|
||||
var starred: Boolean = false
|
||||
)
|
||||
{
|
||||
/* Constructor using just Location */
|
||||
constructor(location: Location) : this (
|
||||
provider=location.provider.toString(),
|
||||
|
@ -48,19 +41,18 @@ data class Trkpt(@Expose val provider: String,
|
|||
longitude=location.longitude,
|
||||
altitude=location.altitude,
|
||||
accuracy=location.accuracy,
|
||||
time=Date(location.time),
|
||||
distanceToStartingPoint=0F,
|
||||
time=location.time,
|
||||
numberSatellites=LocationHelper.getNumberOfSatellites(location),
|
||||
)
|
||||
|
||||
/* Converts WayPoint into Location */
|
||||
fun toLocation(): Location {
|
||||
val location: Location = Location(provider)
|
||||
val location = Location(provider)
|
||||
location.latitude = latitude
|
||||
location.longitude = longitude
|
||||
location.altitude = altitude
|
||||
location.accuracy = accuracy
|
||||
location.time = this.time.time
|
||||
location.time = this.time
|
||||
return location
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.dialogs
|
||||
|
||||
import android.content.Context
|
||||
|
@ -29,7 +28,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.helpers.LogHelper
|
||||
|
||||
|
||||
/*
|
||||
* ErrorDialog object
|
||||
*/
|
||||
|
@ -38,7 +36,6 @@ object ErrorDialog {
|
|||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(ErrorDialog::class.java)
|
||||
|
||||
|
||||
/* Construct and show dialog */
|
||||
fun show(context: Context, errorTitle: Int, errorMessage: Int, errorDetails: String = String()) {
|
||||
// prepare dialog builder
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.dialogs
|
||||
|
||||
import android.content.Context
|
||||
|
@ -26,7 +25,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.helpers.LogHelper
|
||||
|
||||
|
||||
/*
|
||||
* RenameTrackDialog class
|
||||
*/
|
||||
|
@ -41,7 +39,6 @@ class RenameTrackDialog (private var renameTrackListener: RenameTrackListener) {
|
|||
/* Define log tag */
|
||||
private val TAG = LogHelper.makeLogTag(RenameTrackDialog::class.java.simpleName)
|
||||
|
||||
|
||||
/* Construct and show dialog */
|
||||
fun show(context: Context, trackName: String) {
|
||||
// prepare dialog builder
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
import android.content.Context
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.y20k.trackbook.Keys
|
||||
|
@ -32,11 +31,9 @@ class YesNoDialog (private var yesNoDialogListener: YesNoDialogListener) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Define log tag */
|
||||
private val TAG = LogHelper.makeLogTag(YesNoDialog::class.java.simpleName)
|
||||
|
||||
|
||||
/* Construct and show dialog - variant: message from string */
|
||||
fun show(context: Context,
|
||||
type: Int,
|
||||
|
@ -50,7 +47,6 @@ class YesNoDialog (private var yesNoDialogListener: YesNoDialogListener) {
|
|||
show(context, type, title, context.getString(message), yesButton, noButton, payload, payloadString)
|
||||
}
|
||||
|
||||
|
||||
/* Construct and show dialog */
|
||||
fun show(context: Context,
|
||||
type: Int,
|
||||
|
@ -70,7 +66,6 @@ class YesNoDialog (private var yesNoDialogListener: YesNoDialogListener) {
|
|||
builder.setTitle(context.getString(title))
|
||||
}
|
||||
|
||||
|
||||
// add yes button
|
||||
builder.setPositiveButton(yesButton) { _, _ ->
|
||||
// listen for click on yes button
|
||||
|
|
|
@ -14,16 +14,12 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.extensions
|
||||
|
||||
|
||||
import android.content.SharedPreferences
|
||||
|
||||
|
||||
/* Puts a Double value in SharedPreferences */
|
||||
fun SharedPreferences.Editor.putDouble(key: String, double: Double) = putLong(key, java.lang.Double.doubleToRawLongBits(double))
|
||||
|
||||
|
||||
/* gets a Double value from SharedPreferences */
|
||||
fun SharedPreferences.getDouble(key: String, default: Double) = java.lang.Double.longBitsToDouble(getLong(key, java.lang.Double.doubleToRawLongBits(default)))
|
|
@ -1,22 +1,24 @@
|
|||
package org.y20k.trackbook.helpers
|
||||
|
||||
import java.lang.Math.abs
|
||||
import java.security.SecureRandom
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.random.Random
|
||||
|
||||
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US)
|
||||
private val RNG = SecureRandom()
|
||||
|
||||
fun iso8601(datetime: Date): String
|
||||
{
|
||||
return iso8601_format.format(datetime)
|
||||
}
|
||||
|
||||
fun random_long(): Long
|
||||
{
|
||||
return (Random.nextBits(31).toLong() shl 32) + Random.nextBits(32)
|
||||
}
|
||||
|
||||
fun random_int(): Int
|
||||
{
|
||||
return Random.nextBits(31)
|
||||
return abs(RNG.nextInt())
|
||||
}
|
||||
|
||||
fun random_device_id(): String
|
||||
{
|
||||
return "myphone" + random_int()
|
||||
}
|
|
@ -14,18 +14,14 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.helpers
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import org.y20k.trackbook.Keys
|
||||
import org.y20k.trackbook.R
|
||||
|
||||
|
||||
/*
|
||||
* AppThemeHelper object
|
||||
*/
|
||||
|
@ -34,7 +30,6 @@ object AppThemeHelper {
|
|||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(AppThemeHelper::class.java)
|
||||
|
||||
|
||||
/* Sets app theme */
|
||||
fun setTheme(nightModeState: String) {
|
||||
when (nightModeState) {
|
||||
|
@ -67,14 +62,12 @@ object AppThemeHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return weather Night Mode is on, or not */
|
||||
fun isDarkModeOn(context: Context): Boolean {
|
||||
val nightMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
||||
return nightMode == Configuration.UI_MODE_NIGHT_YES
|
||||
}
|
||||
|
||||
|
||||
/* Returns a readable String for currently selected App Theme */
|
||||
fun getCurrentTheme(context: Context): String {
|
||||
return when (PreferencesHelper.loadThemeSelection()) {
|
||||
|
@ -83,22 +76,4 @@ object AppThemeHelper {
|
|||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.helpers
|
||||
|
||||
import android.content.Context
|
||||
|
@ -26,7 +25,6 @@ import java.text.SimpleDateFormat
|
|||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
/*
|
||||
* DateTimeHelper object
|
||||
*/
|
||||
|
@ -81,26 +79,22 @@ object DateTimeHelper {
|
|||
return timeString
|
||||
}
|
||||
|
||||
|
||||
/* Create sortable string for date - used for filenames */
|
||||
fun convertToSortableDateString(date: Date): String {
|
||||
val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US)
|
||||
return dateFormat.format(date)
|
||||
}
|
||||
|
||||
|
||||
/* Creates a readable string for date - used in the UI */
|
||||
fun convertToReadableDate(date: Date, dateStyle: Int = DateFormat.LONG): String {
|
||||
return DateFormat.getDateInstance(dateStyle, Locale.getDefault()).format(date)
|
||||
}
|
||||
|
||||
|
||||
/* Creates a readable string date and time - used in the UI */
|
||||
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)}"
|
||||
}
|
||||
|
||||
|
||||
/* Calculates time difference between two locations */
|
||||
fun calculateTimeDistance(previousLocation: Location?, location: Location): Long {
|
||||
var timeDifference: Long = 0L
|
||||
|
@ -112,5 +106,4 @@ object DateTimeHelper {
|
|||
return timeDifference
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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.")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.helpers
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
@ -22,19 +21,16 @@ import java.math.RoundingMode
|
|||
import java.text.NumberFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
/*
|
||||
* LengthUnitHelper object
|
||||
*/
|
||||
object LengthUnitHelper {
|
||||
|
||||
|
||||
/* Converts for the given unit system a distance value to a readable string */
|
||||
fun convertDistanceToString(distance: Float, useImperial: Boolean = false): String {
|
||||
return convertDistanceToString(distance.toDouble(), useImperial)
|
||||
}
|
||||
|
||||
|
||||
/* Converts for the given unit system a distance value to a readable string */
|
||||
fun convertDistanceToString(distance: Double, useImperial: Boolean = false): String {
|
||||
val readableDistance: Double
|
||||
|
@ -85,7 +81,6 @@ object LengthUnitHelper {
|
|||
return "${numberFormat.format(readableDistance)} $unit"
|
||||
}
|
||||
|
||||
|
||||
/* Determines which unit system the device is using (metric or imperial) */
|
||||
fun useImperialUnits(): Boolean {
|
||||
// America (US), Liberia (LR), Myanmar(MM) use the imperial system
|
||||
|
@ -94,7 +89,6 @@ object LengthUnitHelper {
|
|||
return imperialSystemCountries.contains(countryCode)
|
||||
}
|
||||
|
||||
|
||||
/* Converts for the given unit System distance and duration values to a readable velocity string */
|
||||
fun convertToVelocityString(velocity: Double, useImperialUnits: Boolean = false) : String {
|
||||
var speed: String = "0"
|
||||
|
@ -114,7 +108,6 @@ object LengthUnitHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Coverts meters per second to either km/h or mph */
|
||||
fun convertMetersPerSecond(metersPerSecond: Double, useImperial: Boolean = false): Double {
|
||||
if (useImperial) {
|
||||
|
@ -126,5 +119,4 @@ object LengthUnitHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.helpers
|
||||
|
||||
import android.Manifest
|
||||
|
@ -26,11 +25,8 @@ import android.os.Bundle
|
|||
import android.os.SystemClock
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.y20k.trackbook.Keys
|
||||
import org.y20k.trackbook.core.Track
|
||||
import java.util.*
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
/*
|
||||
* Keys object
|
||||
*/
|
||||
|
@ -39,7 +35,6 @@ object LocationHelper {
|
|||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(LocationHelper::class.java)
|
||||
|
||||
|
||||
/* Get default location */
|
||||
fun getDefaultLocation(): Location {
|
||||
val defaultLocation: Location = Location(LocationManager.NETWORK_PROVIDER)
|
||||
|
@ -68,9 +63,9 @@ object LocationHelper {
|
|||
return lastKnownLocation
|
||||
}
|
||||
|
||||
|
||||
/* 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
|
||||
|
||||
if (currentBestLocation == null) {
|
||||
|
@ -110,15 +105,18 @@ object LocationHelper {
|
|||
}
|
||||
|
||||
/* Checks if GPS location provider is available and enabled */
|
||||
fun isGpsEnabled(locationManager: LocationManager): Boolean {
|
||||
if (locationManager.allProviders.contains(LocationManager.GPS_PROVIDER)) {
|
||||
fun isGpsEnabled(locationManager: LocationManager): Boolean
|
||||
{
|
||||
if (locationManager.allProviders.contains(LocationManager.GPS_PROVIDER))
|
||||
{
|
||||
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Checks if Network location provider is available and enabled */
|
||||
fun isNetworkEnabled(locationManager: LocationManager): Boolean {
|
||||
if (locationManager.allProviders.contains(LocationManager.NETWORK_PROVIDER)) {
|
||||
|
@ -129,14 +127,12 @@ object LocationHelper {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* Checks if given location is new */
|
||||
fun isRecentEnough(location: Location): Boolean {
|
||||
val locationAge: Long = SystemClock.elapsedRealtimeNanos() - location.elapsedRealtimeNanos
|
||||
return locationAge < Keys.DEFAULT_THRESHOLD_LOCATION_AGE
|
||||
}
|
||||
|
||||
|
||||
/* Checks if given location is accurate */
|
||||
fun isAccurateEnough(location: Location, locationAccuracyThreshold: Int): 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 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 distance: Float = calculateDistance(previousLocation, location)
|
||||
val distance: Float = previousLocation.distanceTo(location)
|
||||
|
||||
// With 1*accuracyDelta we have 68% confidence that the points are
|
||||
// different. We can multiply this number to increase confidence but
|
||||
|
@ -174,18 +170,6 @@ object LocationHelper {
|
|||
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 */
|
||||
fun getNumberOfSatellites(location: Location): Int {
|
||||
val numberOfSatellites: Int
|
||||
|
@ -198,5 +182,4 @@ object LocationHelper {
|
|||
return numberOfSatellites
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -14,13 +14,11 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.helpers
|
||||
|
||||
import android.util.Log
|
||||
import org.y20k.trackbook.BuildConfig
|
||||
|
||||
|
||||
/*
|
||||
* LogHelper object
|
||||
*/
|
||||
|
|
|
@ -14,113 +14,58 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.helpers
|
||||
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.location.Location
|
||||
import android.os.Vibrator
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.osmdroid.api.IGeoPoint
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.ItemizedIconOverlay
|
||||
import org.osmdroid.views.overlay.OverlayItem
|
||||
import org.osmdroid.views.overlay.Polygon
|
||||
import org.osmdroid.views.overlay.simplefastpoint.LabelledGeoPoint
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
|
||||
import org.y20k.trackbook.Homepoint
|
||||
import org.y20k.trackbook.Keys
|
||||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.core.Track
|
||||
import org.y20k.trackbook.core.Trkpt
|
||||
import org.y20k.trackbook.Track
|
||||
import org.y20k.trackbook.Trkpt
|
||||
import java.text.DecimalFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
//private var currentPositionOverlay: ItemizedIconOverlay<OverlayItem>
|
||||
|
||||
|
||||
/* 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 */
|
||||
fun createTrackOverlay(context: Context, track: Track, trackingState: Int): SimpleFastPointOverlay
|
||||
fun createTrackOverlay(context: Context, map_view: MapView, track: Track, trackingState: Int)
|
||||
{
|
||||
// get marker color
|
||||
val color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.default_red)
|
||||
else context.getColor(R.color.default_blue)
|
||||
val color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.default_red) else context.getColor(R.color.default_blue)
|
||||
// gather points for overlay
|
||||
val points: MutableList<IGeoPoint> = mutableListOf()
|
||||
track.trkpts.forEach { wayPoint ->
|
||||
val label: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(wayPoint.time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(wayPoint.accuracy)} (${wayPoint.provider})"
|
||||
// only add normal points
|
||||
if (!wayPoint.starred && !wayPoint.isStopOver) {
|
||||
if (!wayPoint.starred)
|
||||
{
|
||||
points.add(LabelledGeoPoint(wayPoint.latitude, wayPoint.longitude, wayPoint.altitude, label))
|
||||
}
|
||||
}
|
||||
|
@ -137,22 +82,13 @@ class MapOverlayHelper (private var markerListener: MarkerListener) {
|
|||
.setPointStyle(style)
|
||||
.setRadius(6F * scalingFactor) // radius is set in px - scaling factor makes that display density independent (= dp)
|
||||
.setIsClickable(true)
|
||||
// .setCellSize(15) // Sets the grid cell size used for indexing, in pixels. Larger cells result in faster rendering speed, but worse fidelity. Default is 10 pixels, for large datasets (>10k points), use 15.
|
||||
// create and return overlay
|
||||
val overlay: SimpleFastPointOverlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
|
||||
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
|
||||
.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.
|
||||
val overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
|
||||
map_view.overlays.add(overlay)
|
||||
}
|
||||
|
||||
/* Creates overlay containing start, stop, stopover and starred markers for track */
|
||||
fun createSpecialMakersTrackOverlay(context: Context, 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 trackingActive: Boolean = trackingState == Keys.STATE_TRACKING_ACTIVE
|
||||
|
@ -162,51 +98,46 @@ class MapOverlayHelper (private var markerListener: MarkerListener) {
|
|||
var overlayItem: OverlayItem? = null
|
||||
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)!!)
|
||||
}
|
||||
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)!!)
|
||||
}
|
||||
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)!!)
|
||||
}
|
||||
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)!!)
|
||||
}
|
||||
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)!!)
|
||||
}
|
||||
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)!!)
|
||||
}
|
||||
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)
|
||||
{
|
||||
overlayItems.add(overlayItem)
|
||||
}
|
||||
}
|
||||
// create and return overlay for current position
|
||||
return createOverlay(context, overlayItems, enableStarring = true)
|
||||
map_view.overlays.add(createOverlay(context, overlayItems))
|
||||
}
|
||||
|
||||
/* 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 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})"
|
||||
|
@ -216,25 +147,19 @@ class MapOverlayHelper (private var markerListener: MarkerListener) {
|
|||
return item
|
||||
}
|
||||
|
||||
/* Creates an overlay */
|
||||
private fun createOverlay(context: Context, overlayItems: ArrayList<OverlayItem>, enableStarring: Boolean): ItemizedIconOverlay<OverlayItem> {
|
||||
fun createOverlay(context: Context, overlayItems: ArrayList<OverlayItem>): ItemizedIconOverlay<OverlayItem>
|
||||
{
|
||||
return ItemizedIconOverlay<OverlayItem>(context, overlayItems,
|
||||
object : ItemizedIconOverlay.OnItemGestureListener<OverlayItem> {
|
||||
override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean {
|
||||
if (enableStarring) {
|
||||
markerListener.onMarkerTapped(item.point.latitude, item.point.longitude)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
override fun onItemLongPress(index: Int, item: OverlayItem): Boolean {
|
||||
val v = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
v.vibrate(50)
|
||||
Toast.makeText(context, item.snippet, Toast.LENGTH_LONG).show()
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.helpers
|
||||
|
||||
import android.app.*
|
||||
|
@ -30,7 +29,6 @@ import org.y20k.trackbook.MainActivity
|
|||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.TrackerService
|
||||
|
||||
|
||||
/*
|
||||
* NotificationHelper class
|
||||
*/
|
||||
|
@ -39,11 +37,9 @@ class NotificationHelper(private val trackerService: TrackerService) {
|
|||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(NotificationHelper::class.java)
|
||||
|
||||
|
||||
/* Main class variables */
|
||||
private val notificationManager: NotificationManager = trackerService.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
|
||||
/* Creates notification */
|
||||
fun createNotification(trackingState: Int, timestamp: String): Notification {
|
||||
|
||||
|
|
|
@ -46,16 +46,23 @@ object PreferencesHelper {
|
|||
|
||||
fun load_device_id(): String
|
||||
{
|
||||
val v = sharedPreferences.getString(Keys.PREF_DEVICE_ID, random_int().toString()).toString();
|
||||
Log.i("VOUSSOIR", "Loaded device_id ${v}.")
|
||||
val fallback = random_device_id()
|
||||
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
|
||||
}
|
||||
|
||||
fun loadZoomLevel(): Double {
|
||||
fun loadZoomLevel(): Double
|
||||
{
|
||||
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) }
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.helpers
|
||||
|
||||
import android.content.Context
|
||||
|
@ -31,7 +30,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.tracklist.TracklistAdapter
|
||||
|
||||
|
||||
/*
|
||||
* UiHelper object
|
||||
*/
|
||||
|
@ -40,7 +38,6 @@ object UiHelper {
|
|||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(UiHelper::class.java)
|
||||
|
||||
|
||||
/* 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) {
|
||||
val scalingFactor: Float = context.resources.displayMetrics.density
|
||||
|
@ -55,7 +52,6 @@ object UiHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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) {
|
||||
val l: Int = ((width / 100.0f) * left).toInt()
|
||||
|
@ -65,7 +61,6 @@ object UiHelper {
|
|||
setViewMargins(context, view, l, r, t, b)
|
||||
}
|
||||
|
||||
|
||||
/* Get the height of the system's top status bar */
|
||||
fun getStatusBarHeight(context: Context): Int {
|
||||
var result: Int = 0
|
||||
|
@ -76,14 +71,12 @@ object UiHelper {
|
|||
return result
|
||||
}
|
||||
|
||||
|
||||
/* Get scaling factor from display density */
|
||||
fun getDensityScalingFactor(context: Context): Float {
|
||||
return context.resources.displayMetrics.density
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
|
|
@ -14,25 +14,22 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.tracklist
|
||||
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.y20k.trackbook.Keys
|
||||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.core.Database
|
||||
import org.y20k.trackbook.core.Track
|
||||
import org.y20k.trackbook.Database
|
||||
import org.y20k.trackbook.Track
|
||||
import org.y20k.trackbook.helpers.*
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
|
@ -65,27 +62,25 @@ class TracklistAdapter(val fragment: Fragment, val database: Database) : Recycle
|
|||
{
|
||||
return
|
||||
}
|
||||
val cursor: Cursor = database.connection.query(
|
||||
"trkpt",
|
||||
arrayOf("distinct strftime('%Y-%m-%d', time)", "device_id"),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"time DESC",
|
||||
null,
|
||||
val cursor: Cursor = database.connection.rawQuery(
|
||||
"SELECT distinct(date(time/1000, 'unixepoch', 'localtime')) as thedate, device_id FROM trkpt ORDER BY thedate DESC",
|
||||
arrayOf()
|
||||
)
|
||||
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())
|
||||
{
|
||||
val start_time: Date? = df.parse(cursor.getString(0) + "T00:00:00.000")
|
||||
val stop_time: Date? = df.parse(cursor.getString(0) + "T23:59:59.999")
|
||||
Log.i("VOUSSOIR", "TracklistAdapter prep track ${cursor.getString(0)}")
|
||||
val trackdate = cursor.getString(0)
|
||||
val device_id = cursor.getString(1)
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
/* Overrides getItemViewType */
|
||||
override fun getItemViewType(position: Int): Int
|
||||
{
|
||||
return Keys.VIEW_TYPE_TRACK
|
||||
}
|
||||
|
||||
|
||||
/* Overrides getItemCount from RecyclerView.Adapter */
|
||||
override fun getItemCount(): Int
|
||||
{
|
||||
return tracks.size
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onBindViewHolder from RecyclerView.Adapter */
|
||||
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 */
|
||||
fun getTrackName(positionInRecyclerView: Int): String
|
||||
{
|
||||
|
@ -188,7 +178,6 @@ class TracklistAdapter(val fragment: Fragment, val database: Database) : Recycle
|
|||
// return trackDataString
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Inner class: ViewHolder for a track element
|
||||
*/
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.ui
|
||||
|
||||
import android.Manifest
|
||||
|
@ -23,53 +22,57 @@ import android.app.Activity
|
|||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.location.Location
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.constraintlayout.widget.Group
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.textview.MaterialTextView
|
||||
import org.osmdroid.api.IMapController
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.ItemizedIconOverlay
|
||||
import org.osmdroid.views.overlay.OverlayItem
|
||||
import org.osmdroid.views.overlay.Polygon
|
||||
import org.osmdroid.views.overlay.TilesOverlay
|
||||
import org.osmdroid.views.overlay.compass.CompassOverlay
|
||||
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
||||
import org.y20k.trackbook.Homepoint
|
||||
import org.y20k.trackbook.Keys
|
||||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.Trackbook
|
||||
import org.y20k.trackbook.core.Track
|
||||
import org.y20k.trackbook.Track
|
||||
import org.y20k.trackbook.helpers.*
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
|
||||
/* Define log tag */
|
||||
private val TAG: String = LogHelper.makeLogTag(MapFragmentLayoutHolder::class.java)
|
||||
|
||||
|
||||
data class MapFragmentLayoutHolder(
|
||||
private var context: Context,
|
||||
private var inflater: LayoutInflater,
|
||||
private var container: ViewGroup?,
|
||||
private var statusBarHeight: Int,
|
||||
private val startLocation: Location,
|
||||
private val trackingState: Int
|
||||
)
|
||||
{
|
||||
/* Main class variables */
|
||||
val rootView: View
|
||||
var userInteraction: Boolean = false
|
||||
val currentLocationButton: FloatingActionButton
|
||||
val mainButton: ExtendedFloatingActionButton
|
||||
private val mapView: MapView
|
||||
private val homepoint_overlays: ArrayList<ItemizedIconOverlay<OverlayItem>> = ArrayList()
|
||||
private var currentPositionOverlay: ItemizedIconOverlay<OverlayItem>
|
||||
private var current_location_radius: Polygon = Polygon()
|
||||
private var currentTrackOverlay: SimpleFastPointOverlay?
|
||||
private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay<OverlayItem>?
|
||||
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 zoomLevel: Double
|
||||
|
||||
|
||||
/* Init block */
|
||||
init {
|
||||
// find views
|
||||
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)
|
||||
|
||||
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
|
||||
currentPositionOverlay = MapOverlayHelper(markerListener).createMyLocationOverlay(context, startLocation, trackingState)
|
||||
currentPositionOverlay = createOverlay(context, ArrayList<OverlayItem>())
|
||||
mapView.overlays.add(currentPositionOverlay)
|
||||
centerMap(startLocation)
|
||||
|
||||
|
@ -131,7 +133,6 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
|||
addInteractionListener()
|
||||
}
|
||||
|
||||
|
||||
/* Listen for user interaction */
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun addInteractionListener() {
|
||||
|
@ -141,7 +142,6 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Set map center */
|
||||
fun centerMap(location: Location, animated: Boolean = false) {
|
||||
val position = GeoPoint(location.latitude, location.longitude)
|
||||
|
@ -152,7 +152,6 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
|||
userInteraction = false
|
||||
}
|
||||
|
||||
|
||||
/* Save current best location and state of map to shared preferences */
|
||||
fun saveState(currentBestLocation: Location) {
|
||||
PreferencesHelper.saveCurrentBestLocation(currentBestLocation)
|
||||
|
@ -161,14 +160,78 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
|||
userInteraction = false
|
||||
}
|
||||
|
||||
|
||||
/* Mark current position on map */
|
||||
fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED) {
|
||||
mapView.overlays.remove(currentPositionOverlay)
|
||||
currentPositionOverlay = MapOverlayHelper(markerListener).createMyLocationOverlay(context, location, trackingState)
|
||||
mapView.overlays.add(currentPositionOverlay)
|
||||
fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED)
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.markCurrentPosition")
|
||||
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 */
|
||||
fun overlayCurrentTrack(track: Track, trackingState: Int) {
|
||||
|
@ -179,11 +242,8 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
|||
mapView.overlays.remove(currentTrackSpecialMarkerOverlay)
|
||||
}
|
||||
if (track.trkpts.isNotEmpty()) {
|
||||
val mapOverlayHelper: MapOverlayHelper = MapOverlayHelper(markerListener)
|
||||
currentTrackOverlay = mapOverlayHelper.createTrackOverlay(context, track, trackingState)
|
||||
currentTrackSpecialMarkerOverlay = mapOverlayHelper.createSpecialMakersTrackOverlay(context, track, trackingState)
|
||||
mapView.overlays.add(currentTrackSpecialMarkerOverlay)
|
||||
mapView.overlays.add(currentTrackOverlay)
|
||||
createTrackOverlay(context, mapView, track, trackingState)
|
||||
createSpecialMakersTrackOverlay(context, mapView, track, trackingState)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,7 +266,6 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Toggles content and visibility of the location error snackbar */
|
||||
fun toggleLocationErrorBar(gpsProviderActive: Boolean, networkProviderActive: Boolean) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
|
||||
package org.y20k.trackbook.ui
|
||||
|
||||
import android.app.Activity
|
||||
|
@ -46,19 +45,17 @@ import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
|
|||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
||||
import org.y20k.trackbook.Keys
|
||||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.core.Track
|
||||
import org.y20k.trackbook.core.TrackStatistics
|
||||
import org.y20k.trackbook.Track
|
||||
import org.y20k.trackbook.TrackStatistics
|
||||
import org.y20k.trackbook.helpers.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
/*
|
||||
* 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 container: ViewGroup?,
|
||||
var track: Track
|
||||
|
@ -71,8 +68,6 @@ data class TrackFragmentLayoutHolder(
|
|||
val editButton: ImageButton
|
||||
val trackNameView: MaterialTextView
|
||||
private val mapView: MapView
|
||||
private var trackSpecialMarkersOverlay: ItemizedIconOverlay<OverlayItem>?
|
||||
private var trackOverlay: SimpleFastPointOverlay?
|
||||
private var controller: IMapController
|
||||
//private var zoomLevel: Double
|
||||
private val statisticsSheetBehavior: BottomSheetBehavior<View>
|
||||
|
@ -96,7 +91,6 @@ data class TrackFragmentLayoutHolder(
|
|||
private val trackManagementViews: Group
|
||||
private val useImperialUnits: Boolean
|
||||
|
||||
|
||||
/* Init block */
|
||||
init {
|
||||
// find views
|
||||
|
@ -152,13 +146,9 @@ data class TrackFragmentLayoutHolder(
|
|||
// compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / UiHelper.getDensityScalingFactor(context))) TODO REMOVE
|
||||
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()) {
|
||||
mapView.overlays.add(trackSpecialMarkersOverlay)
|
||||
mapView.overlays.add(trackOverlay)
|
||||
createSpecialMakersTrackOverlay(context, mapView, track, Keys.STATE_TRACKING_STOPPED, displayStartEndMarker = true)
|
||||
createTrackOverlay(context, mapView, track, Keys.STATE_TRACKING_STOPPED)
|
||||
}
|
||||
|
||||
// set up and show statistics sheet
|
||||
|
@ -168,25 +158,6 @@ data class TrackFragmentLayoutHolder(
|
|||
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 */
|
||||
fun saveViewStateToTrack()
|
||||
{
|
||||
|
@ -195,7 +166,6 @@ data class TrackFragmentLayoutHolder(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Sets up the statistics sheet */
|
||||
private fun setupStatisticsViews()
|
||||
{
|
||||
|
@ -225,7 +195,6 @@ data class TrackFragmentLayoutHolder(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Shows/hides the statistics sheet */
|
||||
private fun toggleStatisticsSheetVisibility() {
|
||||
when (statisticsSheetBehavior.state) {
|
||||
|
@ -263,7 +232,6 @@ data class TrackFragmentLayoutHolder(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onZoom from MapListener */
|
||||
override fun onZoom(event: ZoomEvent?): Boolean {
|
||||
if (event == null) {
|
||||
|
@ -274,7 +242,6 @@ data class TrackFragmentLayoutHolder(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onScroll from MapListener */
|
||||
override fun onScroll(event: ScrollEvent?): Boolean {
|
||||
if (event == null) {
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
android:viewportWidth="96.0"
|
||||
android:width="24dp">
|
||||
<path
|
||||
android:fillAlpha="0.33"
|
||||
android:fillAlpha="0.0"
|
||||
android:fillColor="@color/default_red"
|
||||
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:fillColor="#ff000000"
|
||||
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
|
||||
</vector>
|
|
@ -3,10 +3,6 @@
|
|||
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_blue"
|
||||
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
|
||||
|
|
|
@ -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>
|
|
@ -4,7 +4,7 @@
|
|||
android:viewportWidth="96.0"
|
||||
android:width="24dp">
|
||||
<path
|
||||
android:fillAlpha="0.33"
|
||||
android:fillAlpha="0.0"
|
||||
android:fillColor="@color/default_red"
|
||||
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
|
||||
<path
|
||||
|
|
Loading…
Reference in a new issue