From df77b089ac1c875ad92bc23ab4fb158756175132 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Thu, 9 Mar 2023 20:24:06 -0800 Subject: [PATCH] checkpoint --- .../org/y20k/trackbook/{core => }/Database.kt | 8 +- .../y20k/trackbook/{core => }/Homepoint.kt | 5 +- app/src/main/java/org/y20k/trackbook/Keys.kt | 4 +- .../java/org/y20k/trackbook/MainActivity.kt | 112 +++--- .../java/org/y20k/trackbook/MapFragment.kt | 9 +- .../org/y20k/trackbook/SettingsFragment.kt | 21 +- .../org/y20k/trackbook/{core => }/Track.kt | 46 +-- .../java/org/y20k/trackbook/TrackFragment.kt | 27 +- .../main/java/org/y20k/trackbook/Trackbook.kt | 46 ++- .../java/org/y20k/trackbook/TrackerService.kt | 53 +-- .../trackbook/TrackingToggleTileService.kt | 13 - .../org/y20k/trackbook/TracklistFragment.kt | 3 - .../org/y20k/trackbook/{core => }/Trkpt.kt | 38 +-- .../org/y20k/trackbook/dialogs/ErrorDialog.kt | 3 - .../trackbook/dialogs/RenameTrackDialog.kt | 3 - .../org/y20k/trackbook/dialogs/YesNoDialog.kt | 5 - .../extensions/SharedPreferencesExt.kt | 4 - .../y20k/trackbook/{helpers => }/functions.kt | 16 +- .../y20k/trackbook/helpers/AppThemeHelper.kt | 25 -- .../y20k/trackbook/helpers/DateTimeHelper.kt | 7 - .../org/y20k/trackbook/helpers/FileHelper.kt | 98 ------ .../trackbook/helpers/LengthUnitHelper.kt | 8 - .../y20k/trackbook/helpers/LocationHelper.kt | 37 +- .../org/y20k/trackbook/helpers/LogHelper.kt | 2 - .../trackbook/helpers/MapOverlayHelper.kt | 319 +++++++----------- .../trackbook/helpers/NotificationHelper.kt | 4 - .../trackbook/helpers/PreferencesHelper.kt | 15 +- .../org/y20k/trackbook/helpers/TrackHelper.kt | 47 --- .../org/y20k/trackbook/helpers/UiHelper.kt | 7 - .../trackbook/tracklist/TracklistAdapter.kt | 39 +-- .../trackbook/ui/MapFragmentLayoutHolder.kt | 122 +++++-- .../trackbook/ui/TrackFragmentLayoutHolder.kt | 41 +-- ....xml => ic_marker_location_black_24dp.xml} | 4 +- .../drawable/ic_marker_location_blue_24dp.xml | 4 - .../ic_marker_location_blue_grey_24dp.xml | 13 - .../drawable/ic_marker_location_red_24dp.xml | 2 +- 36 files changed, 406 insertions(+), 804 deletions(-) rename app/src/main/java/org/y20k/trackbook/{core => }/Database.kt (82%) rename app/src/main/java/org/y20k/trackbook/{core => }/Homepoint.kt (94%) rename app/src/main/java/org/y20k/trackbook/{core => }/Track.kt (80%) rename app/src/main/java/org/y20k/trackbook/{core => }/Trkpt.kt (56%) rename app/src/main/java/org/y20k/trackbook/{helpers => }/functions.kt (61%) delete mode 100644 app/src/main/java/org/y20k/trackbook/helpers/FileHelper.kt delete mode 100644 app/src/main/java/org/y20k/trackbook/helpers/TrackHelper.kt rename app/src/main/res/drawable/{ic_marker_location_red_grey_24dp.xml => ic_marker_location_black_24dp.xml} (81%) delete mode 100644 app/src/main/res/drawable/ic_marker_location_blue_grey_24dp.xml diff --git a/app/src/main/java/org/y20k/trackbook/core/Database.kt b/app/src/main/java/org/y20k/trackbook/Database.kt similarity index 82% rename from app/src/main/java/org/y20k/trackbook/core/Database.kt rename to app/src/main/java/org/y20k/trackbook/Database.kt index f9d3997..b547908 100644 --- a/app/src/main/java/org/y20k/trackbook/core/Database.kt +++ b/app/src/main/java/org/y20k/trackbook/Database.kt @@ -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() diff --git a/app/src/main/java/org/y20k/trackbook/core/Homepoint.kt b/app/src/main/java/org/y20k/trackbook/Homepoint.kt similarity index 94% rename from app/src/main/java/org/y20k/trackbook/core/Homepoint.kt rename to app/src/main/java/org/y20k/trackbook/Homepoint.kt index 1acef79..9822011 100644 --- a/app/src/main/java/org/y20k/trackbook/core/Homepoint.kt +++ b/app/src/main/java/org/y20k/trackbook/Homepoint.kt @@ -1,5 +1,4 @@ -package org.y20k.trackbook.core - +package org.y20k.trackbook import android.location.Location import java.util.* @@ -17,4 +16,4 @@ class Homepoint(val latitude: Double, val longitude: Double, val radius: Double, location.time = GregorianCalendar.getInstance().time.time return location } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/y20k/trackbook/Keys.kt b/app/src/main/java/org/y20k/trackbook/Keys.kt index 3763cd9..e9b59e4 100644 --- a/app/src/main/java/org/y20k/trackbook/Keys.kt +++ b/app/src/main/java/org/y20k/trackbook/Keys.kt @@ -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 diff --git a/app/src/main/java/org/y20k/trackbook/MainActivity.kt b/app/src/main/java/org/y20k/trackbook/MainActivity.kt index ea51654..6802a6c 100644 --- a/app/src/main/java/org/y20k/trackbook/MainActivity.kt +++ b/app/src/main/java/org/y20k/trackbook/MainActivity.kt @@ -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( - 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() + 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, + grantResults: IntArray + ) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + Log.i("VOUSSOIR", "MainActivity tries to load the database.") + trackbook.load_database() + } } diff --git a/app/src/main/java/org/y20k/trackbook/MapFragment.kt b/app/src/main/java/org/y20k/trackbook/MapFragment.kt index c41bb7e..e017c12 100644 --- a/app/src/main/java/org/y20k/trackbook/MapFragment.kt +++ b/app/src/main/java/org/y20k/trackbook/MapFragment.kt @@ -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 { diff --git a/app/src/main/java/org/y20k/trackbook/SettingsFragment.kt b/app/src/main/java/org/y20k/trackbook/SettingsFragment.kt index 224b69f..fff93c8 100644 --- a/app/src/main/java/org/y20k/trackbook/SettingsFragment.kt +++ b/app/src/main/java/org/y20k/trackbook/SettingsFragment.kt @@ -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) { diff --git a/app/src/main/java/org/y20k/trackbook/core/Track.kt b/app/src/main/java/org/y20k/trackbook/Track.kt similarity index 80% rename from app/src/main/java/org/y20k/trackbook/core/Track.kt rename to app/src/main/java/org/y20k/trackbook/Track.kt index 4ea965c..8511f79 100644 --- a/app/src/main/java/org/y20k/trackbook/core/Track.kt +++ b/app/src/main/java/org/y20k/trackbook/Track.kt @@ -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 = ArrayDeque(), 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 = ArrayDeque(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 { - 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) diff --git a/app/src/main/java/org/y20k/trackbook/TrackFragment.kt b/app/src/main/java/org/y20k/trackbook/TrackFragment.kt index 393ea5d..b98b4ae 100644 --- a/app/src/main/java/org/y20k/trackbook/TrackFragment.kt +++ b/app/src/main/java/org/y20k/trackbook/TrackFragment.kt @@ -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 diff --git a/app/src/main/java/org/y20k/trackbook/Trackbook.kt b/app/src/main/java/org/y20k/trackbook/Trackbook.kt index b90dfa5..637e2cd 100644 --- a/app/src/main/java/org/y20k/trackbook/Trackbook.kt +++ b/app/src/main/java/org/y20k/trackbook/Trackbook.kt @@ -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") diff --git a/app/src/main/java/org/y20k/trackbook/TrackerService.kt b/app/src/main/java/org/y20k/trackbook/TrackerService.kt index 1b0ee32..2f610fb 100644 --- a/app/src/main/java/org/y20k/trackbook/TrackerService.kt +++ b/app/src/main/java/org/y20k/trackbook/TrackerService.kt @@ -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() } diff --git a/app/src/main/java/org/y20k/trackbook/TrackingToggleTileService.kt b/app/src/main/java/org/y20k/trackbook/TrackingToggleTileService.kt index 773d1dc..b86652f 100644 --- a/app/src/main/java/org/y20k/trackbook/TrackingToggleTileService.kt +++ b/app/src/main/java/org/y20k/trackbook/TrackingToggleTileService.kt @@ -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() { - } \ No newline at end of file diff --git a/app/src/main/java/org/y20k/trackbook/TracklistFragment.kt b/app/src/main/java/org/y20k/trackbook/TracklistFragment.kt index f6cce15..e97631b 100644 --- a/app/src/main/java/org/y20k/trackbook/TracklistFragment.kt +++ b/app/src/main/java/org/y20k/trackbook/TracklistFragment.kt @@ -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 */ diff --git a/app/src/main/java/org/y20k/trackbook/core/Trkpt.kt b/app/src/main/java/org/y20k/trackbook/Trkpt.kt similarity index 56% rename from app/src/main/java/org/y20k/trackbook/core/Trkpt.kt rename to app/src/main/java/org/y20k/trackbook/Trkpt.kt index 7ed4b9c..1254b10 100644 --- a/app/src/main/java/org/y20k/trackbook/core/Trkpt.kt +++ b/app/src/main/java/org/y20k/trackbook/Trkpt.kt @@ -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 } diff --git a/app/src/main/java/org/y20k/trackbook/dialogs/ErrorDialog.kt b/app/src/main/java/org/y20k/trackbook/dialogs/ErrorDialog.kt index f4c6d3a..3e18b76 100644 --- a/app/src/main/java/org/y20k/trackbook/dialogs/ErrorDialog.kt +++ b/app/src/main/java/org/y20k/trackbook/dialogs/ErrorDialog.kt @@ -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 diff --git a/app/src/main/java/org/y20k/trackbook/dialogs/RenameTrackDialog.kt b/app/src/main/java/org/y20k/trackbook/dialogs/RenameTrackDialog.kt index 16d155d..d01099c 100644 --- a/app/src/main/java/org/y20k/trackbook/dialogs/RenameTrackDialog.kt +++ b/app/src/main/java/org/y20k/trackbook/dialogs/RenameTrackDialog.kt @@ -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 diff --git a/app/src/main/java/org/y20k/trackbook/dialogs/YesNoDialog.kt b/app/src/main/java/org/y20k/trackbook/dialogs/YesNoDialog.kt index 8df53a9..3f659db 100644 --- a/app/src/main/java/org/y20k/trackbook/dialogs/YesNoDialog.kt +++ b/app/src/main/java/org/y20k/trackbook/dialogs/YesNoDialog.kt @@ -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 diff --git a/app/src/main/java/org/y20k/trackbook/extensions/SharedPreferencesExt.kt b/app/src/main/java/org/y20k/trackbook/extensions/SharedPreferencesExt.kt index b991951..a1f1ffd 100644 --- a/app/src/main/java/org/y20k/trackbook/extensions/SharedPreferencesExt.kt +++ b/app/src/main/java/org/y20k/trackbook/extensions/SharedPreferencesExt.kt @@ -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))) \ No newline at end of file diff --git a/app/src/main/java/org/y20k/trackbook/helpers/functions.kt b/app/src/main/java/org/y20k/trackbook/functions.kt similarity index 61% rename from app/src/main/java/org/y20k/trackbook/helpers/functions.kt rename to app/src/main/java/org/y20k/trackbook/functions.kt index b738802..85fcb37 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/functions.kt +++ b/app/src/main/java/org/y20k/trackbook/functions.kt @@ -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() } \ No newline at end of file diff --git a/app/src/main/java/org/y20k/trackbook/helpers/AppThemeHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/AppThemeHelper.kt index 6e6fa9f..0ad77db 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/AppThemeHelper.kt +++ b/app/src/main/java/org/y20k/trackbook/helpers/AppThemeHelper.kt @@ -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 - } - - - - } \ No newline at end of file diff --git a/app/src/main/java/org/y20k/trackbook/helpers/DateTimeHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/DateTimeHelper.kt index 4d4813a..c8274c4 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/DateTimeHelper.kt +++ b/app/src/main/java/org/y20k/trackbook/helpers/DateTimeHelper.kt @@ -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 } - } \ No newline at end of file diff --git a/app/src/main/java/org/y20k/trackbook/helpers/FileHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/FileHelper.kt deleted file mode 100644 index adafc83..0000000 --- a/app/src/main/java/org/y20k/trackbook/helpers/FileHelper.kt +++ /dev/null @@ -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.") - } - } -} diff --git a/app/src/main/java/org/y20k/trackbook/helpers/LengthUnitHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/LengthUnitHelper.kt index 66c358b..338573b 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/LengthUnitHelper.kt +++ b/app/src/main/java/org/y20k/trackbook/helpers/LengthUnitHelper.kt @@ -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 { } } - } \ No newline at end of file diff --git a/app/src/main/java/org/y20k/trackbook/helpers/LocationHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/LocationHelper.kt index 6206004..0efbaa8 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/LocationHelper.kt +++ b/app/src/main/java/org/y20k/trackbook/helpers/LocationHelper.kt @@ -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 } - } diff --git a/app/src/main/java/org/y20k/trackbook/helpers/LogHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/LogHelper.kt index 6af9800..32d7bb9 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/LogHelper.kt +++ b/app/src/main/java/org/y20k/trackbook/helpers/LogHelper.kt @@ -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 */ diff --git a/app/src/main/java/org/y20k/trackbook/helpers/MapOverlayHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/MapOverlayHelper.kt index eb6bb67..13173fe 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/MapOverlayHelper.kt +++ b/app/src/main/java/org/y20k/trackbook/helpers/MapOverlayHelper.kt @@ -14,227 +14,152 @@ * 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 - /* Creates icon overlay for current position (used in MapFragment) */ - fun createMyLocationOverlay(context: Context, location: Location, trackingState: Int): ItemizedIconOverlay - { - val overlayItems: ArrayList = ArrayList() - 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 - { - val overlayItems: ArrayList = ArrayList() - - // 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 - { - // get marker color - val color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.default_red) - else context.getColor(R.color.default_blue) - // gather points for overlay - val points: MutableList = 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) { - points.add(LabelledGeoPoint(wayPoint.latitude, wayPoint.longitude, wayPoint.altitude, label)) - } - } - val pointTheme: SimplePointTheme = SimplePointTheme(points, false) - // set styling for overlay - val style: Paint = Paint() - style.style = Paint.Style.FILL - style.color = color - style.flags = Paint.ANTI_ALIAS_FLAG - val scalingFactor: Float = UiHelper.getDensityScalingFactor(context) - val overlayOptions: SimpleFastPointOverlayOptions = SimpleFastPointOverlayOptions.getDefaultStyle() - .setAlgorithm(SimpleFastPointOverlayOptions.RenderingAlgorithm.MAXIMUM_OPTIMIZATION) - .setSymbol(SimpleFastPointOverlayOptions.Shape.CIRCLE) - .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 - } - - /* Creates overlay containing start, stop, stopover and starred markers for track */ - fun createSpecialMakersTrackOverlay(context: Context, track: Track, trackingState: Int, displayStartEndMarker: Boolean = false): ItemizedIconOverlay - { - val overlayItems: ArrayList = ArrayList() - val trackingActive: Boolean = trackingState == Keys.STATE_TRACKING_ACTIVE - val maxIndex: Int = track.trkpts.size - 1 - - track.trkpts.forEachIndexed { index: Int, trkpt: Trkpt -> - 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.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.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.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.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.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.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) - } - - /* Creates a marker overlay item */ - private 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})" - val position: GeoPoint = GeoPoint(latitude, longitude) - val item: OverlayItem = OverlayItem(title, description, position) - item.markerHotspot = OverlayItem.HotspotPlace.CENTER - return item - } - - /* Creates an overlay */ - private fun createOverlay(context: Context, overlayItems: ArrayList, enableStarring: Boolean): ItemizedIconOverlay { - return ItemizedIconOverlay(context, overlayItems, - object : ItemizedIconOverlay.OnItemGestureListener { - 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 - } - }) - } +/* Creates icon overlay for current position (used in MapFragment) */ +fun createMyLocationOverlay(context: Context, map_view: MapView, currentPositionOverlay: ItemizedIconOverlay, location: Location, trackingState: Int) +{ } + + +/* Creates icon overlay for track */ +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) + // gather points for overlay + val points: MutableList = 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) + { + points.add(LabelledGeoPoint(wayPoint.latitude, wayPoint.longitude, wayPoint.altitude, label)) + } + } + val pointTheme: SimplePointTheme = SimplePointTheme(points, false) + // set styling for overlay + val style: Paint = Paint() + style.style = Paint.Style.FILL + style.color = color + style.flags = Paint.ANTI_ALIAS_FLAG + val scalingFactor: Float = UiHelper.getDensityScalingFactor(context) + val overlayOptions: SimpleFastPointOverlayOptions = SimpleFastPointOverlayOptions.getDefaultStyle() + .setAlgorithm(SimpleFastPointOverlayOptions.RenderingAlgorithm.MAXIMUM_OPTIMIZATION) + .setSymbol(SimpleFastPointOverlayOptions.Shape.CIRCLE) + .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. + val overlay = SimpleFastPointOverlay(pointTheme, overlayOptions) + map_view.overlays.add(overlay) +} + +/* Creates overlay containing start, stop, stopover and starred markers for track */ +fun createSpecialMakersTrackOverlay(context: Context, map_view: MapView, track: Track, trackingState: Int, displayStartEndMarker: Boolean = false) +{ + val overlayItems: ArrayList = ArrayList() + val trackingActive: Boolean = trackingState == Keys.STATE_TRACKING_ACTIVE + val maxIndex: Int = track.trkpts.size - 1 + + track.trkpts.forEachIndexed { index: Int, trkpt: Trkpt -> + var overlayItem: OverlayItem? = null + if (!trackingActive && index == 0 && displayStartEndMarker && trkpt.starred) + { + 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) + 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) + 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) + 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) + 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) + overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_star_red_24dp)!!) + } + if (overlayItem != null) + { + overlayItems.add(overlayItem) + } + } + map_view.overlays.add(createOverlay(context, overlayItems)) +} + + + +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})" + val position: GeoPoint = GeoPoint(latitude, longitude) + val item: OverlayItem = OverlayItem(title, description, position) + item.markerHotspot = OverlayItem.HotspotPlace.CENTER + return item +} + +fun createOverlay(context: Context, overlayItems: ArrayList): ItemizedIconOverlay +{ + return ItemizedIconOverlay(context, overlayItems, + object : ItemizedIconOverlay.OnItemGestureListener { + override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean { + 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 + } + } + ) +} diff --git a/app/src/main/java/org/y20k/trackbook/helpers/NotificationHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/NotificationHelper.kt index 8a15e15..f05b227 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/NotificationHelper.kt +++ b/app/src/main/java/org/y20k/trackbook/helpers/NotificationHelper.kt @@ -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 { diff --git a/app/src/main/java/org/y20k/trackbook/helpers/PreferencesHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/PreferencesHelper.kt index 47d7980..5f05132 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/PreferencesHelper.kt +++ b/app/src/main/java/org/y20k/trackbook/helpers/PreferencesHelper.kt @@ -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) } } diff --git a/app/src/main/java/org/y20k/trackbook/helpers/TrackHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/TrackHelper.kt deleted file mode 100644 index 9f8f6c2..0000000 --- a/app/src/main/java/org/y20k/trackbook/helpers/TrackHelper.kt +++ /dev/null @@ -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() - } - } - } - } -} diff --git a/app/src/main/java/org/y20k/trackbook/helpers/UiHelper.kt b/app/src/main/java/org/y20k/trackbook/helpers/UiHelper.kt index f1af4fc..2a167cc 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/UiHelper.kt +++ b/app/src/main/java/org/y20k/trackbook/helpers/UiHelper.kt @@ -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 diff --git a/app/src/main/java/org/y20k/trackbook/tracklist/TracklistAdapter.kt b/app/src/main/java/org/y20k/trackbook/tracklist/TracklistAdapter.kt index 12f5774..6102def 100644 --- a/app/src/main/java/org/y20k/trackbook/tracklist/TracklistAdapter.kt +++ b/app/src/main/java/org/y20k/trackbook/tracklist/TracklistAdapter.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 */ diff --git a/app/src/main/java/org/y20k/trackbook/ui/MapFragmentLayoutHolder.kt b/app/src/main/java/org/y20k/trackbook/ui/MapFragmentLayoutHolder.kt index 5e6b153..53b65e1 100644 --- a/app/src/main/java/org/y20k/trackbook/ui/MapFragmentLayoutHolder.kt +++ b/app/src/main/java/org/y20k/trackbook/ui/MapFragmentLayoutHolder.kt @@ -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> = ArrayList() private var currentPositionOverlay: ItemizedIconOverlay + private var current_location_radius: Polygon = Polygon() private var currentTrackOverlay: SimpleFastPointOverlay? private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay? 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()) 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) + { + Log.i("VOUSSOIR", "MapFragmentLayoutHolder.createHomepointOverlays") + val overlayItems: java.util.ArrayList = java.util.ArrayList() + + 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() } } - } diff --git a/app/src/main/java/org/y20k/trackbook/ui/TrackFragmentLayoutHolder.kt b/app/src/main/java/org/y20k/trackbook/ui/TrackFragmentLayoutHolder.kt index f6d2d11..2b6f398 100644 --- a/app/src/main/java/org/y20k/trackbook/ui/TrackFragmentLayoutHolder.kt +++ b/app/src/main/java/org/y20k/trackbook/ui/TrackFragmentLayoutHolder.kt @@ -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? - private var trackOverlay: SimpleFastPointOverlay? private var controller: IMapController //private var zoomLevel: Double private val statisticsSheetBehavior: BottomSheetBehavior @@ -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) { diff --git a/app/src/main/res/drawable/ic_marker_location_red_grey_24dp.xml b/app/src/main/res/drawable/ic_marker_location_black_24dp.xml similarity index 81% rename from app/src/main/res/drawable/ic_marker_location_red_grey_24dp.xml rename to app/src/main/res/drawable/ic_marker_location_black_24dp.xml index f49d46b..7fbed74 100644 --- a/app/src/main/res/drawable/ic_marker_location_red_grey_24dp.xml +++ b/app/src/main/res/drawable/ic_marker_location_black_24dp.xml @@ -4,10 +4,10 @@ android:viewportWidth="96.0" android:width="24dp"> diff --git a/app/src/main/res/drawable/ic_marker_location_blue_24dp.xml b/app/src/main/res/drawable/ic_marker_location_blue_24dp.xml index 0556d40..5e1dd63 100755 --- a/app/src/main/res/drawable/ic_marker_location_blue_24dp.xml +++ b/app/src/main/res/drawable/ic_marker_location_blue_24dp.xml @@ -3,10 +3,6 @@ android:viewportHeight="96.0" android:viewportWidth="96.0" android:width="24dp"> - diff --git a/app/src/main/res/drawable/ic_marker_location_blue_grey_24dp.xml b/app/src/main/res/drawable/ic_marker_location_blue_grey_24dp.xml deleted file mode 100644 index 108bc6b..0000000 --- a/app/src/main/res/drawable/ic_marker_location_blue_grey_24dp.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_marker_location_red_24dp.xml b/app/src/main/res/drawable/ic_marker_location_red_24dp.xml index e11c57b..9080ed6 100755 --- a/app/src/main/res/drawable/ic_marker_location_red_24dp.xml +++ b/app/src/main/res/drawable/ic_marker_location_red_24dp.xml @@ -4,7 +4,7 @@ android:viewportWidth="96.0" android:width="24dp">