Adjust codestyle to an .editorconfig
parent
7f3aa5e9e1
commit
4dcea97982
|
@ -0,0 +1,9 @@
|
||||||
|
# editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
|
@ -1,10 +1,11 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
package="org.y20k.trackbook">
|
||||||
package="org.y20k.trackbook">
|
|
||||||
|
|
||||||
<!-- USE GPS AND NETWORK - EXCLUDE NON-GPS DEVICES -->
|
<!-- USE GPS AND NETWORK - EXCLUDE NON-GPS DEVICES -->
|
||||||
<uses-feature android:name="android.hardware.location.gps" android:required="true" />
|
<uses-feature
|
||||||
|
android:name="android.hardware.location.gps"
|
||||||
|
android:required="true" />
|
||||||
<uses-feature android:name="android.hardware.location.network" />
|
<uses-feature android:name="android.hardware.location.network" />
|
||||||
|
|
||||||
<!-- NORMAL PERMISSIONS, automatically granted -->
|
<!-- NORMAL PERMISSIONS, automatically granted -->
|
||||||
|
@ -19,26 +20,26 @@
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".Trackbook"
|
android:name=".Trackbook"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
|
|
||||||
<!-- MAIN ACTIVITY -->
|
<!-- MAIN ACTIVITY -->
|
||||||
<activity android:name=".MainActivity">
|
<activity android:name=".MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- TRACKER SERVICE -->
|
<!-- TRACKER SERVICE -->
|
||||||
<service
|
<service
|
||||||
android:name=".TrackerService"
|
android:name=".TrackerService"
|
||||||
android:foregroundServiceType="location"
|
android:exported="false"
|
||||||
android:exported="false">
|
android:foregroundServiceType="location">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.y20k.trackbook.action.START" />
|
<action android:name="org.y20k.trackbook.action.START" />
|
||||||
<action android:name="org.y20k.trackbook.action.STOP" />
|
<action android:name="org.y20k.trackbook.action.STOP" />
|
||||||
|
@ -49,8 +50,8 @@
|
||||||
<!-- TRACKING TOGGLE SERVICE SYSTEM QUICK SETTINGS -->
|
<!-- TRACKING TOGGLE SERVICE SYSTEM QUICK SETTINGS -->
|
||||||
<service
|
<service
|
||||||
android:name=".TrackingToggleTileService"
|
android:name=".TrackingToggleTileService"
|
||||||
android:label="@string/quick_settings_tile_title_default"
|
|
||||||
android:icon="@drawable/ic_notification_icon_small_24dp"
|
android:icon="@drawable/ic_notification_icon_small_24dp"
|
||||||
|
android:label="@string/quick_settings_tile_title_default"
|
||||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||||
|
@ -59,13 +60,13 @@
|
||||||
|
|
||||||
<!-- FILE PROVIDER GPX -->
|
<!-- FILE PROVIDER GPX -->
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/provider_paths"/>
|
android:resource="@xml/provider_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
|
@ -44,8 +44,9 @@ object Keys {
|
||||||
const val ARG_TRACK_ID: String = "ArgTrackId"
|
const val ARG_TRACK_ID: String = "ArgTrackId"
|
||||||
|
|
||||||
// preferences
|
// preferences
|
||||||
const val PREF_ONE_TIME_HOUSEKEEPING_NECESSARY = "ONE_TIME_HOUSEKEEPING_NECESSARY_VERSIONCODE_38" // increment to current app version code to trigger housekeeping that runs only once
|
const val PREF_ONE_TIME_HOUSEKEEPING_NECESSARY =
|
||||||
const val PREF_THEME_SELECTION: String= "prefThemeSelection"
|
"ONE_TIME_HOUSEKEEPING_NECESSARY_VERSIONCODE_38" // increment to current app version code to trigger housekeeping that runs only once
|
||||||
|
const val PREF_THEME_SELECTION: String = "prefThemeSelection"
|
||||||
const val PREF_CURRENT_BEST_LOCATION_PROVIDER: String = "prefCurrentBestLocationProvider"
|
const val PREF_CURRENT_BEST_LOCATION_PROVIDER: String = "prefCurrentBestLocationProvider"
|
||||||
const val PREF_CURRENT_BEST_LOCATION_LATITUDE: String = "prefCurrentBestLocationLatitude"
|
const val PREF_CURRENT_BEST_LOCATION_LATITUDE: String = "prefCurrentBestLocationLatitude"
|
||||||
const val PREF_CURRENT_BEST_LOCATION_LONGITUDE: String = "prefCurrentBestLocationLongitude"
|
const val PREF_CURRENT_BEST_LOCATION_LONGITUDE: String = "prefCurrentBestLocationLongitude"
|
||||||
|
@ -77,7 +78,7 @@ object Keys {
|
||||||
const val DIALOG_EMPTY_PAYLOAD_INT: Int = -1
|
const val DIALOG_EMPTY_PAYLOAD_INT: Int = -1
|
||||||
|
|
||||||
// folder names
|
// folder names
|
||||||
const val FOLDER_TEMP: String = "temp"
|
const val FOLDER_TEMP: String = "temp"
|
||||||
const val FOLDER_TRACKS: String = "tracks"
|
const val FOLDER_TRACKS: String = "tracks"
|
||||||
const val FOLDER_GPX: String = "gpx"
|
const val FOLDER_GPX: String = "gpx"
|
||||||
|
|
||||||
|
@ -95,21 +96,29 @@ object Keys {
|
||||||
const val DEFAULT_RFC2822_DATE: String = "Thu, 01 Jan 1970 01:00:00 +0100" // --> Date(0)
|
const val DEFAULT_RFC2822_DATE: String = "Thu, 01 Jan 1970 01:00:00 +0100" // --> Date(0)
|
||||||
const val ONE_HOUR_IN_MILLISECONDS: Int = 3600000
|
const val ONE_HOUR_IN_MILLISECONDS: Int = 3600000
|
||||||
const val EMPTY_STRING_RESOURCE: Int = 0
|
const val EMPTY_STRING_RESOURCE: Int = 0
|
||||||
const val REQUEST_CURRENT_LOCATION_INTERVAL: Long = 1000L // 1 second in milliseconds
|
const val REQUEST_CURRENT_LOCATION_INTERVAL: Long =
|
||||||
const val ADD_WAYPOINT_TO_TRACK_INTERVAL: Long = 15000L // 15 seconds in milliseconds
|
1000L // 1 second in milliseconds
|
||||||
const val SIGNIFICANT_TIME_DIFFERENCE: Long = 120000L // 2 minutes in milliseconds
|
const val ADD_WAYPOINT_TO_TRACK_INTERVAL: Long =
|
||||||
const val STOP_OVER_THRESHOLD: Long = 300000L // 5 minutes in milliseconds
|
15000L // 15 seconds in milliseconds
|
||||||
|
const val SIGNIFICANT_TIME_DIFFERENCE: Long =
|
||||||
|
120000L // 2 minutes in milliseconds
|
||||||
|
const val STOP_OVER_THRESHOLD: Long =
|
||||||
|
300000L // 5 minutes in milliseconds
|
||||||
const val IMPLAUSIBLE_TRACK_START_SPEED: Double = 250.0 // 250 km/h
|
const val IMPLAUSIBLE_TRACK_START_SPEED: Double = 250.0 // 250 km/h
|
||||||
const val DEFAULT_LATITUDE: Double = 71.172500 // latitude Nordkapp, Norway
|
const val DEFAULT_LATITUDE: Double =
|
||||||
const val DEFAULT_LONGITUDE: Double = 25.784444 // longitude Nordkapp, Norway
|
71.172500 // latitude Nordkapp, Norway
|
||||||
|
const val DEFAULT_LONGITUDE: Double =
|
||||||
|
25.784444 // longitude Nordkapp, Norway
|
||||||
const val DEFAULT_ACCURACY: Float = 300f // in meters
|
const val DEFAULT_ACCURACY: Float = 300f // in meters
|
||||||
const val DEFAULT_ALTITUDE: Double = 0.0
|
const val DEFAULT_ALTITUDE: Double = 0.0
|
||||||
const val DEFAULT_TIME: Long = 0L
|
const val DEFAULT_TIME: Long = 0L
|
||||||
const val DEFAULT_THRESHOLD_LOCATION_ACCURACY: Int = 30 // 30 meters
|
const val DEFAULT_THRESHOLD_LOCATION_ACCURACY: Int = 30 // 30 meters
|
||||||
const val DEFAULT_THRESHOLD_LOCATION_AGE: Long = 60000000000L // one minute in nanoseconds
|
const val DEFAULT_THRESHOLD_LOCATION_AGE: Long =
|
||||||
|
60000000000L // one minute in nanoseconds
|
||||||
const val DEFAULT_THRESHOLD_DISTANCE: Float = 15f // 15 meters
|
const val DEFAULT_THRESHOLD_DISTANCE: Float = 15f // 15 meters
|
||||||
const val DEFAULT_ZOOM_LEVEL: Double = 16.0
|
const val DEFAULT_ZOOM_LEVEL: Double = 16.0
|
||||||
const val ALTITUDE_MEASUREMENT_ERROR_THRESHOLD = 10 // altitude changes of 10 meter or more (per 15 seconds) are being discarded
|
const val ALTITUDE_MEASUREMENT_ERROR_THRESHOLD =
|
||||||
|
10 // altitude changes of 10 meter or more (per 15 seconds) are being discarded
|
||||||
const val REQUEST_CODE_FOREGROUND = 42
|
const val REQUEST_CODE_FOREGROUND = 42
|
||||||
|
|
||||||
// requests
|
// requests
|
||||||
|
|
|
@ -69,7 +69,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
// set up views
|
// set up views
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
navHostFragment = supportFragmentManager.findFragmentById(R.id.main_container) as NavHostFragment
|
navHostFragment =
|
||||||
|
supportFragmentManager.findFragmentById(R.id.main_container) as NavHostFragment
|
||||||
bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_navigation_view)
|
bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_navigation_view)
|
||||||
bottomNavigationView.setupWithNavController(navController = navHostFragment.navController)
|
bottomNavigationView.setupWithNavController(navController = navHostFragment.navController)
|
||||||
|
|
||||||
|
@ -77,10 +78,11 @@ class MainActivity : AppCompatActivity() {
|
||||||
navHostFragment.navController.addOnDestinationChangedListener { _, destination, _ ->
|
navHostFragment.navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||||
when (destination.id) {
|
when (destination.id) {
|
||||||
R.id.fragment_track -> {
|
R.id.fragment_track -> {
|
||||||
runOnUiThread( Runnable() {
|
runOnUiThread(Runnable() {
|
||||||
run(){
|
run() {
|
||||||
// mark menu item "Tracks" as checked
|
// mark menu item "Tracks" as checked
|
||||||
bottomNavigationView.menu.findItem(R.id.tracklist_fragment).setChecked(true)
|
bottomNavigationView.menu.findItem(R.id.tracklist_fragment)
|
||||||
|
.setChecked(true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -91,7 +93,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// register listener for changes in shared preferences
|
// register listener for changes in shared preferences
|
||||||
PreferenceManager.getDefaultSharedPreferences(this as Context).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferenceManager.getDefaultSharedPreferences(this as Context)
|
||||||
|
.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -99,20 +102,22 @@ class MainActivity : AppCompatActivity() {
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
// unregister listener for changes in shared preferences
|
// unregister listener for changes in shared preferences
|
||||||
PreferenceManager.getDefaultSharedPreferences(this as Context).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferenceManager.getDefaultSharedPreferences(this as Context)
|
||||||
|
.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Defines the listener for changes in shared preferences
|
* Defines the listener for changes in shared preferences
|
||||||
*/
|
*/
|
||||||
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
private val sharedPreferenceChangeListener =
|
||||||
when (key) {
|
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||||
Keys.PREF_THEME_SELECTION -> {
|
when (key) {
|
||||||
AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection(this@MainActivity))
|
Keys.PREF_THEME_SELECTION -> {
|
||||||
|
AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection(this@MainActivity))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* End of declaration
|
* End of declaration
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -73,9 +73,20 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlay.Mark
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onStop from Fragment */
|
/* Overrides onStop from Fragment */
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
// initialize layout
|
// initialize layout
|
||||||
layout = MapFragmentLayoutHolder(activity as Context, this as MapOverlay.MarkerListener, inflater, container, currentBestLocation, trackingState)
|
layout = MapFragmentLayoutHolder(
|
||||||
|
activity as Context,
|
||||||
|
this as MapOverlay.MarkerListener,
|
||||||
|
inflater,
|
||||||
|
container,
|
||||||
|
currentBestLocation,
|
||||||
|
trackingState
|
||||||
|
)
|
||||||
|
|
||||||
// set up buttons
|
// set up buttons
|
||||||
layout.currentLocationButton.setOnClickListener {
|
layout.currentLocationButton.setOnClickListener {
|
||||||
|
@ -104,11 +115,22 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlay.Mark
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
// request location permission if denied
|
// request location permission if denied
|
||||||
if (ContextCompat.checkSelfPermission(activity as Context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) {
|
if (ContextCompat.checkSelfPermission(
|
||||||
this.requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), Keys.REQUEST_CODE_FOREGROUND)
|
activity as Context,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_DENIED
|
||||||
|
) {
|
||||||
|
this.requestPermissions(
|
||||||
|
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
||||||
|
Keys.REQUEST_CODE_FOREGROUND
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// bind to TrackerService
|
// bind to TrackerService
|
||||||
activity?.bindService(Intent(activity, TrackerService::class.java), connection, Context.BIND_AUTO_CREATE)
|
activity?.bindService(
|
||||||
|
Intent(activity, TrackerService::class.java),
|
||||||
|
connection,
|
||||||
|
Context.BIND_AUTO_CREATE
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,14 +164,22 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlay.Mark
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onRequestPermissionsResult from Fragment */
|
/* Overrides onRequestPermissionsResult from Fragment */
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
Keys.REQUEST_CODE_FOREGROUND -> {
|
Keys.REQUEST_CODE_FOREGROUND -> {
|
||||||
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
||||||
// permission was granted - re-bind service
|
// permission was granted - re-bind service
|
||||||
activity?.unbindService(connection)
|
activity?.unbindService(connection)
|
||||||
activity?.bindService(Intent(activity, TrackerService::class.java), connection, Context.BIND_AUTO_CREATE)
|
activity?.bindService(
|
||||||
|
Intent(activity, TrackerService::class.java),
|
||||||
|
connection,
|
||||||
|
Context.BIND_AUTO_CREATE
|
||||||
|
)
|
||||||
LogHelper.i(TAG, "Request result: Location permission has been granted.")
|
LogHelper.i(TAG, "Request result: Location permission has been granted.")
|
||||||
} else {
|
} else {
|
||||||
// permission denied - unbind service
|
// permission denied - unbind service
|
||||||
|
@ -163,7 +193,12 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlay.Mark
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onYesNoDialog from YesNoDialogListener */
|
/* Overrides onYesNoDialog from YesNoDialogListener */
|
||||||
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String) {
|
override fun onYesNoDialog(
|
||||||
|
type: Int,
|
||||||
|
dialogResult: Boolean,
|
||||||
|
payload: Int,
|
||||||
|
payloadString: String
|
||||||
|
) {
|
||||||
super.onYesNoDialog(type, dialogResult, payload, payloadString)
|
super.onYesNoDialog(type, dialogResult, payload, payloadString)
|
||||||
when (type) {
|
when (type) {
|
||||||
Keys.DIALOG_EMPTY_RECORDING -> {
|
Keys.DIALOG_EMPTY_RECORDING -> {
|
||||||
|
@ -218,11 +253,18 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlay.Mark
|
||||||
/* Saves track - shows dialog, if recording is still empty */
|
/* Saves track - shows dialog, if recording is still empty */
|
||||||
private fun saveTrack() {
|
private fun saveTrack() {
|
||||||
if (track.wayPoints.isEmpty()) {
|
if (track.wayPoints.isEmpty()) {
|
||||||
YesNoDialog(this as YesNoDialog.YesNoDialogListener).show(activity as Context, type = Keys.DIALOG_EMPTY_RECORDING, title = R.string.dialog_error_empty_recording_title, message = R.string.dialog_error_empty_recording_message, yesButton = R.string.dialog_error_empty_recording_action_resume)
|
YesNoDialog(this as YesNoDialog.YesNoDialogListener).show(
|
||||||
|
activity as Context,
|
||||||
|
type = Keys.DIALOG_EMPTY_RECORDING,
|
||||||
|
title = R.string.dialog_error_empty_recording_title,
|
||||||
|
message = R.string.dialog_error_empty_recording_message,
|
||||||
|
yesButton = R.string.dialog_error_empty_recording_action_resume
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
// step 1: create and store filenames for json and gpx files
|
// step 1: create and store filenames for json and gpx files
|
||||||
track.trackUriString = FileHelper.getTrackFileUri(activity as Context, track).toString()
|
track.trackUriString =
|
||||||
|
FileHelper.getTrackFileUri(activity as Context, track).toString()
|
||||||
track.gpxUriString = FileHelper.getGpxFileUri(activity as Context, track).toString()
|
track.gpxUriString = FileHelper.getGpxFileUri(activity as Context, track).toString()
|
||||||
// step 2: save track
|
// step 2: save track
|
||||||
FileHelper.saveTrackSuspended(track, saveGpxToo = true)
|
FileHelper.saveTrackSuspended(track, saveGpxToo = true)
|
||||||
|
@ -251,16 +293,17 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlay.Mark
|
||||||
/*
|
/*
|
||||||
* Defines the listener for changes in shared preferences
|
* Defines the listener for changes in shared preferences
|
||||||
*/
|
*/
|
||||||
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
private val sharedPreferenceChangeListener =
|
||||||
when (key) {
|
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||||
Keys.PREF_TRACKING_STATE -> {
|
when (key) {
|
||||||
if (activity != null) {
|
Keys.PREF_TRACKING_STATE -> {
|
||||||
trackingState = PreferencesHelper.loadTrackingState(activity as Context)
|
if (activity != null) {
|
||||||
layout.updateRecordingButton(trackingState)
|
trackingState = PreferencesHelper.loadTrackingState(activity as Context)
|
||||||
|
layout.updateRecordingButton(trackingState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* End of declaration
|
* End of declaration
|
||||||
*/
|
*/
|
||||||
|
@ -279,15 +322,18 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlay.Mark
|
||||||
trackingState = trackerService.trackingState
|
trackingState = trackerService.trackingState
|
||||||
layout.updateRecordingButton(trackingState)
|
layout.updateRecordingButton(trackingState)
|
||||||
// register listener for changes in shared preferences
|
// register listener for changes in shared preferences
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity as Context).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferenceManager.getDefaultSharedPreferences(activity as Context)
|
||||||
|
.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
// start listening for location updates
|
// start listening for location updates
|
||||||
handler.removeCallbacks(periodicLocationRequestRunnable)
|
handler.removeCallbacks(periodicLocationRequestRunnable)
|
||||||
handler.postDelayed(periodicLocationRequestRunnable, 0)
|
handler.postDelayed(periodicLocationRequestRunnable, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceDisconnected(arg0: ComponentName) {
|
override fun onServiceDisconnected(arg0: ComponentName) {
|
||||||
bound = false
|
bound = false
|
||||||
// unregister listener for changes in shared preferences
|
// unregister listener for changes in shared preferences
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity as Context).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferenceManager.getDefaultSharedPreferences(activity as Context)
|
||||||
|
.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
// stop receiving location updates
|
// stop receiving location updates
|
||||||
handler.removeCallbacks(periodicLocationRequestRunnable)
|
handler.removeCallbacks(periodicLocationRequestRunnable)
|
||||||
}
|
}
|
||||||
|
@ -312,7 +358,9 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlay.Mark
|
||||||
layout.markCurrentPosition(currentBestLocation, trackingState)
|
layout.markCurrentPosition(currentBestLocation, trackingState)
|
||||||
layout.overlayCurrentTrack(track, trackingState)
|
layout.overlayCurrentTrack(track, trackingState)
|
||||||
// center map, if it had not been dragged/zoomed before
|
// center map, if it had not been dragged/zoomed before
|
||||||
if (!layout.userInteraction) { layout.centerMap(currentBestLocation, true)}
|
if (!layout.userInteraction) {
|
||||||
|
layout.centerMap(currentBestLocation, true)
|
||||||
|
}
|
||||||
// show error snackbar if necessary
|
// show error snackbar if necessary
|
||||||
layout.toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
layout.toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
||||||
// use the handler to start runnable again after specified delay
|
// use the handler to start runnable again after specified delay
|
||||||
|
|
|
@ -72,12 +72,16 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
preferenceGpsOnly.setDefaultValue(false)
|
preferenceGpsOnly.setDefaultValue(false)
|
||||||
|
|
||||||
// set up "Use Imperial Measurements" preference
|
// set up "Use Imperial Measurements" preference
|
||||||
val preferenceImperialMeasurementUnits: SwitchPreferenceCompat = SwitchPreferenceCompat(activity as Context)
|
val preferenceImperialMeasurementUnits: SwitchPreferenceCompat =
|
||||||
preferenceImperialMeasurementUnits.title = getString(R.string.pref_imperial_measurement_units_title)
|
SwitchPreferenceCompat(activity as Context)
|
||||||
|
preferenceImperialMeasurementUnits.title =
|
||||||
|
getString(R.string.pref_imperial_measurement_units_title)
|
||||||
preferenceImperialMeasurementUnits.setIcon(R.drawable.ic_square_foot_24px)
|
preferenceImperialMeasurementUnits.setIcon(R.drawable.ic_square_foot_24px)
|
||||||
preferenceImperialMeasurementUnits.key = Keys.PREF_USE_IMPERIAL_UNITS
|
preferenceImperialMeasurementUnits.key = Keys.PREF_USE_IMPERIAL_UNITS
|
||||||
preferenceImperialMeasurementUnits.summaryOn = getString(R.string.pref_imperial_measurement_units_summary_imperial)
|
preferenceImperialMeasurementUnits.summaryOn =
|
||||||
preferenceImperialMeasurementUnits.summaryOff = getString(R.string.pref_imperial_measurement_units_summary_metric)
|
getString(R.string.pref_imperial_measurement_units_summary_imperial)
|
||||||
|
preferenceImperialMeasurementUnits.summaryOff =
|
||||||
|
getString(R.string.pref_imperial_measurement_units_summary_metric)
|
||||||
preferenceImperialMeasurementUnits.setDefaultValue(LengthUnitHelper.useImperialUnits())
|
preferenceImperialMeasurementUnits.setDefaultValue(LengthUnitHelper.useImperialUnits())
|
||||||
|
|
||||||
// set up "App Theme" preference
|
// set up "App Theme" preference
|
||||||
|
@ -85,13 +89,27 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
preferenceThemeSelection.title = getString(R.string.pref_theme_selection_title)
|
preferenceThemeSelection.title = getString(R.string.pref_theme_selection_title)
|
||||||
preferenceThemeSelection.setIcon(R.drawable.ic_smartphone_24dp)
|
preferenceThemeSelection.setIcon(R.drawable.ic_smartphone_24dp)
|
||||||
preferenceThemeSelection.key = Keys.PREF_THEME_SELECTION
|
preferenceThemeSelection.key = Keys.PREF_THEME_SELECTION
|
||||||
preferenceThemeSelection.summary = "${getString(R.string.pref_theme_selection_summary)} ${AppThemeHelper.getCurrentTheme(activity as Context)}"
|
preferenceThemeSelection.summary =
|
||||||
preferenceThemeSelection.entries = arrayOf(getString(R.string.pref_theme_selection_mode_device_default), getString(R.string.pref_theme_selection_mode_light), getString(R.string.pref_theme_selection_mode_dark))
|
"${getString(R.string.pref_theme_selection_summary)} ${AppThemeHelper.getCurrentTheme(
|
||||||
preferenceThemeSelection.entryValues = arrayOf(Keys.STATE_THEME_FOLLOW_SYSTEM, Keys.STATE_THEME_LIGHT_MODE, Keys.STATE_THEME_DARK_MODE)
|
activity as Context
|
||||||
|
)}"
|
||||||
|
preferenceThemeSelection.entries = arrayOf(
|
||||||
|
getString(R.string.pref_theme_selection_mode_device_default),
|
||||||
|
getString(R.string.pref_theme_selection_mode_light),
|
||||||
|
getString(R.string.pref_theme_selection_mode_dark)
|
||||||
|
)
|
||||||
|
preferenceThemeSelection.entryValues = arrayOf(
|
||||||
|
Keys.STATE_THEME_FOLLOW_SYSTEM,
|
||||||
|
Keys.STATE_THEME_LIGHT_MODE,
|
||||||
|
Keys.STATE_THEME_DARK_MODE
|
||||||
|
)
|
||||||
preferenceThemeSelection.setOnPreferenceChangeListener { preference, newValue ->
|
preferenceThemeSelection.setOnPreferenceChangeListener { preference, newValue ->
|
||||||
if (preference is ListPreference) {
|
if (preference is ListPreference) {
|
||||||
val index: Int = preference.entryValues.indexOf(newValue)
|
val index: Int = preference.entryValues.indexOf(newValue)
|
||||||
preferenceThemeSelection.summary = "${getString(R.string.pref_theme_selection_summary)} ${preference.entries.get(index)}"
|
preferenceThemeSelection.summary =
|
||||||
|
"${getString(R.string.pref_theme_selection_summary)} ${preference.entries.get(
|
||||||
|
index
|
||||||
|
)}"
|
||||||
return@setOnPreferenceChangeListener true
|
return@setOnPreferenceChangeListener true
|
||||||
} else {
|
} else {
|
||||||
return@setOnPreferenceChangeListener false
|
return@setOnPreferenceChangeListener false
|
||||||
|
@ -103,8 +121,13 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
preferenceDeleteNonStarred.title = getString(R.string.pref_delete_non_starred_title)
|
preferenceDeleteNonStarred.title = getString(R.string.pref_delete_non_starred_title)
|
||||||
preferenceDeleteNonStarred.setIcon(R.drawable.ic_delete_24dp)
|
preferenceDeleteNonStarred.setIcon(R.drawable.ic_delete_24dp)
|
||||||
preferenceDeleteNonStarred.summary = getString(R.string.pref_delete_non_starred_summary)
|
preferenceDeleteNonStarred.summary = getString(R.string.pref_delete_non_starred_summary)
|
||||||
preferenceDeleteNonStarred.setOnPreferenceClickListener{
|
preferenceDeleteNonStarred.setOnPreferenceClickListener {
|
||||||
YesNoDialog(this as YesNoDialog.YesNoDialogListener).show(context = activity as Context, type = Keys.DIALOG_DELETE_NON_STARRED, message = R.string.dialog_yes_no_message_delete_non_starred, yesButton = R.string.dialog_yes_no_positive_button_delete_non_starred)
|
YesNoDialog(this as YesNoDialog.YesNoDialogListener).show(
|
||||||
|
context = activity as Context,
|
||||||
|
type = Keys.DIALOG_DELETE_NON_STARRED,
|
||||||
|
message = R.string.dialog_yes_no_message_delete_non_starred,
|
||||||
|
yesButton = R.string.dialog_yes_no_positive_button_delete_non_starred
|
||||||
|
)
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +146,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
preferenceResetAdvanced.title = getString(R.string.pref_reset_advanced_title)
|
preferenceResetAdvanced.title = getString(R.string.pref_reset_advanced_title)
|
||||||
preferenceResetAdvanced.setIcon(R.drawable.ic_undo_24dp)
|
preferenceResetAdvanced.setIcon(R.drawable.ic_undo_24dp)
|
||||||
preferenceResetAdvanced.summary = getString(R.string.pref_reset_advanced_summary)
|
preferenceResetAdvanced.summary = getString(R.string.pref_reset_advanced_summary)
|
||||||
preferenceResetAdvanced.setOnPreferenceClickListener{
|
preferenceResetAdvanced.setOnPreferenceClickListener {
|
||||||
preferenceAccuracyThreshold.value = Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY
|
preferenceAccuracyThreshold.value = Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
@ -132,14 +155,21 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
val preferenceAppVersion: Preference = Preference(context)
|
val preferenceAppVersion: Preference = Preference(context)
|
||||||
preferenceAppVersion.title = getString(R.string.pref_app_version_title)
|
preferenceAppVersion.title = getString(R.string.pref_app_version_title)
|
||||||
preferenceAppVersion.setIcon(R.drawable.ic_info_24dp)
|
preferenceAppVersion.setIcon(R.drawable.ic_info_24dp)
|
||||||
preferenceAppVersion.summary = "${getString(R.string.pref_app_version_summary)} ${BuildConfig.VERSION_NAME} (${getString(
|
preferenceAppVersion.summary =
|
||||||
R.string.app_version_name)})"
|
"${getString(R.string.pref_app_version_summary)} ${BuildConfig.VERSION_NAME} (${getString(
|
||||||
|
R.string.app_version_name
|
||||||
|
)})"
|
||||||
preferenceAppVersion.setOnPreferenceClickListener {
|
preferenceAppVersion.setOnPreferenceClickListener {
|
||||||
// copy to clipboard
|
// copy to clipboard
|
||||||
val clip: ClipData = ClipData.newPlainText("simple text", preferenceAppVersion.summary)
|
val clip: ClipData = ClipData.newPlainText("simple text", preferenceAppVersion.summary)
|
||||||
val cm: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val cm: ClipboardManager =
|
||||||
|
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
cm.setPrimaryClip(clip)
|
cm.setPrimaryClip(clip)
|
||||||
Toast.makeText(activity as Context, R.string.toast_message_copied_to_clipboard, Toast.LENGTH_LONG).show()
|
Toast.makeText(
|
||||||
|
activity as Context,
|
||||||
|
R.string.toast_message_copied_to_clipboard,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +193,8 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
preferenceCategoryGeneral.title = getString(R.string.pref_general_title)
|
preferenceCategoryGeneral.title = getString(R.string.pref_general_title)
|
||||||
preferenceCategoryGeneral.contains(preferenceImperialMeasurementUnits)
|
preferenceCategoryGeneral.contains(preferenceImperialMeasurementUnits)
|
||||||
preferenceCategoryGeneral.contains(preferenceGpsOnly)
|
preferenceCategoryGeneral.contains(preferenceGpsOnly)
|
||||||
val preferenceCategoryMaintenance: PreferenceCategory = PreferenceCategory(activity as Context)
|
val preferenceCategoryMaintenance: PreferenceCategory =
|
||||||
|
PreferenceCategory(activity as Context)
|
||||||
preferenceCategoryMaintenance.title = getString(R.string.pref_maintenance_title)
|
preferenceCategoryMaintenance.title = getString(R.string.pref_maintenance_title)
|
||||||
preferenceCategoryMaintenance.contains(preferenceDeleteNonStarred)
|
preferenceCategoryMaintenance.contains(preferenceDeleteNonStarred)
|
||||||
|
|
||||||
|
@ -195,7 +226,12 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onYesNoDialog from YesNoDialogListener */
|
/* Overrides onYesNoDialog from YesNoDialogListener */
|
||||||
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String) {
|
override fun onYesNoDialog(
|
||||||
|
type: Int,
|
||||||
|
dialogResult: Boolean,
|
||||||
|
payload: Int,
|
||||||
|
payloadString: String
|
||||||
|
) {
|
||||||
when (type) {
|
when (type) {
|
||||||
Keys.DIALOG_DELETE_NON_STARRED -> {
|
Keys.DIALOG_DELETE_NON_STARRED -> {
|
||||||
when (dialogResult) {
|
when (dialogResult) {
|
||||||
|
@ -218,7 +254,8 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
val uiScope = CoroutineScope(Dispatchers.Main + backgroundJob)
|
val uiScope = CoroutineScope(Dispatchers.Main + backgroundJob)
|
||||||
uiScope.launch {
|
uiScope.launch {
|
||||||
var tracklist: Tracklist = FileHelper.readTracklist(context)
|
var tracklist: Tracklist = FileHelper.readTracklist(context)
|
||||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteNonStarredSuspended(context, tracklist) }
|
val deferred: Deferred<Tracklist> =
|
||||||
|
async { FileHelper.deleteNonStarredSuspended(context, tracklist) }
|
||||||
// wait for result and store in tracklist
|
// wait for result and store in tracklist
|
||||||
tracklist = deferred.await()
|
tracklist = deferred.await()
|
||||||
backgroundJob.cancel()
|
backgroundJob.cancel()
|
||||||
|
|
|
@ -46,7 +46,8 @@ import org.y20k.trackbook.helpers.TrackHelper
|
||||||
import org.y20k.trackbook.ui.TrackFragmentLayoutHolder
|
import org.y20k.trackbook.ui.TrackFragmentLayoutHolder
|
||||||
|
|
||||||
|
|
||||||
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDialog.YesNoDialogListener, MapOverlay.MarkerListener {
|
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener,
|
||||||
|
YesNoDialog.YesNoDialogListener, MapOverlay.MarkerListener {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TrackFragment::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TrackFragment::class.java)
|
||||||
|
@ -61,7 +62,8 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
// get track
|
// get track
|
||||||
val fileUriString: String = arguments?.getString(Keys.ARG_TRACK_FILE_URI, String()) ?: String()
|
val fileUriString: String =
|
||||||
|
arguments?.getString(Keys.ARG_TRACK_FILE_URI, String()) ?: String()
|
||||||
if (fileUriString.isNotBlank()) {
|
if (fileUriString.isNotBlank()) {
|
||||||
track = FileHelper.readTrack(activity as Context, Uri.parse(fileUriString))
|
track = FileHelper.readTrack(activity as Context, Uri.parse(fileUriString))
|
||||||
} else {
|
} else {
|
||||||
|
@ -71,9 +73,19 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onCreateView from Fragment */
|
/* Overrides onCreateView from Fragment */
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
// initialize layout
|
// initialize layout
|
||||||
layout = TrackFragmentLayoutHolder(activity as Context, this as MapOverlay.MarkerListener, inflater, container, track)
|
layout = TrackFragmentLayoutHolder(
|
||||||
|
activity as Context,
|
||||||
|
this as MapOverlay.MarkerListener,
|
||||||
|
inflater,
|
||||||
|
container,
|
||||||
|
track
|
||||||
|
)
|
||||||
|
|
||||||
// set up share button
|
// set up share button
|
||||||
layout.shareButton.setOnClickListener {
|
layout.shareButton.setOnClickListener {
|
||||||
|
@ -87,12 +99,21 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
}
|
}
|
||||||
// set up delete button
|
// set up delete button
|
||||||
layout.deleteButton.setOnClickListener {
|
layout.deleteButton.setOnClickListener {
|
||||||
val dialogMessage: String = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${layout.trackNameView.text}"
|
val dialogMessage: String =
|
||||||
YesNoDialog(this@TrackFragment as YesNoDialog.YesNoDialogListener).show(context = activity as Context, type = Keys.DIALOG_DELETE_TRACK, messageString = dialogMessage, yesButton = R.string.dialog_yes_no_positive_button_delete_recording)
|
"${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${layout.trackNameView.text}"
|
||||||
|
YesNoDialog(this@TrackFragment as YesNoDialog.YesNoDialogListener).show(
|
||||||
|
context = activity as Context,
|
||||||
|
type = Keys.DIALOG_DELETE_TRACK,
|
||||||
|
messageString = dialogMessage,
|
||||||
|
yesButton = R.string.dialog_yes_no_positive_button_delete_recording
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// set up rename button
|
// set up rename button
|
||||||
layout.editButton.setOnClickListener {
|
layout.editButton.setOnClickListener {
|
||||||
RenameTrackDialog(this as RenameTrackDialog.RenameTrackListener).show(activity as Context, layout.trackNameView.text.toString())
|
RenameTrackDialog(this as RenameTrackDialog.RenameTrackListener).show(
|
||||||
|
activity as Context,
|
||||||
|
layout.trackNameView.text.toString()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return layout.rootView
|
return layout.rootView
|
||||||
|
@ -125,8 +146,18 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
val targetUri: Uri? = data.data
|
val targetUri: Uri? = data.data
|
||||||
if (targetUri != null) {
|
if (targetUri != null) {
|
||||||
// copy file async (= fire & forget - no return value needed)
|
// copy file async (= fire & forget - no return value needed)
|
||||||
GlobalScope.launch { FileHelper.saveCopyOfFileSuspended( activity as Context, originalFileUri = sourceUri, targetFileUri = targetUri) }
|
GlobalScope.launch {
|
||||||
Toast.makeText(activity as Context, R.string.toast_message_save_gpx, Toast.LENGTH_LONG).show()
|
FileHelper.saveCopyOfFileSuspended(
|
||||||
|
activity as Context,
|
||||||
|
originalFileUri = sourceUri,
|
||||||
|
targetFileUri = targetUri
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Toast.makeText(
|
||||||
|
activity as Context,
|
||||||
|
R.string.toast_message_save_gpx,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,7 +170,13 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
/* Overrides onRenameTrackDialog from RenameTrackDialog */
|
/* Overrides onRenameTrackDialog from RenameTrackDialog */
|
||||||
override fun onRenameTrackDialog(textInput: String) {
|
override fun onRenameTrackDialog(textInput: String) {
|
||||||
// rename track async (= fire & forget - no return value needed)
|
// rename track async (= fire & forget - no return value needed)
|
||||||
GlobalScope.launch { FileHelper.renameTrackSuspended(activity as Context, layout.track, textInput) }
|
GlobalScope.launch {
|
||||||
|
FileHelper.renameTrackSuspended(
|
||||||
|
activity as Context,
|
||||||
|
layout.track,
|
||||||
|
textInput
|
||||||
|
)
|
||||||
|
}
|
||||||
// update name in layout
|
// update name in layout
|
||||||
layout.track.name = textInput
|
layout.track.name = textInput
|
||||||
layout.trackNameView.text = textInput
|
layout.trackNameView.text = textInput
|
||||||
|
@ -147,7 +184,12 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onYesNoDialog from YesNoDialogListener */
|
/* Overrides onYesNoDialog from YesNoDialogListener */
|
||||||
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String) {
|
override fun onYesNoDialog(
|
||||||
|
type: Int,
|
||||||
|
dialogResult: Boolean,
|
||||||
|
payload: Int,
|
||||||
|
payloadString: String
|
||||||
|
) {
|
||||||
when (type) {
|
when (type) {
|
||||||
Keys.DIALOG_DELETE_TRACK -> {
|
Keys.DIALOG_DELETE_TRACK -> {
|
||||||
when (dialogResult) {
|
when (dialogResult) {
|
||||||
|
@ -189,7 +231,11 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
if (packageManager != null && intent.resolveActivity(packageManager) != null) {
|
if (packageManager != null && intent.resolveActivity(packageManager) != null) {
|
||||||
startActivityForResult(intent, Keys.REQUEST_SAVE_GPX)
|
startActivityForResult(intent, Keys.REQUEST_SAVE_GPX)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(activity as Context, R.string.toast_message_install_file_helper, Toast.LENGTH_LONG).show()
|
Toast.makeText(
|
||||||
|
activity as Context,
|
||||||
|
R.string.toast_message_install_file_helper,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +243,11 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
/* Share track as GPX via share sheet */
|
/* Share track as GPX via share sheet */
|
||||||
private fun shareGpxTrack() {
|
private fun shareGpxTrack() {
|
||||||
val gpxFile = Uri.parse(layout.track.gpxUriString).toFile()
|
val gpxFile = Uri.parse(layout.track.gpxUriString).toFile()
|
||||||
val gpxShareUri = FileProvider.getUriForFile(this.activity as Context, "${requireActivity().applicationContext.packageName}.provider", gpxFile)
|
val gpxShareUri = FileProvider.getUriForFile(
|
||||||
|
this.activity as Context,
|
||||||
|
"${requireActivity().applicationContext.packageName}.provider",
|
||||||
|
gpxFile
|
||||||
|
)
|
||||||
val shareIntent: Intent = Intent.createChooser(Intent().apply {
|
val shareIntent: Intent = Intent.createChooser(Intent().apply {
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
data = gpxShareUri
|
data = gpxShareUri
|
||||||
|
@ -211,7 +261,8 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
|
||||||
if (packageManager != null && shareIntent.resolveActivity(packageManager) != null) {
|
if (packageManager != null && shareIntent.resolveActivity(packageManager) != null) {
|
||||||
startActivity(shareIntent)
|
startActivity(shareIntent)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(activity, R.string.toast_message_install_file_helper, Toast.LENGTH_LONG).show()
|
Toast.makeText(activity, R.string.toast_message_install_file_helper, Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ import org.y20k.trackbook.helpers.PreferencesHelper
|
||||||
/*
|
/*
|
||||||
* Trackbook.class
|
* Trackbook.class
|
||||||
*/
|
*/
|
||||||
class Trackbook: Application() {
|
class Trackbook : Application() {
|
||||||
|
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
|
|
|
@ -48,7 +48,7 @@ import kotlin.coroutines.CoroutineContext
|
||||||
/*
|
/*
|
||||||
* TrackerService class
|
* TrackerService class
|
||||||
*/
|
*/
|
||||||
class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
class TrackerService() : Service(), CoroutineScope, SensorEventListener {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TrackerService::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TrackerService::class.java)
|
||||||
|
@ -101,7 +101,8 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
currentBestLocation = LocationHelper.getLastKnownLocation(this)
|
currentBestLocation = LocationHelper.getLastKnownLocation(this)
|
||||||
track = FileHelper.readTrack(this, FileHelper.getTempFileUri(this))
|
track = FileHelper.readTrack(this, FileHelper.getTempFileUri(this))
|
||||||
backgroundJob = Job()
|
backgroundJob = Job()
|
||||||
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,16 +112,19 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
// SERVICE RESTART (via START_STICKY)
|
// SERVICE RESTART (via START_STICKY)
|
||||||
if (intent == null) {
|
if (intent == null) {
|
||||||
if (trackingState == Keys.STATE_TRACKING_ACTIVE) {
|
if (trackingState == Keys.STATE_TRACKING_ACTIVE) {
|
||||||
LogHelper.w(TAG, "Trackbook has been killed by the operating system. Trying to resume recording.")
|
LogHelper.w(
|
||||||
|
TAG,
|
||||||
|
"Trackbook has been killed by the operating system. Trying to resume recording."
|
||||||
|
)
|
||||||
resumeTracking()
|
resumeTracking()
|
||||||
}
|
}
|
||||||
// ACTION STOP
|
// ACTION STOP
|
||||||
} else if (Keys.ACTION_STOP == intent.action) {
|
} else if (Keys.ACTION_STOP == intent.action) {
|
||||||
stopTracking()
|
stopTracking()
|
||||||
// ACTION START
|
// ACTION START
|
||||||
} else if (Keys.ACTION_START == intent.action) {
|
} else if (Keys.ACTION_START == intent.action) {
|
||||||
startTracking()
|
startTracking()
|
||||||
// ACTION RESUME
|
// ACTION RESUME
|
||||||
} else if (Keys.ACTION_RESUME == intent.action) {
|
} else if (Keys.ACTION_RESUME == intent.action) {
|
||||||
resumeTracking()
|
resumeTracking()
|
||||||
}
|
}
|
||||||
|
@ -172,7 +176,8 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
// remove notification
|
// remove notification
|
||||||
stopForeground(true)
|
stopForeground(true)
|
||||||
// stop listening for changes in shared preferences
|
// stop listening for changes in shared preferences
|
||||||
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
// stop receiving location updates
|
// stop receiving location updates
|
||||||
removeGpsLocationListener()
|
removeGpsLocationListener()
|
||||||
removeNetworkLocationListener()
|
removeNetworkLocationListener()
|
||||||
|
@ -193,7 +198,8 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
if (sensorEvent != null) {
|
if (sensorEvent != null) {
|
||||||
if (stepCountOffset == 0f) {
|
if (stepCountOffset == 0f) {
|
||||||
// store steps previously recorded by the system
|
// store steps previously recorded by the system
|
||||||
stepCountOffset = (sensorEvent.values[0] - 1) - track.stepCount // subtract any steps recorded during this session in case the app was killed
|
stepCountOffset =
|
||||||
|
(sensorEvent.values[0] - 1) - track.stepCount // subtract any steps recorded during this session in case the app was killed
|
||||||
}
|
}
|
||||||
// calculate step count - subtract steps previously recorded
|
// calculate step count - subtract steps previously recorded
|
||||||
steps = sensorEvent.values[0] - stepCountOffset
|
steps = sensorEvent.values[0] - stepCountOffset
|
||||||
|
@ -215,7 +221,8 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
// set resumed flag
|
// set resumed flag
|
||||||
resumed = true
|
resumed = true
|
||||||
// calculate length of recording break
|
// calculate length of recording break
|
||||||
track.recordingPaused = track.recordingPaused + TrackHelper.calculateDurationOfPause(track.recordingStop)
|
track.recordingPaused =
|
||||||
|
track.recordingPaused + TrackHelper.calculateDurationOfPause(track.recordingStop)
|
||||||
// start tracking
|
// start tracking
|
||||||
startTracking(newTrack = false)
|
startTracking(newTrack = false)
|
||||||
}
|
}
|
||||||
|
@ -301,20 +308,27 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
currentBestLocation = location
|
currentBestLocation = location
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onProviderEnabled(provider: String) {
|
override fun onProviderEnabled(provider: String) {
|
||||||
LogHelper.v(TAG, "onProviderEnabled $provider")
|
LogHelper.v(TAG, "onProviderEnabled $provider")
|
||||||
when (provider) {
|
when (provider) {
|
||||||
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
|
LocationManager.GPS_PROVIDER -> gpsProviderActive =
|
||||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
LocationHelper.isGpsEnabled(locationManager)
|
||||||
|
LocationManager.NETWORK_PROVIDER -> networkProviderActive =
|
||||||
|
LocationHelper.isNetworkEnabled(locationManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onProviderDisabled(provider: String) {
|
override fun onProviderDisabled(provider: String) {
|
||||||
LogHelper.v(TAG, "onProviderDisabled $provider")
|
LogHelper.v(TAG, "onProviderDisabled $provider")
|
||||||
when (provider) {
|
when (provider) {
|
||||||
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
|
LocationManager.GPS_PROVIDER -> gpsProviderActive =
|
||||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
LocationHelper.isGpsEnabled(locationManager)
|
||||||
|
LocationManager.NETWORK_PROVIDER -> networkProviderActive =
|
||||||
|
LocationHelper.isNetworkEnabled(locationManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
|
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
|
||||||
// deprecated method
|
// deprecated method
|
||||||
}
|
}
|
||||||
|
@ -329,13 +343,25 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
// check if Network provider is available
|
// check if Network provider is available
|
||||||
if (gpsProviderActive) {
|
if (gpsProviderActive) {
|
||||||
// check for location permission
|
// check for location permission
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
this,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
// adds GPS location listener
|
// adds GPS location listener
|
||||||
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f,gpsLocationListener)
|
locationManager.requestLocationUpdates(
|
||||||
|
LocationManager.GPS_PROVIDER,
|
||||||
|
0,
|
||||||
|
0f,
|
||||||
|
gpsLocationListener
|
||||||
|
)
|
||||||
gpsLocationListenerRegistered = true
|
gpsLocationListenerRegistered = true
|
||||||
LogHelper.v(TAG, "Added GPS location listener.")
|
LogHelper.v(TAG, "Added GPS location listener.")
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to add GPS location listener. Location permission is not granted.")
|
LogHelper.w(
|
||||||
|
TAG,
|
||||||
|
"Unable to add GPS location listener. Location permission is not granted."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to add GPS location listener.")
|
LogHelper.w(TAG, "Unable to add GPS location listener.")
|
||||||
|
@ -353,50 +379,83 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
// check if Network provider is available
|
// check if Network provider is available
|
||||||
if (networkProviderActive && !gpsOnly) {
|
if (networkProviderActive && !gpsOnly) {
|
||||||
// check for location permission
|
// check for location permission
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
this,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
// adds Network location listener
|
// adds Network location listener
|
||||||
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0f, networkLocationListener)
|
locationManager.requestLocationUpdates(
|
||||||
|
LocationManager.NETWORK_PROVIDER,
|
||||||
|
0,
|
||||||
|
0f,
|
||||||
|
networkLocationListener
|
||||||
|
)
|
||||||
networkLocationListenerRegistered = true
|
networkLocationListenerRegistered = true
|
||||||
LogHelper.v(TAG, "Added Network location listener.")
|
LogHelper.v(TAG, "Added Network location listener.")
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to add Network location listener. Location permission is not granted.")
|
LogHelper.w(
|
||||||
|
TAG,
|
||||||
|
"Unable to add Network location listener. Location permission is not granted."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to add Network location listener.")
|
LogHelper.w(TAG, "Unable to add Network location listener.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogHelper.v(TAG, "Skipping registration. Network location listener has already been added.")
|
LogHelper.v(
|
||||||
|
TAG,
|
||||||
|
"Skipping registration. Network location listener has already been added."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Adds location listeners to location manager */
|
/* Adds location listeners to location manager */
|
||||||
fun removeGpsLocationListener() {
|
fun removeGpsLocationListener() {
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
this,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
locationManager.removeUpdates(gpsLocationListener)
|
locationManager.removeUpdates(gpsLocationListener)
|
||||||
gpsLocationListenerRegistered = false
|
gpsLocationListenerRegistered = false
|
||||||
LogHelper.v(TAG, "Removed GPS location listener.")
|
LogHelper.v(TAG, "Removed GPS location listener.")
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to remove GPS location listener. Location permission is needed.")
|
LogHelper.w(
|
||||||
|
TAG,
|
||||||
|
"Unable to remove GPS location listener. Location permission is needed."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Adds location listeners to location manager */
|
/* Adds location listeners to location manager */
|
||||||
fun removeNetworkLocationListener() {
|
fun removeNetworkLocationListener() {
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
this,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
locationManager.removeUpdates(gpsLocationListener)
|
locationManager.removeUpdates(gpsLocationListener)
|
||||||
networkLocationListenerRegistered = false
|
networkLocationListenerRegistered = false
|
||||||
LogHelper.v(TAG, "Removed Network location listener.")
|
LogHelper.v(TAG, "Removed Network location listener.")
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to remove Network location listener. Location permission is needed.")
|
LogHelper.w(
|
||||||
|
TAG,
|
||||||
|
"Unable to remove Network location listener. Location permission is needed."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Registers a step counter listener */
|
/* Registers a step counter listener */
|
||||||
private fun startStepCounter() {
|
private fun startStepCounter() {
|
||||||
val stepCounterAvailable = sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER), SensorManager.SENSOR_DELAY_UI)
|
val stepCounterAvailable = sensorManager.registerListener(
|
||||||
|
this,
|
||||||
|
sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER),
|
||||||
|
SensorManager.SENSOR_DELAY_UI
|
||||||
|
)
|
||||||
if (!stepCounterAvailable) {
|
if (!stepCounterAvailable) {
|
||||||
LogHelper.w(TAG, "Pedometer sensor not available.")
|
LogHelper.w(TAG, "Pedometer sensor not available.")
|
||||||
track.stepCount = -1f
|
track.stepCount = -1f
|
||||||
|
@ -406,7 +465,12 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
|
|
||||||
/* Displays / updates notification */
|
/* Displays / updates notification */
|
||||||
private fun displayNotification(): Notification {
|
private fun displayNotification(): Notification {
|
||||||
val notification: Notification = notificationHelper.createNotification(trackingState, track.length, track.duration, useImperial)
|
val notification: Notification = notificationHelper.createNotification(
|
||||||
|
trackingState,
|
||||||
|
track.length,
|
||||||
|
track.duration,
|
||||||
|
useImperial
|
||||||
|
)
|
||||||
notificationManager.notify(Keys.TRACKER_SERVICE_NOTIFICATION_ID, notification)
|
notificationManager.notify(Keys.TRACKER_SERVICE_NOTIFICATION_ID, notification)
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -415,7 +479,8 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
/*
|
/*
|
||||||
* Defines the listener for changes in shared preferences
|
* Defines the listener for changes in shared preferences
|
||||||
*/
|
*/
|
||||||
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
private val sharedPreferenceChangeListener =
|
||||||
|
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||||
when (key) {
|
when (key) {
|
||||||
// preference "Restrict to GPS"
|
// preference "Restrict to GPS"
|
||||||
Keys.PREF_GPS_ONLY -> {
|
Keys.PREF_GPS_ONLY -> {
|
||||||
|
@ -431,7 +496,8 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
}
|
}
|
||||||
// preference "Accuracy Threshold"
|
// preference "Accuracy Threshold"
|
||||||
Keys.PREF_LOCATION_ACCURACY_THRESHOLD -> {
|
Keys.PREF_LOCATION_ACCURACY_THRESHOLD -> {
|
||||||
locationAccuracyThreshold = PreferencesHelper.loadAccuracyThreshold(this@TrackerService)
|
locationAccuracyThreshold =
|
||||||
|
PreferencesHelper.loadAccuracyThreshold(this@TrackerService)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,7 +523,13 @@ class TrackerService(): Service(), CoroutineScope, SensorEventListener {
|
||||||
private val periodicTrackUpdate: Runnable = object : Runnable {
|
private val periodicTrackUpdate: Runnable = object : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
// add waypoint to track - step count is continuously updated in onSensorChanged
|
// add waypoint to track - step count is continuously updated in onSensorChanged
|
||||||
val result: Pair<Track, Boolean> = TrackHelper.addWayPointToTrack(this@TrackerService, track, currentBestLocation, locationAccuracyThreshold, resumed)
|
val result: Pair<Track, Boolean> = TrackHelper.addWayPointToTrack(
|
||||||
|
this@TrackerService,
|
||||||
|
track,
|
||||||
|
currentBestLocation,
|
||||||
|
locationAccuracyThreshold,
|
||||||
|
resumed
|
||||||
|
)
|
||||||
// get track from result
|
// get track from result
|
||||||
track = result.first
|
track = result.first
|
||||||
// check if waypoint was successfully added (= result.second)
|
// check if waypoint was successfully added (= result.second)
|
||||||
|
|
|
@ -31,7 +31,7 @@ import org.y20k.trackbook.helpers.PreferencesHelper
|
||||||
/*
|
/*
|
||||||
* TrackingToggleTileService class
|
* TrackingToggleTileService class
|
||||||
*/
|
*/
|
||||||
class TrackingToggleTileService(): TileService() {
|
class TrackingToggleTileService() : TileService() {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TrackingToggleTileService::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TrackingToggleTileService::class.java)
|
||||||
|
@ -66,7 +66,8 @@ class TrackingToggleTileService(): TileService() {
|
||||||
// set up tile
|
// set up tile
|
||||||
updateTile()
|
updateTile()
|
||||||
// register listener for changes in shared preferences
|
// register listener for changes in shared preferences
|
||||||
PreferenceManager.getDefaultSharedPreferences(this@TrackingToggleTileService).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferenceManager.getDefaultSharedPreferences(this@TrackingToggleTileService)
|
||||||
|
.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,7 +85,8 @@ class TrackingToggleTileService(): TileService() {
|
||||||
override fun onStopListening() {
|
override fun onStopListening() {
|
||||||
super.onStopListening()
|
super.onStopListening()
|
||||||
// unregister listener for changes in shared preferences
|
// unregister listener for changes in shared preferences
|
||||||
PreferenceManager.getDefaultSharedPreferences(this@TrackingToggleTileService).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferenceManager.getDefaultSharedPreferences(this@TrackingToggleTileService)
|
||||||
|
.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,19 +140,18 @@ class TrackingToggleTileService(): TileService() {
|
||||||
/*
|
/*
|
||||||
* Defines the listener for changes in shared preferences
|
* Defines the listener for changes in shared preferences
|
||||||
*/
|
*/
|
||||||
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
private val sharedPreferenceChangeListener =
|
||||||
when (key) {
|
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||||
Keys.PREF_TRACKING_STATE -> {
|
when (key) {
|
||||||
trackingState = PreferencesHelper.loadTrackingState(this)
|
Keys.PREF_TRACKING_STATE -> {
|
||||||
updateTile()
|
trackingState = PreferencesHelper.loadTrackingState(this)
|
||||||
|
updateTile()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* End of declaration
|
* End of declaration
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -40,7 +40,8 @@ import org.y20k.trackbook.tracklist.TracklistAdapter
|
||||||
/*
|
/*
|
||||||
* TracklistFragment class
|
* TracklistFragment class
|
||||||
*/
|
*/
|
||||||
class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener, YesNoDialog.YesNoDialogListener {
|
class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
|
YesNoDialog.YesNoDialogListener {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TracklistFragment::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TracklistFragment::class.java)
|
||||||
|
@ -61,7 +62,11 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onCreateView from Fragment */
|
/* Overrides onCreateView from Fragment */
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
// find views
|
// find views
|
||||||
val rootView = inflater.inflate(R.layout.fragment_tracklist, container, false)
|
val rootView = inflater.inflate(R.layout.fragment_tracklist, container, false)
|
||||||
trackElementList = rootView.findViewById(R.id.track_element_list)
|
trackElementList = rootView.findViewById(R.id.track_element_list)
|
||||||
|
@ -77,8 +82,17 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
// ask user
|
// ask user
|
||||||
val adapterPosition: Int = viewHolder.adapterPosition
|
val adapterPosition: Int = viewHolder.adapterPosition
|
||||||
val dialogMessage: String = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${tracklistAdapter.getTrackName(adapterPosition)}"
|
val dialogMessage: String =
|
||||||
YesNoDialog(this@TracklistFragment as YesNoDialog.YesNoDialogListener).show(context = activity as Context, type = Keys.DIALOG_DELETE_TRACK, messageString = dialogMessage, yesButton = R.string.dialog_yes_no_positive_button_delete_recording, payload = adapterPosition)
|
"${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${tracklistAdapter.getTrackName(
|
||||||
|
adapterPosition
|
||||||
|
)}"
|
||||||
|
YesNoDialog(this@TracklistFragment as YesNoDialog.YesNoDialogListener).show(
|
||||||
|
context = activity as Context,
|
||||||
|
type = Keys.DIALOG_DELETE_TRACK,
|
||||||
|
messageString = dialogMessage,
|
||||||
|
yesButton = R.string.dialog_yes_no_positive_button_delete_recording,
|
||||||
|
payload = adapterPosition
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val itemTouchHelper = ItemTouchHelper(swipeHandler)
|
val itemTouchHelper = ItemTouchHelper(swipeHandler)
|
||||||
|
@ -103,13 +117,18 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onYesNoDialog from YesNoDialogListener */
|
/* Overrides onYesNoDialog from YesNoDialogListener */
|
||||||
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String) {
|
override fun onYesNoDialog(
|
||||||
|
type: Int,
|
||||||
|
dialogResult: Boolean,
|
||||||
|
payload: Int,
|
||||||
|
payloadString: String
|
||||||
|
) {
|
||||||
when (type) {
|
when (type) {
|
||||||
Keys.DIALOG_DELETE_TRACK -> {
|
Keys.DIALOG_DELETE_TRACK -> {
|
||||||
when (dialogResult) {
|
when (dialogResult) {
|
||||||
// user tapped remove track
|
// user tapped remove track
|
||||||
true -> {
|
true -> {
|
||||||
toggleOnboardingLayout(tracklistAdapter.itemCount -1)
|
toggleOnboardingLayout(tracklistAdapter.itemCount - 1)
|
||||||
tracklistAdapter.removeTrack(activity as Context, payload)
|
tracklistAdapter.removeTrack(activity as Context, payload)
|
||||||
}
|
}
|
||||||
// user tapped cancel
|
// user tapped cancel
|
||||||
|
@ -139,11 +158,11 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inner class: custom LinearLayoutManager that overrides onLayoutCompleted
|
* Inner class: custom LinearLayoutManager that overrides onLayoutCompleted
|
||||||
*/
|
*/
|
||||||
inner class CustomLinearLayoutManager(context: Context): LinearLayoutManager(context, VERTICAL, false) {
|
inner class CustomLinearLayoutManager(context: Context) :
|
||||||
|
LinearLayoutManager(context, VERTICAL, false) {
|
||||||
|
|
||||||
override fun supportsPredictiveItemAnimations(): Boolean {
|
override fun supportsPredictiveItemAnimations(): Boolean {
|
||||||
return true
|
return true
|
||||||
|
@ -157,7 +176,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
if (deleteTrackId != -1L) {
|
if (deleteTrackId != -1L) {
|
||||||
val position: Int = tracklistAdapter.findPosition(deleteTrackId)
|
val position: Int = tracklistAdapter.findPosition(deleteTrackId)
|
||||||
tracklistAdapter.removeTrack(this@TracklistFragment.activity as Context, position)
|
tracklistAdapter.removeTrack(this@TracklistFragment.activity as Context, position)
|
||||||
toggleOnboardingLayout(tracklistAdapter.itemCount -1)
|
toggleOnboardingLayout(tracklistAdapter.itemCount - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,24 +32,26 @@ import java.util.*
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Track (@Expose var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMAT_VERSION,
|
data class Track(
|
||||||
@Expose val wayPoints: MutableList<WayPoint> = mutableListOf<WayPoint>(),
|
@Expose var trackFormatVersion: Int = Keys.CURRENT_TRACK_FORMAT_VERSION,
|
||||||
@Expose var length: Float = 0f,
|
@Expose val wayPoints: MutableList<WayPoint> = mutableListOf<WayPoint>(),
|
||||||
@Expose var duration: Long = 0L,
|
@Expose var length: Float = 0f,
|
||||||
@Expose var recordingPaused: Long = 0L,
|
@Expose var duration: Long = 0L,
|
||||||
@Expose var stepCount: Float = 0f,
|
@Expose var recordingPaused: Long = 0L,
|
||||||
@Expose var recordingStart: Date = GregorianCalendar.getInstance().time,
|
@Expose var stepCount: Float = 0f,
|
||||||
@Expose var recordingStop: Date = recordingStart,
|
@Expose var recordingStart: Date = GregorianCalendar.getInstance().time,
|
||||||
@Expose var maxAltitude: Double = 0.0,
|
@Expose var recordingStop: Date = recordingStart,
|
||||||
@Expose var minAltitude: Double = 0.0,
|
@Expose var maxAltitude: Double = 0.0,
|
||||||
@Expose var positiveElevation: Double = 0.0,
|
@Expose var minAltitude: Double = 0.0,
|
||||||
@Expose var negativeElevation: Double = 0.0,
|
@Expose var positiveElevation: Double = 0.0,
|
||||||
@Expose var trackUriString: String = String(),
|
@Expose var negativeElevation: Double = 0.0,
|
||||||
@Expose var gpxUriString: String = String(),
|
@Expose var trackUriString: String = String(),
|
||||||
@Expose var latitude: Double = Keys.DEFAULT_LATITUDE,
|
@Expose var gpxUriString: String = String(),
|
||||||
@Expose var longitude: Double = Keys.DEFAULT_LONGITUDE,
|
@Expose var latitude: Double = Keys.DEFAULT_LATITUDE,
|
||||||
@Expose var zoomLevel: Double = Keys.DEFAULT_ZOOM_LEVEL,
|
@Expose var longitude: Double = Keys.DEFAULT_LONGITUDE,
|
||||||
@Expose var name: String = String()): Parcelable {
|
@Expose var zoomLevel: Double = Keys.DEFAULT_ZOOM_LEVEL,
|
||||||
|
@Expose var name: String = String()
|
||||||
|
) : Parcelable {
|
||||||
|
|
||||||
|
|
||||||
/* Creates a TracklistElement */
|
/* Creates a TracklistElement */
|
||||||
|
|
|
@ -31,9 +31,11 @@ import java.util.*
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Tracklist (@Expose val tracklistFormatVersion: Int = Keys.CURRENT_TRACKLIST_FORMAT_VERSION,
|
data class Tracklist(
|
||||||
@Expose val tracklistElements: MutableList<TracklistElement> = mutableListOf<TracklistElement>(),
|
@Expose val tracklistFormatVersion: Int = Keys.CURRENT_TRACKLIST_FORMAT_VERSION,
|
||||||
@Expose var modificationDate: Date = Date()): Parcelable {
|
@Expose val tracklistElements: MutableList<TracklistElement> = mutableListOf<TracklistElement>(),
|
||||||
|
@Expose var modificationDate: Date = Date()
|
||||||
|
) : Parcelable {
|
||||||
|
|
||||||
/* Return trackelement for given track id */
|
/* Return trackelement for given track id */
|
||||||
fun getTrackElement(trackId: Long): TracklistElement? {
|
fun getTrackElement(trackId: Long): TracklistElement? {
|
||||||
|
@ -47,7 +49,11 @@ data class Tracklist (@Expose val tracklistFormatVersion: Int = Keys.CURRENT_TRA
|
||||||
|
|
||||||
/* Create a deep copy */
|
/* Create a deep copy */
|
||||||
fun deepCopy(): Tracklist {
|
fun deepCopy(): Tracklist {
|
||||||
return Tracklist(tracklistFormatVersion, mutableListOf<TracklistElement>().apply { addAll(tracklistElements) }, modificationDate)
|
return Tracklist(
|
||||||
|
tracklistFormatVersion,
|
||||||
|
mutableListOf<TracklistElement>().apply { addAll(tracklistElements) },
|
||||||
|
modificationDate
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -29,14 +29,16 @@ import java.util.*
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class TracklistElement(@Expose var name: String,
|
data class TracklistElement(
|
||||||
@Expose val date: Date,
|
@Expose var name: String,
|
||||||
@Expose val dateString: String,
|
@Expose val date: Date,
|
||||||
@Expose val durationString: String,
|
@Expose val dateString: String,
|
||||||
@Expose val length: Float,
|
@Expose val durationString: String,
|
||||||
@Expose val trackUriString: String,
|
@Expose val length: Float,
|
||||||
@Expose val gpxUriString: String,
|
@Expose val trackUriString: String,
|
||||||
@Expose var starred: Boolean = false): Parcelable {
|
@Expose val gpxUriString: String,
|
||||||
|
@Expose var starred: Boolean = false
|
||||||
|
) : Parcelable {
|
||||||
|
|
||||||
/* Returns unique ID for TracklistElement - currently the start date */
|
/* Returns unique ID for TracklistElement - currently the start date */
|
||||||
fun getTrackId(): Long {
|
fun getTrackId(): Long {
|
||||||
|
|
|
@ -29,16 +29,18 @@ import kotlinx.android.parcel.Parcelize
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class WayPoint(@Expose val provider: String,
|
data class WayPoint(
|
||||||
@Expose val latitude: Double,
|
@Expose val provider: String,
|
||||||
@Expose val longitude: Double,
|
@Expose val latitude: Double,
|
||||||
@Expose val altitude: Double,
|
@Expose val longitude: Double,
|
||||||
@Expose val accuracy: Float,
|
@Expose val altitude: Double,
|
||||||
@Expose val time: Long,
|
@Expose val accuracy: Float,
|
||||||
@Expose val distanceToStartingPoint: Float = 0f,
|
@Expose val time: Long,
|
||||||
@Expose val numberSatellites: Int = 0,
|
@Expose val distanceToStartingPoint: Float = 0f,
|
||||||
@Expose var isStopOver: Boolean = false,
|
@Expose val numberSatellites: Int = 0,
|
||||||
@Expose var starred: Boolean = false): Parcelable {
|
@Expose var isStopOver: Boolean = false,
|
||||||
|
@Expose var starred: Boolean = false
|
||||||
|
) : Parcelable {
|
||||||
|
|
||||||
|
|
||||||
/* Converts WayPoint into Location */
|
/* Converts WayPoint into Location */
|
||||||
|
|
|
@ -38,9 +38,15 @@ object ErrorDialog {
|
||||||
|
|
||||||
|
|
||||||
/* Construct and show dialog */
|
/* Construct and show dialog */
|
||||||
fun show(context: Context, errorTitle: Int, errorMessage: Int, errorDetails: String = String()) {
|
fun show(
|
||||||
|
context: Context,
|
||||||
|
errorTitle: Int,
|
||||||
|
errorMessage: Int,
|
||||||
|
errorDetails: String = String()
|
||||||
|
) {
|
||||||
// prepare dialog builder
|
// prepare dialog builder
|
||||||
val builder: MaterialAlertDialogBuilder = MaterialAlertDialogBuilder(context, R.style.AlertDialogTheme)
|
val builder: MaterialAlertDialogBuilder =
|
||||||
|
MaterialAlertDialogBuilder(context, R.style.AlertDialogTheme)
|
||||||
|
|
||||||
// set title
|
// set title
|
||||||
builder.setTitle(context.getString(errorTitle))
|
builder.setTitle(context.getString(errorTitle))
|
||||||
|
@ -81,10 +87,12 @@ object ErrorDialog {
|
||||||
errorMessageView.text = context.getString(errorMessage)
|
errorMessageView.text = context.getString(errorMessage)
|
||||||
|
|
||||||
// add okay button
|
// add okay button
|
||||||
builder.setPositiveButton(R.string.dialog_generic_button_okay, DialogInterface.OnClickListener { _, _ ->
|
builder.setPositiveButton(
|
||||||
// listen for click on okay button
|
R.string.dialog_generic_button_okay,
|
||||||
// do nothing
|
DialogInterface.OnClickListener { _, _ ->
|
||||||
})
|
// listen for click on okay button
|
||||||
|
// do nothing
|
||||||
|
})
|
||||||
|
|
||||||
// display error dialog
|
// display error dialog
|
||||||
builder.show()
|
builder.show()
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.y20k.trackbook.helpers.LogHelper
|
||||||
/*
|
/*
|
||||||
* RenameTrackDialog class
|
* RenameTrackDialog class
|
||||||
*/
|
*/
|
||||||
class RenameTrackDialog (private var renameTrackListener: RenameTrackListener) {
|
class RenameTrackDialog(private var renameTrackListener: RenameTrackListener) {
|
||||||
|
|
||||||
/* Interface used to communicate back to activity */
|
/* Interface used to communicate back to activity */
|
||||||
interface RenameTrackListener {
|
interface RenameTrackListener {
|
||||||
|
|
|
@ -24,7 +24,7 @@ import org.y20k.trackbook.helpers.LogHelper
|
||||||
/*
|
/*
|
||||||
* YesNoDialog class
|
* YesNoDialog class
|
||||||
*/
|
*/
|
||||||
class YesNoDialog (private var yesNoDialogListener: YesNoDialogListener) {
|
class YesNoDialog(private var yesNoDialogListener: YesNoDialogListener) {
|
||||||
|
|
||||||
/* Interface used to communicate back to activity */
|
/* Interface used to communicate back to activity */
|
||||||
interface YesNoDialogListener {
|
interface YesNoDialogListener {
|
||||||
|
@ -37,35 +37,46 @@ class YesNoDialog (private var yesNoDialogListener: YesNoDialogListener) {
|
||||||
private val TAG = LogHelper.makeLogTag(YesNoDialog::class.java.simpleName)
|
private val TAG = LogHelper.makeLogTag(YesNoDialog::class.java.simpleName)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Construct and show dialog - variant: message from string */
|
/* Construct and show dialog - variant: message from string */
|
||||||
fun show(context: Context,
|
fun show(
|
||||||
type: Int,
|
context: Context,
|
||||||
title: Int = Keys.EMPTY_STRING_RESOURCE,
|
type: Int,
|
||||||
message: Int,
|
title: Int = Keys.EMPTY_STRING_RESOURCE,
|
||||||
yesButton: Int = R.string.dialog_yes_no_positive_button_default,
|
message: Int,
|
||||||
noButton: Int = R.string.dialog_generic_button_cancel,
|
yesButton: Int = R.string.dialog_yes_no_positive_button_default,
|
||||||
payload: Int = Keys.DIALOG_EMPTY_PAYLOAD_INT,
|
noButton: Int = R.string.dialog_generic_button_cancel,
|
||||||
payloadString: String = Keys.DIALOG_EMPTY_PAYLOAD_STRING) {
|
payload: Int = Keys.DIALOG_EMPTY_PAYLOAD_INT,
|
||||||
|
payloadString: String = Keys.DIALOG_EMPTY_PAYLOAD_STRING
|
||||||
|
) {
|
||||||
// extract string from message resource and feed into main show method
|
// extract string from message resource and feed into main show method
|
||||||
show(context, type, title, context.getString(message), yesButton, noButton, payload, payloadString)
|
show(
|
||||||
|
context,
|
||||||
|
type,
|
||||||
|
title,
|
||||||
|
context.getString(message),
|
||||||
|
yesButton,
|
||||||
|
noButton,
|
||||||
|
payload,
|
||||||
|
payloadString
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Construct and show dialog */
|
/* Construct and show dialog */
|
||||||
fun show(context: Context,
|
fun show(
|
||||||
type: Int,
|
context: Context,
|
||||||
title: Int = Keys.EMPTY_STRING_RESOURCE,
|
type: Int,
|
||||||
messageString: String,
|
title: Int = Keys.EMPTY_STRING_RESOURCE,
|
||||||
yesButton: Int = R.string.dialog_yes_no_positive_button_default,
|
messageString: String,
|
||||||
noButton: Int = R.string.dialog_generic_button_cancel,
|
yesButton: Int = R.string.dialog_yes_no_positive_button_default,
|
||||||
payload: Int = Keys.DIALOG_EMPTY_PAYLOAD_INT,
|
noButton: Int = R.string.dialog_generic_button_cancel,
|
||||||
payloadString: String = Keys.DIALOG_EMPTY_PAYLOAD_STRING) {
|
payload: Int = Keys.DIALOG_EMPTY_PAYLOAD_INT,
|
||||||
|
payloadString: String = Keys.DIALOG_EMPTY_PAYLOAD_STRING
|
||||||
|
) {
|
||||||
|
|
||||||
// prepare dialog builder
|
// prepare dialog builder
|
||||||
val builder: MaterialAlertDialogBuilder = MaterialAlertDialogBuilder(context, R.style.AlertDialogTheme)
|
val builder: MaterialAlertDialogBuilder =
|
||||||
|
MaterialAlertDialogBuilder(context, R.style.AlertDialogTheme)
|
||||||
|
|
||||||
// set title and message
|
// set title and message
|
||||||
builder.setMessage(messageString)
|
builder.setMessage(messageString)
|
||||||
|
@ -87,7 +98,7 @@ class YesNoDialog (private var yesNoDialogListener: YesNoDialogListener) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle outside-click as "no"
|
// handle outside-click as "no"
|
||||||
builder.setOnCancelListener(){
|
builder.setOnCancelListener() {
|
||||||
yesNoDialogListener.onYesNoDialog(type, false, payload, payloadString)
|
yesNoDialogListener.onYesNoDialog(type, false, payload, payloadString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,10 @@ import android.content.SharedPreferences
|
||||||
|
|
||||||
|
|
||||||
/* Puts a Double value in SharedPreferences */
|
/* Puts a Double value in SharedPreferences */
|
||||||
fun SharedPreferences.Editor.putDouble(key: String, double: Double) = putLong(key, java.lang.Double.doubleToRawLongBits(double))
|
fun SharedPreferences.Editor.putDouble(key: String, double: Double) =
|
||||||
|
putLong(key, java.lang.Double.doubleToRawLongBits(double))
|
||||||
|
|
||||||
|
|
||||||
/* gets a Double value from SharedPreferences */
|
/* gets a Double value from SharedPreferences */
|
||||||
fun SharedPreferences.getDouble(key: String, default: Double) = java.lang.Double.longBitsToDouble(getLong(key, java.lang.Double.doubleToRawLongBits(default)))
|
fun SharedPreferences.getDouble(key: String, default: Double) =
|
||||||
|
java.lang.Double.longBitsToDouble(getLong(key, java.lang.Double.doubleToRawLongBits(default)))
|
||||||
|
|
|
@ -99,6 +99,4 @@ object AppThemeHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -36,8 +36,10 @@ object DateTimeHelper {
|
||||||
fun convertToReadableTime(context: Context, milliseconds: Long): String {
|
fun convertToReadableTime(context: Context, milliseconds: Long): String {
|
||||||
var timeString: String = String()
|
var timeString: String = String()
|
||||||
val hours: Long = TimeUnit.MILLISECONDS.toHours(milliseconds)
|
val hours: Long = TimeUnit.MILLISECONDS.toHours(milliseconds)
|
||||||
val minutes: Long = TimeUnit.MILLISECONDS.toMinutes(milliseconds) % TimeUnit.HOURS.toMinutes(1)
|
val minutes: Long =
|
||||||
val seconds: Long = TimeUnit.MILLISECONDS.toSeconds(milliseconds) % TimeUnit.MINUTES.toSeconds(1)
|
TimeUnit.MILLISECONDS.toMinutes(milliseconds) % TimeUnit.HOURS.toMinutes(1)
|
||||||
|
val seconds: Long =
|
||||||
|
TimeUnit.MILLISECONDS.toSeconds(milliseconds) % TimeUnit.MINUTES.toSeconds(1)
|
||||||
val h: String = context.getString(R.string.abbreviation_hours)
|
val h: String = context.getString(R.string.abbreviation_hours)
|
||||||
val m: String = context.getString(R.string.abbreviation_minutes)
|
val m: String = context.getString(R.string.abbreviation_minutes)
|
||||||
val s: String = context.getString(R.string.abbreviation_seconds)
|
val s: String = context.getString(R.string.abbreviation_seconds)
|
||||||
|
@ -70,13 +72,19 @@ object DateTimeHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Creates a readable string date and time - used in the UI */
|
/* Creates a readable string date and time - used in the UI */
|
||||||
fun convertToReadableDateAndTime(date: Date, dateStyle: Int = DateFormat.SHORT, timeStyle: Int = DateFormat.SHORT): String {
|
fun convertToReadableDateAndTime(
|
||||||
return "${DateFormat.getDateInstance(dateStyle, Locale.getDefault()).format(date)} ${DateFormat.getTimeInstance(timeStyle, Locale.getDefault()).format(date)}"
|
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 */
|
/* Calculates time difference between two locations */
|
||||||
fun calculateTimeDistance(previousLocation: Location?, location: Location): Long {
|
fun calculateTimeDistance(previousLocation: Location?, location: Location): Long {
|
||||||
var timeDifference: Long = 0L
|
var timeDifference: Long = 0L
|
||||||
// two data points needed to calculate time difference
|
// two data points needed to calculate time difference
|
||||||
if (previousLocation != null) {
|
if (previousLocation != null) {
|
||||||
|
|
|
@ -48,10 +48,10 @@ object FileHelper {
|
||||||
|
|
||||||
/* Return an InputStream for given Uri */
|
/* Return an InputStream for given Uri */
|
||||||
fun getTextFileStream(context: Context, uri: Uri): InputStream? {
|
fun getTextFileStream(context: Context, uri: Uri): InputStream? {
|
||||||
var stream : InputStream? = null
|
var stream: InputStream? = null
|
||||||
try {
|
try {
|
||||||
stream = context.contentResolver.openInputStream(uri)
|
stream = context.contentResolver.openInputStream(uri)
|
||||||
} catch (e : Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
return stream
|
return stream
|
||||||
|
@ -154,16 +154,19 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Creates Uri for Gpx file of a track */
|
/* Creates Uri for Gpx file of a track */
|
||||||
fun getGpxFileUri(context: Context, track: Track): Uri = File(context.getExternalFilesDir(Keys.FOLDER_GPX), getGpxFileName(track)).toUri()
|
fun getGpxFileUri(context: Context, track: Track): Uri =
|
||||||
|
File(context.getExternalFilesDir(Keys.FOLDER_GPX), getGpxFileName(track)).toUri()
|
||||||
|
|
||||||
|
|
||||||
/* Creates file name for Gpx file of a track */
|
/* Creates file name for Gpx file of a track */
|
||||||
fun getGpxFileName(track: Track): String = DateTimeHelper.convertToSortableDateString(track.recordingStart) + Keys.GPX_FILE_EXTENSION
|
fun getGpxFileName(track: Track): String =
|
||||||
|
DateTimeHelper.convertToSortableDateString(track.recordingStart) + Keys.GPX_FILE_EXTENSION
|
||||||
|
|
||||||
|
|
||||||
/* Creates Uri for json track file */
|
/* Creates Uri for json track file */
|
||||||
fun getTrackFileUri(context: Context, track: Track): Uri {
|
fun getTrackFileUri(context: Context, track: Track): Uri {
|
||||||
val fileName: String = DateTimeHelper.convertToSortableDateString(track.recordingStart) + Keys.TRACKBOOK_FILE_EXTENSION
|
val fileName: String =
|
||||||
|
DateTimeHelper.convertToSortableDateString(track.recordingStart) + Keys.TRACKBOOK_FILE_EXTENSION
|
||||||
return File(context.getExternalFilesDir(Keys.FOLDER_TRACKS), fileName).toUri()
|
return File(context.getExternalFilesDir(Keys.FOLDER_TRACKS), fileName).toUri()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +178,11 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for saveTracklist */
|
/* Suspend function: Wrapper for saveTracklist */
|
||||||
suspend fun addTrackAndSaveTracklistSuspended(context: Context, track: Track, modificationDate: Date = track.recordingStop) {
|
suspend fun addTrackAndSaveTracklistSuspended(
|
||||||
|
context: Context,
|
||||||
|
track: Track,
|
||||||
|
modificationDate: Date = track.recordingStop
|
||||||
|
) {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
val tracklist: Tracklist = readTracklist(context)
|
val tracklist: Tracklist = readTracklist(context)
|
||||||
tracklist.tracklistElements.add(track.toTracklistElement(context))
|
tracklist.tracklistElements.add(track.toTracklistElement(context))
|
||||||
|
@ -193,7 +200,11 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for saveTracklist */
|
/* Suspend function: Wrapper for saveTracklist */
|
||||||
suspend fun saveTracklistSuspended(context: Context, tracklist: Tracklist, modificationDate: Date) {
|
suspend fun saveTracklistSuspended(
|
||||||
|
context: Context,
|
||||||
|
tracklist: Tracklist,
|
||||||
|
modificationDate: Date
|
||||||
|
) {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
cont.resume(saveTracklist(context, tracklist, modificationDate))
|
cont.resume(saveTracklist(context, tracklist, modificationDate))
|
||||||
}
|
}
|
||||||
|
@ -217,7 +228,11 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for deleteTrack */
|
/* Suspend function: Wrapper for deleteTrack */
|
||||||
suspend fun deleteTrackSuspended(context: Context, position: Int, tracklist: Tracklist): Tracklist {
|
suspend fun deleteTrackSuspended(
|
||||||
|
context: Context,
|
||||||
|
position: Int,
|
||||||
|
tracklist: Tracklist
|
||||||
|
): Tracklist {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
cont.resume(deleteTrack(context, position, tracklist))
|
cont.resume(deleteTrack(context, position, tracklist))
|
||||||
}
|
}
|
||||||
|
@ -240,14 +255,19 @@ object FileHelper {
|
||||||
|
|
||||||
/* Suspend function: Wrapper for readTracklist */
|
/* Suspend function: Wrapper for readTracklist */
|
||||||
suspend fun readTracklistSuspended(context: Context): Tracklist {
|
suspend fun readTracklistSuspended(context: Context): Tracklist {
|
||||||
return suspendCoroutine {cont ->
|
return suspendCoroutine { cont ->
|
||||||
cont.resume(readTracklist(context))
|
cont.resume(readTracklist(context))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for copyFile */
|
/* Suspend function: Wrapper for copyFile */
|
||||||
suspend fun saveCopyOfFileSuspended(context: Context, originalFileUri: Uri, targetFileUri: Uri, deleteOriginal: Boolean = false) {
|
suspend fun saveCopyOfFileSuspended(
|
||||||
|
context: Context,
|
||||||
|
originalFileUri: Uri,
|
||||||
|
targetFileUri: Uri,
|
||||||
|
deleteOriginal: Boolean = false
|
||||||
|
) {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
cont.resume(copyFile(context, originalFileUri, targetFileUri, deleteOriginal))
|
cont.resume(copyFile(context, originalFileUri, targetFileUri, deleteOriginal))
|
||||||
}
|
}
|
||||||
|
@ -304,7 +324,6 @@ object FileHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Renames track */
|
/* Renames track */
|
||||||
private fun renameTrack(context: Context, track: Track, newName: String) {
|
private fun renameTrack(context: Context, track: Track, newName: String) {
|
||||||
// search track in tracklist
|
// search track in tracklist
|
||||||
|
@ -329,13 +348,17 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Deletes multiple tracks */
|
/* Deletes multiple tracks */
|
||||||
private fun deleteTracks(context: Context, tracklistElements: MutableList<TracklistElement>, tracklist: Tracklist): Tracklist {
|
private fun deleteTracks(
|
||||||
|
context: Context,
|
||||||
|
tracklistElements: MutableList<TracklistElement>,
|
||||||
|
tracklist: Tracklist
|
||||||
|
): Tracklist {
|
||||||
tracklistElements.forEach { tracklistElement ->
|
tracklistElements.forEach { tracklistElement ->
|
||||||
// delete track files
|
// delete track files
|
||||||
tracklistElement.trackUriString.toUri().toFile().delete()
|
tracklistElement.trackUriString.toUri().toFile().delete()
|
||||||
tracklistElement.gpxUriString.toUri().toFile().delete()
|
tracklistElement.gpxUriString.toUri().toFile().delete()
|
||||||
}
|
}
|
||||||
tracklist.tracklistElements.removeAll{ tracklistElements.contains(it) }
|
tracklist.tracklistElements.removeAll { tracklistElements.contains(it) }
|
||||||
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
||||||
return tracklist
|
return tracklist
|
||||||
}
|
}
|
||||||
|
@ -348,14 +371,23 @@ object FileHelper {
|
||||||
tracklistElement.trackUriString.toUri().toFile().delete()
|
tracklistElement.trackUriString.toUri().toFile().delete()
|
||||||
tracklistElement.gpxUriString.toUri().toFile().delete()
|
tracklistElement.gpxUriString.toUri().toFile().delete()
|
||||||
// remove track element from list
|
// remove track element from list
|
||||||
tracklist.tracklistElements.removeIf { TrackHelper.getTrackId(it) == TrackHelper.getTrackId(tracklistElement) }
|
tracklist.tracklistElements.removeIf {
|
||||||
|
TrackHelper.getTrackId(it) == TrackHelper.getTrackId(
|
||||||
|
tracklistElement
|
||||||
|
)
|
||||||
|
}
|
||||||
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
||||||
return tracklist
|
return tracklist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Copies file to specified target */
|
/* Copies file to specified target */
|
||||||
private fun copyFile(context: Context, originalFileUri: Uri, targetFileUri: Uri, deleteOriginal: Boolean = false) {
|
private fun copyFile(
|
||||||
|
context: Context,
|
||||||
|
originalFileUri: Uri,
|
||||||
|
targetFileUri: Uri,
|
||||||
|
deleteOriginal: Boolean = false
|
||||||
|
) {
|
||||||
val inputStream = context.contentResolver.openInputStream(originalFileUri)
|
val inputStream = context.contentResolver.openInputStream(originalFileUri)
|
||||||
val outputStream = context.contentResolver.openOutputStream(targetFileUri)
|
val outputStream = context.contentResolver.openOutputStream(targetFileUri)
|
||||||
if (outputStream != null) {
|
if (outputStream != null) {
|
||||||
|
@ -389,7 +421,6 @@ object FileHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Converts byte value into a human readable format */
|
/* Converts byte value into a human readable format */
|
||||||
// Source: https://programming.guide/java/formatting-byte-size-to-human-readable-format.html
|
// Source: https://programming.guide/java/formatting-byte-size-to-human-readable-format.html
|
||||||
fun getReadableByteCount(bytes: Long, si: Boolean = true): String {
|
fun getReadableByteCount(bytes: Long, si: Boolean = true): String {
|
||||||
|
@ -430,7 +461,8 @@ object FileHelper {
|
||||||
val builder: StringBuilder = StringBuilder()
|
val builder: StringBuilder = StringBuilder()
|
||||||
reader.forEachLine {
|
reader.forEachLine {
|
||||||
builder.append(it)
|
builder.append(it)
|
||||||
builder.append("\n") }
|
builder.append("\n")
|
||||||
|
}
|
||||||
stream.close()
|
stream.close()
|
||||||
return builder.toString()
|
return builder.toString()
|
||||||
}
|
}
|
||||||
|
@ -444,8 +476,14 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Writes given bitmap as image file to storage */
|
/* Writes given bitmap as image file to storage */
|
||||||
private fun writeImageFile(context: Context, bitmap: Bitmap, file: File, format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG, quality: Int = 75) {
|
private fun writeImageFile(
|
||||||
if (file.exists()) file.delete ()
|
context: Context,
|
||||||
|
bitmap: Bitmap,
|
||||||
|
file: File,
|
||||||
|
format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
|
||||||
|
quality: Int = 75
|
||||||
|
) {
|
||||||
|
if (file.exists()) file.delete()
|
||||||
try {
|
try {
|
||||||
val out = FileOutputStream(file)
|
val out = FileOutputStream(file)
|
||||||
bitmap.compress(format, quality, out)
|
bitmap.compress(format, quality, out)
|
||||||
|
|
|
@ -96,7 +96,12 @@ object LengthUnitHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Converts for the given unit System distance and duration values to a readable velocity string */
|
/* Converts for the given unit System distance and duration values to a readable velocity string */
|
||||||
fun convertToVelocityString(trackDuration: Long, trackRecordingPause: Long, trackLength: Float, useImperialUnits: Boolean = false) : String {
|
fun convertToVelocityString(
|
||||||
|
trackDuration: Long,
|
||||||
|
trackRecordingPause: Long,
|
||||||
|
trackLength: Float,
|
||||||
|
useImperialUnits: Boolean = false
|
||||||
|
): String {
|
||||||
var speed: String = "0"
|
var speed: String = "0"
|
||||||
|
|
||||||
// duration minus pause in seconds
|
// duration minus pause in seconds
|
||||||
|
@ -104,7 +109,8 @@ object LengthUnitHelper {
|
||||||
|
|
||||||
if (duration > 0L) {
|
if (duration > 0L) {
|
||||||
// speed in km/h / mph
|
// speed in km/h / mph
|
||||||
val velocity: Double = convertMetersPerSecond((trackLength / duration), useImperialUnits)
|
val velocity: Double =
|
||||||
|
convertMetersPerSecond((trackLength / duration), useImperialUnits)
|
||||||
// create readable speed string
|
// create readable speed string
|
||||||
var bd: BigDecimal = BigDecimal.valueOf(velocity)
|
var bd: BigDecimal = BigDecimal.valueOf(velocity)
|
||||||
bd = bd.setScale(1, RoundingMode.HALF_UP)
|
bd = bd.setScale(1, RoundingMode.HALF_UP)
|
||||||
|
|
|
@ -62,10 +62,19 @@ object LocationHelper {
|
||||||
// get last location that Trackbook has stored
|
// get last location that Trackbook has stored
|
||||||
var lastKnownLocation: Location = PreferencesHelper.loadCurrentBestLocation(context)
|
var lastKnownLocation: Location = PreferencesHelper.loadCurrentBestLocation(context)
|
||||||
// try to get the last location the system has stored - it is probably more recent
|
// try to get the last location the system has stored - it is probably more recent
|
||||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(
|
||||||
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
context,
|
||||||
val lastKnownLocationGps: Location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) ?: lastKnownLocation
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
val lastKnownLocationNetwork: Location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) ?: lastKnownLocation
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
val locationManager =
|
||||||
|
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
|
val lastKnownLocationGps: Location =
|
||||||
|
locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
||||||
|
?: lastKnownLocation
|
||||||
|
val lastKnownLocationNetwork: Location =
|
||||||
|
locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
|
||||||
|
?: lastKnownLocation
|
||||||
when (isBetterLocation(lastKnownLocationGps, lastKnownLocationNetwork)) {
|
when (isBetterLocation(lastKnownLocationGps, lastKnownLocationNetwork)) {
|
||||||
true -> lastKnownLocation = lastKnownLocationGps
|
true -> lastKnownLocation = lastKnownLocationGps
|
||||||
false -> lastKnownLocation = lastKnownLocationNetwork
|
false -> lastKnownLocation = lastKnownLocationNetwork
|
||||||
|
@ -87,7 +96,7 @@ object LocationHelper {
|
||||||
// check whether the new location fix is newer or older
|
// check whether the new location fix is newer or older
|
||||||
val timeDelta: Long = location.time - currentBestLocation.time
|
val timeDelta: Long = location.time - currentBestLocation.time
|
||||||
val isSignificantlyNewer: Boolean = timeDelta > Keys.SIGNIFICANT_TIME_DIFFERENCE
|
val isSignificantlyNewer: Boolean = timeDelta > Keys.SIGNIFICANT_TIME_DIFFERENCE
|
||||||
val isSignificantlyOlder:Boolean = timeDelta < -Keys.SIGNIFICANT_TIME_DIFFERENCE
|
val isSignificantlyOlder: Boolean = timeDelta < -Keys.SIGNIFICANT_TIME_DIFFERENCE
|
||||||
|
|
||||||
when {
|
when {
|
||||||
// if it's been more than two minutes since the current location, use the new location because the user has likely moved
|
// if it's been more than two minutes since the current location, use the new location because the user has likely moved
|
||||||
|
@ -136,7 +145,6 @@ object LocationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Checks if given location is new */
|
/* Checks if given location is new */
|
||||||
fun isRecentEnough(location: Location): Boolean {
|
fun isRecentEnough(location: Location): Boolean {
|
||||||
val locationAge: Long = SystemClock.elapsedRealtimeNanos() - location.elapsedRealtimeNanos
|
val locationAge: Long = SystemClock.elapsedRealtimeNanos() - location.elapsedRealtimeNanos
|
||||||
|
@ -148,8 +156,10 @@ object LocationHelper {
|
||||||
fun isAccurateEnough(location: Location, locationAccuracyThreshold: Int): Boolean {
|
fun isAccurateEnough(location: Location, locationAccuracyThreshold: Int): Boolean {
|
||||||
val isAccurate: Boolean
|
val isAccurate: Boolean
|
||||||
when (location.provider) {
|
when (location.provider) {
|
||||||
LocationManager.GPS_PROVIDER -> isAccurate = location.accuracy < locationAccuracyThreshold
|
LocationManager.GPS_PROVIDER -> isAccurate =
|
||||||
else -> isAccurate = location.accuracy < locationAccuracyThreshold + 10 // a bit more relaxed when location comes from network provider
|
location.accuracy < locationAccuracyThreshold
|
||||||
|
else -> isAccurate =
|
||||||
|
location.accuracy < locationAccuracyThreshold + 10 // a bit more relaxed when location comes from network provider
|
||||||
}
|
}
|
||||||
return isAccurate
|
return isAccurate
|
||||||
}
|
}
|
||||||
|
@ -158,14 +168,25 @@ object LocationHelper {
|
||||||
/* Checks if the first location of track is plausible */
|
/* Checks if the first location of track is plausible */
|
||||||
fun isFirstLocationPlausible(secondLocation: Location, track: Track): Boolean {
|
fun isFirstLocationPlausible(secondLocation: Location, track: Track): Boolean {
|
||||||
// speed in km/h
|
// speed in km/h
|
||||||
val speed: Double = calculateSpeed(firstLocation = track.wayPoints[0].toLocation(), secondLocation = secondLocation, firstTimestamp = track.recordingStart.time, secondTimestamp = GregorianCalendar.getInstance().time.time)
|
val speed: Double = calculateSpeed(
|
||||||
|
firstLocation = track.wayPoints[0].toLocation(),
|
||||||
|
secondLocation = secondLocation,
|
||||||
|
firstTimestamp = track.recordingStart.time,
|
||||||
|
secondTimestamp = GregorianCalendar.getInstance().time.time
|
||||||
|
)
|
||||||
// plausible = speed under 250 km/h
|
// plausible = speed under 250 km/h
|
||||||
return speed < Keys.IMPLAUSIBLE_TRACK_START_SPEED
|
return speed < Keys.IMPLAUSIBLE_TRACK_START_SPEED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Calculates speed */
|
/* Calculates speed */
|
||||||
private fun calculateSpeed(firstLocation: Location, secondLocation: Location, firstTimestamp: Long, secondTimestamp: Long, useImperial: Boolean = false): Double {
|
private fun calculateSpeed(
|
||||||
|
firstLocation: Location,
|
||||||
|
secondLocation: Location,
|
||||||
|
firstTimestamp: Long,
|
||||||
|
secondTimestamp: Long,
|
||||||
|
useImperial: Boolean = false
|
||||||
|
): Double {
|
||||||
// time difference in seconds
|
// time difference in seconds
|
||||||
val timeDifference: Long = (secondTimestamp - firstTimestamp) / 1000L
|
val timeDifference: Long = (secondTimestamp - firstTimestamp) / 1000L
|
||||||
// distance in meters
|
// distance in meters
|
||||||
|
@ -194,7 +215,7 @@ object LocationHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Calculates distance in meters between two locations */
|
/* Calculates distance in meters between two locations */
|
||||||
fun calculateDistance(previousLocation: Location?, location: Location): Float {
|
fun calculateDistance(previousLocation: Location?, location: Location): Float {
|
||||||
var distance: Float = 0f
|
var distance: Float = 0f
|
||||||
// two data points needed to calculate distance
|
// two data points needed to calculate distance
|
||||||
if (previousLocation != null) {
|
if (previousLocation != null) {
|
||||||
|
@ -206,20 +227,26 @@ object LocationHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Calculate elevation differences */
|
/* Calculate elevation differences */
|
||||||
fun calculateElevationDifferences(previousLocation: Location?, location: Location, track: Track): Pair<Double, Double> {
|
fun calculateElevationDifferences(
|
||||||
|
previousLocation: Location?,
|
||||||
|
location: Location,
|
||||||
|
track: Track
|
||||||
|
): Pair<Double, Double> {
|
||||||
// store current values
|
// store current values
|
||||||
var positiveElevation: Double = track.positiveElevation
|
var positiveElevation: Double = track.positiveElevation
|
||||||
var negativeElevation: Double = track.negativeElevation
|
var negativeElevation: Double = track.negativeElevation
|
||||||
if (previousLocation != null) {
|
if (previousLocation != null) {
|
||||||
// factor is bigger than 1 if the time stamp difference is larger than the movement recording interval
|
// factor is bigger than 1 if the time stamp difference is larger than the movement recording interval
|
||||||
val timeDifferenceFactor: Long = (location.time - previousLocation.time) / Keys.ADD_WAYPOINT_TO_TRACK_INTERVAL
|
val timeDifferenceFactor: Long =
|
||||||
|
(location.time - previousLocation.time) / Keys.ADD_WAYPOINT_TO_TRACK_INTERVAL
|
||||||
// get elevation difference and sum it up
|
// get elevation difference and sum it up
|
||||||
val altitudeDifference: Double = location.altitude - previousLocation.altitude
|
val altitudeDifference: Double = location.altitude - previousLocation.altitude
|
||||||
if (altitudeDifference > 0 && altitudeDifference < Keys.ALTITUDE_MEASUREMENT_ERROR_THRESHOLD * timeDifferenceFactor && location.altitude != Keys.DEFAULT_ALTITUDE) {
|
if (altitudeDifference > 0 && altitudeDifference < Keys.ALTITUDE_MEASUREMENT_ERROR_THRESHOLD * timeDifferenceFactor && location.altitude != Keys.DEFAULT_ALTITUDE) {
|
||||||
positiveElevation = track.positiveElevation + altitudeDifference // upwards movement
|
positiveElevation = track.positiveElevation + altitudeDifference // upwards movement
|
||||||
}
|
}
|
||||||
if (altitudeDifference < 0 && altitudeDifference > -Keys.ALTITUDE_MEASUREMENT_ERROR_THRESHOLD * timeDifferenceFactor && location.altitude != Keys.DEFAULT_ALTITUDE) {
|
if (altitudeDifference < 0 && altitudeDifference > -Keys.ALTITUDE_MEASUREMENT_ERROR_THRESHOLD * timeDifferenceFactor && location.altitude != Keys.DEFAULT_ALTITUDE) {
|
||||||
negativeElevation = track.negativeElevation + altitudeDifference // downwards movement
|
negativeElevation =
|
||||||
|
track.negativeElevation + altitudeDifference // downwards movement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Pair(positiveElevation, negativeElevation)
|
return Pair(positiveElevation, negativeElevation)
|
||||||
|
|
|
@ -38,7 +38,7 @@ import java.util.*
|
||||||
/*
|
/*
|
||||||
* MapHelper class
|
* MapHelper class
|
||||||
*/
|
*/
|
||||||
class MapOverlay (private var markerListener: MarkerListener) {
|
class MapOverlay(private var markerListener: MarkerListener) {
|
||||||
|
|
||||||
/* Interface used to communicate back to activity/fragment */
|
/* Interface used to communicate back to activity/fragment */
|
||||||
interface MarkerListener {
|
interface MarkerListener {
|
||||||
|
@ -51,7 +51,11 @@ class MapOverlay (private var markerListener: MarkerListener) {
|
||||||
|
|
||||||
|
|
||||||
/* Creates icon overlay for current position (used in MapFragment) */
|
/* Creates icon overlay for current position (used in MapFragment) */
|
||||||
fun createMyLocationOverlay(context: Context, location: Location, trackingState: Int): ItemizedIconOverlay<OverlayItem> {
|
fun createMyLocationOverlay(
|
||||||
|
context: Context,
|
||||||
|
location: Location,
|
||||||
|
trackingState: Int
|
||||||
|
): ItemizedIconOverlay<OverlayItem> {
|
||||||
|
|
||||||
val overlayItems = ArrayList<OverlayItem>()
|
val overlayItems = ArrayList<OverlayItem>()
|
||||||
val locationIsOld = LocationHelper.isOldLocation(location)
|
val locationIsOld = LocationHelper.isOldLocation(location)
|
||||||
|
@ -62,21 +66,38 @@ class MapOverlay (private var markerListener: MarkerListener) {
|
||||||
// CASE: Tracking active
|
// CASE: Tracking active
|
||||||
Keys.STATE_TRACKING_ACTIVE -> {
|
Keys.STATE_TRACKING_ACTIVE -> {
|
||||||
when (locationIsOld) {
|
when (locationIsOld) {
|
||||||
true -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_red_grey_24dp)!!
|
true -> newMarker = ContextCompat.getDrawable(
|
||||||
false -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_red_24dp)!!
|
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
|
// CASE. Tracking is NOT active
|
||||||
else -> {
|
else -> {
|
||||||
when (locationIsOld) {
|
when (locationIsOld) {
|
||||||
true -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_blue_grey_24dp)!!
|
true -> newMarker = ContextCompat.getDrawable(
|
||||||
false -> newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_blue_24dp)!!
|
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
|
// add marker to list of overlay items
|
||||||
val overlayItem = createOverlayItem(context, location.latitude, location.longitude, location.accuracy, location.provider, location.time)
|
val overlayItem = createOverlayItem(
|
||||||
|
context,
|
||||||
|
location.latitude,
|
||||||
|
location.longitude,
|
||||||
|
location.accuracy,
|
||||||
|
location.provider,
|
||||||
|
location.time
|
||||||
|
)
|
||||||
overlayItem.setMarker(newMarker)
|
overlayItem.setMarker(newMarker)
|
||||||
overlayItems.add(overlayItem)
|
overlayItems.add(overlayItem)
|
||||||
|
|
||||||
|
@ -86,7 +107,11 @@ class MapOverlay (private var markerListener: MarkerListener) {
|
||||||
|
|
||||||
|
|
||||||
/* Creates icon overlay for track */
|
/* Creates icon overlay for track */
|
||||||
fun createTrackOverlay(context: Context, track: Track, trackingState: Int): ItemizedIconOverlay<OverlayItem> {
|
fun createTrackOverlay(
|
||||||
|
context: Context,
|
||||||
|
track: Track,
|
||||||
|
trackingState: Int
|
||||||
|
): ItemizedIconOverlay<OverlayItem> {
|
||||||
|
|
||||||
val overlayItems = ArrayList<OverlayItem>()
|
val overlayItems = ArrayList<OverlayItem>()
|
||||||
val wayPoints = track.wayPoints
|
val wayPoints = track.wayPoints
|
||||||
|
@ -100,27 +125,48 @@ class MapOverlay (private var markerListener: MarkerListener) {
|
||||||
// CASE: Recording is active
|
// CASE: Recording is active
|
||||||
Keys.STATE_TRACKING_ACTIVE -> {
|
Keys.STATE_TRACKING_ACTIVE -> {
|
||||||
if (wayPoint.starred) {
|
if (wayPoint.starred) {
|
||||||
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_star_red_24dp)!!
|
newMarker =
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.ic_star_red_24dp)!!
|
||||||
} else if (wayPoint.isStopOver) {
|
} else if (wayPoint.isStopOver) {
|
||||||
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_grey_24dp)!!
|
newMarker = ContextCompat.getDrawable(
|
||||||
|
context,
|
||||||
|
R.drawable.ic_marker_track_location_grey_24dp
|
||||||
|
)!!
|
||||||
} else {
|
} else {
|
||||||
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_red_24dp)!!
|
newMarker = ContextCompat.getDrawable(
|
||||||
|
context,
|
||||||
|
R.drawable.ic_marker_track_location_red_24dp
|
||||||
|
)!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// CASE: Recording is paused/stopped
|
// CASE: Recording is paused/stopped
|
||||||
else -> {
|
else -> {
|
||||||
if (wayPoint.starred) {
|
if (wayPoint.starred) {
|
||||||
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_star_blue_24dp)!!
|
newMarker =
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.ic_star_blue_24dp)!!
|
||||||
} else if (wayPoint.isStopOver) {
|
} else if (wayPoint.isStopOver) {
|
||||||
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_grey_24dp)!!
|
newMarker = ContextCompat.getDrawable(
|
||||||
|
context,
|
||||||
|
R.drawable.ic_marker_track_location_grey_24dp
|
||||||
|
)!!
|
||||||
} else {
|
} else {
|
||||||
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_blue_24dp)!!
|
newMarker = ContextCompat.getDrawable(
|
||||||
|
context,
|
||||||
|
R.drawable.ic_marker_track_location_blue_24dp
|
||||||
|
)!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create overlay item and add to list of overlay items
|
// create overlay item and add to list of overlay items
|
||||||
val overlayItem = createOverlayItem(context, wayPoint.latitude, wayPoint.longitude, wayPoint.accuracy, wayPoint.provider, wayPoint.time)
|
val overlayItem = createOverlayItem(
|
||||||
|
context,
|
||||||
|
wayPoint.latitude,
|
||||||
|
wayPoint.longitude,
|
||||||
|
wayPoint.accuracy,
|
||||||
|
wayPoint.provider,
|
||||||
|
wayPoint.time
|
||||||
|
)
|
||||||
overlayItem.setMarker(newMarker)
|
overlayItem.setMarker(newMarker)
|
||||||
overlayItems.add(overlayItem)
|
overlayItems.add(overlayItem)
|
||||||
}
|
}
|
||||||
|
@ -131,23 +177,45 @@ class MapOverlay (private var markerListener: MarkerListener) {
|
||||||
|
|
||||||
|
|
||||||
/* Creates a marker overlay item */
|
/* Creates a marker overlay item */
|
||||||
private fun createOverlayItem(context: Context, latitude: Double, longitude: Double, accuracy: Float, provider: String, time: Long): OverlayItem {
|
private fun createOverlayItem(
|
||||||
val title: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)}"
|
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_accuracy)}: ${DecimalFormat("#0.00").format(accuracy)} (${provider})"
|
||||||
val description: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(accuracy)} (${provider})"
|
val description: String =
|
||||||
|
"${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(
|
||||||
|
SimpleDateFormat.MEDIUM,
|
||||||
|
Locale.getDefault()
|
||||||
|
)
|
||||||
|
.format(time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat(
|
||||||
|
"#0.00"
|
||||||
|
).format(accuracy)} (${provider})"
|
||||||
val position: GeoPoint = GeoPoint(latitude, longitude)
|
val position: GeoPoint = GeoPoint(latitude, longitude)
|
||||||
return OverlayItem(title, description, position)
|
return OverlayItem(title, description, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Creates an overlay */
|
/* Creates an overlay */
|
||||||
private fun createOverlay(context: Context, overlayItems: ArrayList<OverlayItem>): ItemizedIconOverlay<OverlayItem> {
|
private fun createOverlay(
|
||||||
|
context: Context,
|
||||||
|
overlayItems: ArrayList<OverlayItem>
|
||||||
|
): ItemizedIconOverlay<OverlayItem> {
|
||||||
return ItemizedIconOverlay<OverlayItem>(context, overlayItems,
|
return ItemizedIconOverlay<OverlayItem>(context, overlayItems,
|
||||||
object : ItemizedIconOverlay.OnItemGestureListener<OverlayItem> {
|
object : ItemizedIconOverlay.OnItemGestureListener<OverlayItem> {
|
||||||
override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean {
|
override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean {
|
||||||
markerListener.onMarkerTapped(item.point.latitude, item.point.longitude)
|
markerListener.onMarkerTapped(item.point.latitude, item.point.longitude)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemLongPress(index: Int, item: OverlayItem): Boolean {
|
override fun onItemLongPress(index: Int, item: OverlayItem): Boolean {
|
||||||
val v = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
val v = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||||
v.vibrate(50)
|
v.vibrate(50)
|
||||||
|
|
|
@ -41,11 +41,17 @@ class NotificationHelper(private val trackerService: TrackerService) {
|
||||||
|
|
||||||
|
|
||||||
/* Main class variables */
|
/* Main class variables */
|
||||||
private val notificationManager: NotificationManager = trackerService.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
private val notificationManager: NotificationManager =
|
||||||
|
trackerService.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
|
|
||||||
/* Creates notification */
|
/* Creates notification */
|
||||||
fun createNotification(trackingState: Int, trackLength: Float, duration: Long, useImperial: Boolean): Notification {
|
fun createNotification(
|
||||||
|
trackingState: Int,
|
||||||
|
trackLength: Float,
|
||||||
|
duration: Long,
|
||||||
|
useImperial: Boolean
|
||||||
|
): Notification {
|
||||||
|
|
||||||
// create notification channel if necessary
|
// create notification channel if necessary
|
||||||
if (shouldCreateNotificationChannel()) {
|
if (shouldCreateNotificationChannel()) {
|
||||||
|
@ -53,7 +59,8 @@ class NotificationHelper(private val trackerService: TrackerService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build notification
|
// Build notification
|
||||||
val builder = NotificationCompat.Builder(trackerService, Keys.NOTIFICATION_CHANNEL_RECORDING)
|
val builder =
|
||||||
|
NotificationCompat.Builder(trackerService, Keys.NOTIFICATION_CHANNEL_RECORDING)
|
||||||
builder.setContentIntent(showActionPendingIntent)
|
builder.setContentIntent(showActionPendingIntent)
|
||||||
builder.setSmallIcon(R.drawable.ic_notification_icon_small_24dp)
|
builder.setSmallIcon(R.drawable.ic_notification_icon_small_24dp)
|
||||||
builder.setContentText(getContentString(trackerService, duration, trackLength, useImperial))
|
builder.setContentText(getContentString(trackerService, duration, trackLength, useImperial))
|
||||||
|
@ -63,13 +70,23 @@ class NotificationHelper(private val trackerService: TrackerService) {
|
||||||
Keys.STATE_TRACKING_ACTIVE -> {
|
Keys.STATE_TRACKING_ACTIVE -> {
|
||||||
builder.setContentTitle(trackerService.getString(R.string.notification_title_trackbook_running))
|
builder.setContentTitle(trackerService.getString(R.string.notification_title_trackbook_running))
|
||||||
builder.addAction(stopAction)
|
builder.addAction(stopAction)
|
||||||
builder.setLargeIcon(AppCompatResources.getDrawable(trackerService, R.drawable.ic_notification_icon_large_tracking_active_48dp)!!.toBitmap())
|
builder.setLargeIcon(
|
||||||
|
AppCompatResources.getDrawable(
|
||||||
|
trackerService,
|
||||||
|
R.drawable.ic_notification_icon_large_tracking_active_48dp
|
||||||
|
)!!.toBitmap()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
builder.setContentTitle(trackerService.getString(R.string.notification_title_trackbook_not_running))
|
builder.setContentTitle(trackerService.getString(R.string.notification_title_trackbook_not_running))
|
||||||
builder.addAction(resumeAction)
|
builder.addAction(resumeAction)
|
||||||
builder.addAction(showAction)
|
builder.addAction(showAction)
|
||||||
builder.setLargeIcon(AppCompatResources.getDrawable(trackerService, R.drawable.ic_notification_icon_large_tracking_stopped_48dp)!!.toBitmap())
|
builder.setLargeIcon(
|
||||||
|
AppCompatResources.getDrawable(
|
||||||
|
trackerService,
|
||||||
|
R.drawable.ic_notification_icon_large_tracking_stopped_48dp
|
||||||
|
)!!.toBitmap()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,56 +96,77 @@ class NotificationHelper(private val trackerService: TrackerService) {
|
||||||
|
|
||||||
|
|
||||||
/* Build context text for notification builder */
|
/* Build context text for notification builder */
|
||||||
private fun getContentString(context: Context, duration: Long, trackLength: Float, useImperial: Boolean): String {
|
private fun getContentString(
|
||||||
return "${LengthUnitHelper.convertDistanceToString(trackLength, useImperial)} • ${DateTimeHelper.convertToReadableTime(context, duration)}"
|
context: Context,
|
||||||
|
duration: Long,
|
||||||
|
trackLength: Float,
|
||||||
|
useImperial: Boolean
|
||||||
|
): String {
|
||||||
|
return "${LengthUnitHelper.convertDistanceToString(
|
||||||
|
trackLength,
|
||||||
|
useImperial
|
||||||
|
)} • ${DateTimeHelper.convertToReadableTime(context, duration)}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Checks if notification channel should be created */
|
/* Checks if notification channel should be created */
|
||||||
private fun shouldCreateNotificationChannel() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !nowPlayingChannelExists()
|
private fun shouldCreateNotificationChannel() =
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !nowPlayingChannelExists()
|
||||||
|
|
||||||
|
|
||||||
/* Checks if notification channel exists */
|
/* Checks if notification channel exists */
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
private fun nowPlayingChannelExists() = notificationManager.getNotificationChannel(Keys.NOTIFICATION_CHANNEL_RECORDING) != null
|
private fun nowPlayingChannelExists() =
|
||||||
|
notificationManager.getNotificationChannel(Keys.NOTIFICATION_CHANNEL_RECORDING) != null
|
||||||
|
|
||||||
|
|
||||||
/* Create a notification channel */
|
/* Create a notification channel */
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
private fun createNotificationChannel() {
|
private fun createNotificationChannel() {
|
||||||
val notificationChannel = NotificationChannel(Keys.NOTIFICATION_CHANNEL_RECORDING,
|
val notificationChannel = NotificationChannel(
|
||||||
|
Keys.NOTIFICATION_CHANNEL_RECORDING,
|
||||||
trackerService.getString(R.string.notification_channel_recording_name),
|
trackerService.getString(R.string.notification_channel_recording_name),
|
||||||
NotificationManager.IMPORTANCE_LOW)
|
NotificationManager.IMPORTANCE_LOW
|
||||||
.apply { description = trackerService.getString(R.string.notification_channel_recording_description) }
|
)
|
||||||
|
.apply {
|
||||||
|
description =
|
||||||
|
trackerService.getString(R.string.notification_channel_recording_description)
|
||||||
|
}
|
||||||
notificationManager.createNotificationChannel(notificationChannel)
|
notificationManager.createNotificationChannel(notificationChannel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Notification pending intents */
|
/* Notification pending intents */
|
||||||
private val stopActionPendingIntent = PendingIntent.getService(
|
private val stopActionPendingIntent = PendingIntent.getService(
|
||||||
trackerService,14,
|
trackerService, 14,
|
||||||
Intent(trackerService, TrackerService::class.java).setAction(Keys.ACTION_STOP),0)
|
Intent(trackerService, TrackerService::class.java).setAction(Keys.ACTION_STOP), 0
|
||||||
|
)
|
||||||
private val resumeActionPendingIntent = PendingIntent.getService(
|
private val resumeActionPendingIntent = PendingIntent.getService(
|
||||||
trackerService, 16,
|
trackerService, 16,
|
||||||
Intent(trackerService, TrackerService::class.java).setAction(Keys.ACTION_RESUME),0)
|
Intent(trackerService, TrackerService::class.java).setAction(Keys.ACTION_RESUME), 0
|
||||||
private val showActionPendingIntent: PendingIntent? = TaskStackBuilder.create(trackerService).run {
|
)
|
||||||
addNextIntentWithParentStack(Intent(trackerService, MainActivity::class.java))
|
private val showActionPendingIntent: PendingIntent? =
|
||||||
getPendingIntent(10, PendingIntent.FLAG_UPDATE_CURRENT)
|
TaskStackBuilder.create(trackerService).run {
|
||||||
}
|
addNextIntentWithParentStack(Intent(trackerService, MainActivity::class.java))
|
||||||
|
getPendingIntent(10, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Notification actions */
|
/* Notification actions */
|
||||||
private val stopAction = NotificationCompat.Action(
|
private val stopAction = NotificationCompat.Action(
|
||||||
R.drawable.ic_notification_action_stop_24dp,
|
R.drawable.ic_notification_action_stop_24dp,
|
||||||
trackerService.getString(R.string.notification_stop),
|
trackerService.getString(R.string.notification_stop),
|
||||||
stopActionPendingIntent)
|
stopActionPendingIntent
|
||||||
|
)
|
||||||
private val resumeAction = NotificationCompat.Action(
|
private val resumeAction = NotificationCompat.Action(
|
||||||
R.drawable.ic_notification_action_resume_36dp,
|
R.drawable.ic_notification_action_resume_36dp,
|
||||||
trackerService.getString(R.string.notification_resume),
|
trackerService.getString(R.string.notification_resume),
|
||||||
resumeActionPendingIntent)
|
resumeActionPendingIntent
|
||||||
|
)
|
||||||
private val showAction = NotificationCompat.Action(
|
private val showAction = NotificationCompat.Action(
|
||||||
R.drawable.ic_notification_action_show_36dp,
|
R.drawable.ic_notification_action_show_36dp,
|
||||||
trackerService.getString(R.string.notification_show),
|
trackerService.getString(R.string.notification_show),
|
||||||
showActionPendingIntent)
|
showActionPendingIntent
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
|
@ -80,7 +80,10 @@ object PreferencesHelper {
|
||||||
// get preferences
|
// get preferences
|
||||||
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
// load length unit system
|
// load length unit system
|
||||||
return settings.getBoolean(Keys.PREF_USE_IMPERIAL_UNITS, LengthUnitHelper.useImperialUnits())
|
return settings.getBoolean(
|
||||||
|
Keys.PREF_USE_IMPERIAL_UNITS,
|
||||||
|
LengthUnitHelper.useImperialUnits()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,7 +100,10 @@ object PreferencesHelper {
|
||||||
// get preferences
|
// get preferences
|
||||||
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
// load tracking state
|
// load tracking state
|
||||||
return settings.getInt(Keys.PREF_LOCATION_ACCURACY_THRESHOLD, Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY)
|
return settings.getInt(
|
||||||
|
Keys.PREF_LOCATION_ACCURACY_THRESHOLD,
|
||||||
|
Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,15 +111,23 @@ object PreferencesHelper {
|
||||||
fun loadCurrentBestLocation(context: Context): Location {
|
fun loadCurrentBestLocation(context: Context): Location {
|
||||||
// get preferences
|
// get preferences
|
||||||
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
val provider: String = settings.getString(Keys.PREF_CURRENT_BEST_LOCATION_PROVIDER, LocationManager.NETWORK_PROVIDER) ?: LocationManager.NETWORK_PROVIDER
|
val provider: String = settings.getString(
|
||||||
|
Keys.PREF_CURRENT_BEST_LOCATION_PROVIDER,
|
||||||
|
LocationManager.NETWORK_PROVIDER
|
||||||
|
) ?: LocationManager.NETWORK_PROVIDER
|
||||||
// create location
|
// create location
|
||||||
val currentBestLocation: Location = Location(provider)
|
val currentBestLocation: Location = Location(provider)
|
||||||
// load location attributes
|
// load location attributes
|
||||||
currentBestLocation.latitude = settings.getDouble(Keys.PREF_CURRENT_BEST_LOCATION_LATITUDE, Keys.DEFAULT_LATITUDE)
|
currentBestLocation.latitude =
|
||||||
currentBestLocation.longitude = settings.getDouble(Keys.PREF_CURRENT_BEST_LOCATION_LONGITUDE, Keys.DEFAULT_LONGITUDE)
|
settings.getDouble(Keys.PREF_CURRENT_BEST_LOCATION_LATITUDE, Keys.DEFAULT_LATITUDE)
|
||||||
currentBestLocation.accuracy = settings.getFloat(Keys.PREF_CURRENT_BEST_LOCATION_ACCURACY, Keys.DEFAULT_ACCURACY)
|
currentBestLocation.longitude =
|
||||||
currentBestLocation.altitude = settings.getDouble(Keys.PREF_CURRENT_BEST_LOCATION_ALTITUDE, Keys.DEFAULT_ALTITUDE)
|
settings.getDouble(Keys.PREF_CURRENT_BEST_LOCATION_LONGITUDE, Keys.DEFAULT_LONGITUDE)
|
||||||
currentBestLocation.time = settings.getLong(Keys.PREF_CURRENT_BEST_LOCATION_TIME, Keys.DEFAULT_TIME)
|
currentBestLocation.accuracy =
|
||||||
|
settings.getFloat(Keys.PREF_CURRENT_BEST_LOCATION_ACCURACY, Keys.DEFAULT_ACCURACY)
|
||||||
|
currentBestLocation.altitude =
|
||||||
|
settings.getDouble(Keys.PREF_CURRENT_BEST_LOCATION_ALTITUDE, Keys.DEFAULT_ALTITUDE)
|
||||||
|
currentBestLocation.time =
|
||||||
|
settings.getLong(Keys.PREF_CURRENT_BEST_LOCATION_TIME, Keys.DEFAULT_TIME)
|
||||||
return currentBestLocation
|
return currentBestLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +149,9 @@ object PreferencesHelper {
|
||||||
|
|
||||||
/* Load currently selected app theme */
|
/* Load currently selected app theme */
|
||||||
fun loadThemeSelection(context: Context): String {
|
fun loadThemeSelection(context: Context): String {
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context).getString(Keys.PREF_THEME_SELECTION, Keys.STATE_THEME_FOLLOW_SYSTEM) ?: Keys.STATE_THEME_FOLLOW_SYSTEM
|
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.getString(Keys.PREF_THEME_SELECTION, Keys.STATE_THEME_FOLLOW_SYSTEM)
|
||||||
|
?: Keys.STATE_THEME_FOLLOW_SYSTEM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,13 @@ object TrackHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Adds given locatiom as waypoint to track */
|
/* Adds given locatiom as waypoint to track */
|
||||||
fun addWayPointToTrack(context: Context, track: Track, location: Location, locationAccuracyThreshold: Int, resumed: Boolean): Pair<Track, Boolean> {
|
fun addWayPointToTrack(
|
||||||
|
context: Context,
|
||||||
|
track: Track,
|
||||||
|
location: Location,
|
||||||
|
locationAccuracyThreshold: Int,
|
||||||
|
resumed: Boolean
|
||||||
|
): Pair<Track, Boolean> {
|
||||||
// get previous location
|
// get previous location
|
||||||
val previousLocation: Location?
|
val previousLocation: Location?
|
||||||
var numberOfWayPoints: Int = track.wayPoints.size
|
var numberOfWayPoints: Int = track.wayPoints.size
|
||||||
|
@ -58,7 +64,11 @@ object TrackHelper {
|
||||||
previousLocation = null
|
previousLocation = null
|
||||||
}
|
}
|
||||||
// CASE: Second location - check if first location was plausible & remove implausible location
|
// CASE: Second location - check if first location was plausible & remove implausible location
|
||||||
else if (numberOfWayPoints == 1 && !LocationHelper.isFirstLocationPlausible(location, track)) {
|
else if (numberOfWayPoints == 1 && !LocationHelper.isFirstLocationPlausible(
|
||||||
|
location,
|
||||||
|
track
|
||||||
|
)
|
||||||
|
) {
|
||||||
previousLocation = null
|
previousLocation = null
|
||||||
numberOfWayPoints = 0
|
numberOfWayPoints = 0
|
||||||
track.wayPoints.removeAt(0)
|
track.wayPoints.removeAt(0)
|
||||||
|
@ -76,8 +86,8 @@ object TrackHelper {
|
||||||
|
|
||||||
// add only if recent and accurate and different
|
// add only if recent and accurate and different
|
||||||
val shouldBeAdded: Boolean = (LocationHelper.isRecentEnough(location) &&
|
val shouldBeAdded: Boolean = (LocationHelper.isRecentEnough(location) &&
|
||||||
LocationHelper.isAccurateEnough(location, locationAccuracyThreshold) &&
|
LocationHelper.isAccurateEnough(location, locationAccuracyThreshold) &&
|
||||||
LocationHelper.isDifferentEnough(previousLocation, location))
|
LocationHelper.isDifferentEnough(previousLocation, location))
|
||||||
|
|
||||||
// // Debugging for shouldBeAdded - remove for production
|
// // Debugging for shouldBeAdded - remove for production
|
||||||
// val recentEnough: Boolean = LocationHelper.isRecentEnough(location)
|
// val recentEnough: Boolean = LocationHelper.isRecentEnough(location)
|
||||||
|
@ -95,7 +105,8 @@ object TrackHelper {
|
||||||
if (shouldBeAdded) {
|
if (shouldBeAdded) {
|
||||||
// update distance (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
// update distance (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
||||||
if (!resumed) {
|
if (!resumed) {
|
||||||
track.length = track.length + LocationHelper.calculateDistance(previousLocation, location)
|
track.length =
|
||||||
|
track.length + LocationHelper.calculateDistance(previousLocation, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location.altitude != 0.0) {
|
if (location.altitude != 0.0) {
|
||||||
|
@ -105,12 +116,23 @@ object TrackHelper {
|
||||||
track.minAltitude = location.altitude
|
track.minAltitude = location.altitude
|
||||||
} else {
|
} else {
|
||||||
// calculate elevation values (upwards / downwards movements)
|
// calculate elevation values (upwards / downwards movements)
|
||||||
val elevationDifferences: Pair<Double, Double> = LocationHelper.calculateElevationDifferences(previousLocation, location, track)
|
val elevationDifferences: Pair<Double, Double> =
|
||||||
|
LocationHelper.calculateElevationDifferences(
|
||||||
|
previousLocation,
|
||||||
|
location,
|
||||||
|
track
|
||||||
|
)
|
||||||
// check if any differences were calculated
|
// check if any differences were calculated
|
||||||
if (elevationDifferences != Pair(track.positiveElevation, track.negativeElevation)) {
|
if (elevationDifferences != Pair(
|
||||||
|
track.positiveElevation,
|
||||||
|
track.negativeElevation
|
||||||
|
)
|
||||||
|
) {
|
||||||
// update altitude values
|
// update altitude values
|
||||||
if (location.altitude > track.maxAltitude) track.maxAltitude = location.altitude
|
if (location.altitude > track.maxAltitude) track.maxAltitude =
|
||||||
if (location.altitude < track.minAltitude) track.minAltitude = location.altitude
|
location.altitude
|
||||||
|
if (location.altitude < track.minAltitude) track.minAltitude =
|
||||||
|
location.altitude
|
||||||
// update elevation values (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
// update elevation values (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
||||||
if (!resumed) {
|
if (!resumed) {
|
||||||
track.positiveElevation = elevationDifferences.first
|
track.positiveElevation = elevationDifferences.first
|
||||||
|
@ -122,7 +144,8 @@ object TrackHelper {
|
||||||
|
|
||||||
// toggle stop over status, if necessary
|
// toggle stop over status, if necessary
|
||||||
if (track.wayPoints.size < 0) {
|
if (track.wayPoints.size < 0) {
|
||||||
track.wayPoints[track.wayPoints.size - 1].isStopOver = LocationHelper.isStopOver(previousLocation, location)
|
track.wayPoints[track.wayPoints.size - 1].isStopOver =
|
||||||
|
LocationHelper.isStopOver(previousLocation, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
// save number of satellites
|
// save number of satellites
|
||||||
|
@ -139,7 +162,18 @@ object TrackHelper {
|
||||||
track.longitude = location.longitude
|
track.longitude = location.longitude
|
||||||
|
|
||||||
// add location as new waypoint
|
// add location as new waypoint
|
||||||
track.wayPoints.add(WayPoint(provider = location.provider, latitude = location.latitude, longitude = location.longitude, altitude = location.altitude, accuracy = location.accuracy, time = location.time, distanceToStartingPoint = track.length, numberSatellites = numberOfSatellites))
|
track.wayPoints.add(
|
||||||
|
WayPoint(
|
||||||
|
provider = location.provider,
|
||||||
|
latitude = location.latitude,
|
||||||
|
longitude = location.longitude,
|
||||||
|
altitude = location.altitude,
|
||||||
|
accuracy = location.accuracy,
|
||||||
|
time = location.time,
|
||||||
|
distanceToStartingPoint = track.length,
|
||||||
|
numberSatellites = numberOfSatellites
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair(track, shouldBeAdded)
|
return Pair(track, shouldBeAdded)
|
||||||
|
@ -157,9 +191,9 @@ object TrackHelper {
|
||||||
|
|
||||||
// add header
|
// add header
|
||||||
gpxString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" +
|
gpxString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" +
|
||||||
"<gpx version=\"1.1\" creator=\"Trackbook App (Android)\"\n" +
|
"<gpx version=\"1.1\" creator=\"Trackbook App (Android)\"\n" +
|
||||||
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
|
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
|
||||||
" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n"
|
" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n"
|
||||||
|
|
||||||
// add track
|
// add track
|
||||||
gpxString += createGpxTrk(track)
|
gpxString += createGpxTrk(track)
|
||||||
|
@ -227,8 +261,16 @@ object TrackHelper {
|
||||||
if (waypoint.latitude == latitude && waypoint.longitude == longitude) {
|
if (waypoint.latitude == latitude && waypoint.longitude == longitude) {
|
||||||
waypoint.starred = !waypoint.starred
|
waypoint.starred = !waypoint.starred
|
||||||
when (waypoint.starred) {
|
when (waypoint.starred) {
|
||||||
true -> Toast.makeText(context, R.string.toast_message_poi_added, Toast.LENGTH_LONG).show()
|
true -> Toast.makeText(
|
||||||
false -> Toast.makeText(context, R.string.toast_message_poi_removed, Toast.LENGTH_LONG).show()
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,14 @@ object UiHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Sets layout margins for given view in DP */
|
/* Sets layout margins for given view in DP */
|
||||||
fun setViewMargins(context: Context, view: View, left: Int = 0, right: Int = 0, top: Int= 0, bottom: Int = 0) {
|
fun setViewMargins(
|
||||||
|
context: Context,
|
||||||
|
view: View,
|
||||||
|
left: Int = 0,
|
||||||
|
right: Int = 0,
|
||||||
|
top: Int = 0,
|
||||||
|
bottom: Int = 0
|
||||||
|
) {
|
||||||
val scalingFactor: Float = context.resources.displayMetrics.density
|
val scalingFactor: Float = context.resources.displayMetrics.density
|
||||||
val l: Int = (left * scalingFactor).toInt()
|
val l: Int = (left * scalingFactor).toInt()
|
||||||
val r: Int = (right * scalingFactor).toInt()
|
val r: Int = (right * scalingFactor).toInt()
|
||||||
|
@ -56,7 +63,16 @@ object UiHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Sets layout margins for given view in percent */
|
/* Sets layout margins for given view in percent */
|
||||||
fun setViewMarginsPercentage(context: Context, view: View, height: Int, width: Int, left: Int = 0, right: Int = 0, top: Int= 0, bottom: Int = 0) {
|
fun setViewMarginsPercentage(
|
||||||
|
context: Context,
|
||||||
|
view: View,
|
||||||
|
height: Int,
|
||||||
|
width: Int,
|
||||||
|
left: Int = 0,
|
||||||
|
right: Int = 0,
|
||||||
|
top: Int = 0,
|
||||||
|
bottom: Int = 0
|
||||||
|
) {
|
||||||
val l: Int = ((width / 100.0f) * left).toInt()
|
val l: Int = ((width / 100.0f) * left).toInt()
|
||||||
val r: Int = ((width / 100.0f) * right).toInt()
|
val r: Int = ((width / 100.0f) * right).toInt()
|
||||||
val t: Int = ((height / 100.0f) * top).toInt()
|
val t: Int = ((height / 100.0f) * top).toInt()
|
||||||
|
@ -69,28 +85,58 @@ object UiHelper {
|
||||||
* Inner class: Callback that detects a left swipe
|
* Inner class: Callback that detects a left swipe
|
||||||
* Credit: https://github.com/kitek/android-rv-swipe-delete/blob/master/app/src/main/java/pl/kitek/rvswipetodelete/SwipeToDeleteCallback.kt
|
* Credit: https://github.com/kitek/android-rv-swipe-delete/blob/master/app/src/main/java/pl/kitek/rvswipetodelete/SwipeToDeleteCallback.kt
|
||||||
*/
|
*/
|
||||||
abstract class SwipeToDeleteCallback(context: Context): ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
|
abstract class SwipeToDeleteCallback(context: Context) :
|
||||||
|
ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
|
||||||
|
|
||||||
private val deleteIcon = ContextCompat.getDrawable(context, R.drawable.ic_remove_circle_24dp)
|
private val deleteIcon =
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.ic_remove_circle_24dp)
|
||||||
private val intrinsicWidth: Int = deleteIcon?.intrinsicWidth ?: 0
|
private val intrinsicWidth: Int = deleteIcon?.intrinsicWidth ?: 0
|
||||||
private val intrinsicHeight: Int = deleteIcon?.intrinsicHeight ?: 0
|
private val intrinsicHeight: Int = deleteIcon?.intrinsicHeight ?: 0
|
||||||
private val background: ColorDrawable = ColorDrawable()
|
private val background: ColorDrawable = ColorDrawable()
|
||||||
private val backgroundColor = context.resources.getColor(R.color.list_card_delete_background, null)
|
private val backgroundColor =
|
||||||
private val clearPaint: Paint = Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
|
context.resources.getColor(R.color.list_card_delete_background, null)
|
||||||
|
private val clearPaint: Paint =
|
||||||
|
Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
|
||||||
|
|
||||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
override fun onMove(
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
|
target: RecyclerView.ViewHolder
|
||||||
|
): Boolean {
|
||||||
// do nothing
|
// do nothing
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
|
override fun onChildDraw(
|
||||||
|
c: Canvas,
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
|
dX: Float,
|
||||||
|
dY: Float,
|
||||||
|
actionState: Int,
|
||||||
|
isCurrentlyActive: Boolean
|
||||||
|
) {
|
||||||
val itemView = viewHolder.itemView
|
val itemView = viewHolder.itemView
|
||||||
val itemHeight = itemView.bottom - itemView.top
|
val itemHeight = itemView.bottom - itemView.top
|
||||||
val isCanceled = dX == 0f && !isCurrentlyActive
|
val isCanceled = dX == 0f && !isCurrentlyActive
|
||||||
|
|
||||||
if (isCanceled) {
|
if (isCanceled) {
|
||||||
clearCanvas(c, itemView.right + dX, itemView.top.toFloat(), itemView.right.toFloat(), itemView.bottom.toFloat())
|
clearCanvas(
|
||||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
|
c,
|
||||||
|
itemView.right + dX,
|
||||||
|
itemView.top.toFloat(),
|
||||||
|
itemView.right.toFloat(),
|
||||||
|
itemView.bottom.toFloat()
|
||||||
|
)
|
||||||
|
super.onChildDraw(
|
||||||
|
c,
|
||||||
|
recyclerView,
|
||||||
|
viewHolder,
|
||||||
|
dX,
|
||||||
|
dY,
|
||||||
|
actionState,
|
||||||
|
isCurrentlyActive
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,8 @@ import java.util.*
|
||||||
/*
|
/*
|
||||||
* TracklistAdapter class
|
* TracklistAdapter class
|
||||||
*/
|
*/
|
||||||
class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
class TracklistAdapter(private val fragment: Fragment) :
|
||||||
|
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TracklistAdapter::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TracklistAdapter::class.java)
|
||||||
|
@ -54,7 +55,7 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
|
|
||||||
/* Listener Interface */
|
/* Listener Interface */
|
||||||
interface TracklistAdapterListener {
|
interface TracklistAdapterListener {
|
||||||
fun onTrackElementTapped(tracklistElement: TracklistElement) { }
|
fun onTrackElementTapped(tracklistElement: TracklistElement) {}
|
||||||
// fun onTrackElementStarred(trackId: Long, starred: Boolean)
|
// fun onTrackElementStarred(trackId: Long, starred: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
tracklistListener = fragment as TracklistAdapterListener
|
tracklistListener = fragment as TracklistAdapterListener
|
||||||
// load tracklist
|
// load tracklist
|
||||||
tracklist = FileHelper.readTracklist(context)
|
tracklist = FileHelper.readTracklist(context)
|
||||||
tracklist.tracklistElements.sortByDescending { tracklistElement -> tracklistElement.date }
|
tracklist.tracklistElements.sortByDescending { tracklistElement -> tracklistElement.date }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,7 +112,8 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
val backgroundJob = Job()
|
val backgroundJob = Job()
|
||||||
val uiScope = CoroutineScope(Dispatchers.Main + backgroundJob)
|
val uiScope = CoroutineScope(Dispatchers.Main + backgroundJob)
|
||||||
uiScope.launch {
|
uiScope.launch {
|
||||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, position, tracklist) }
|
val deferred: Deferred<Tracklist> =
|
||||||
|
async { FileHelper.deleteTrackSuspended(context, position, tracklist) }
|
||||||
// wait for result and store in tracklist
|
// wait for result and store in tracklist
|
||||||
tracklist = deferred.await()
|
tracklist = deferred.await()
|
||||||
notifyItemRemoved(position)
|
notifyItemRemoved(position)
|
||||||
|
@ -122,7 +124,7 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
|
|
||||||
/* Finds current position of track element in adapter list */
|
/* Finds current position of track element in adapter list */
|
||||||
fun findPosition(trackId: Long): Int {
|
fun findPosition(trackId: Long): Int {
|
||||||
tracklist.tracklistElements.forEachIndexed {index, tracklistElement ->
|
tracklist.tracklistElements.forEachIndexed { index, tracklistElement ->
|
||||||
if (tracklistElement.getTrackId() == trackId) return index
|
if (tracklistElement.getTrackId() == trackId) return index
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
|
@ -143,7 +145,11 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
FileHelper.saveTracklistSuspended(context, tracklist, GregorianCalendar.getInstance().time)
|
FileHelper.saveTracklistSuspended(
|
||||||
|
context,
|
||||||
|
tracklist,
|
||||||
|
GregorianCalendar.getInstance().time
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,9 +160,16 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
val trackDataString: String
|
val trackDataString: String
|
||||||
when (tracklistElement.name == tracklistElement.dateString) {
|
when (tracklistElement.name == tracklistElement.dateString) {
|
||||||
// CASE: no individual name set - exclude date
|
// CASE: no individual name set - exclude date
|
||||||
true -> trackDataString = "${LengthUnitHelper.convertDistanceToString(tracklistElement.length, useImperial)} • ${tracklistElement.durationString}"
|
true -> trackDataString = "${LengthUnitHelper.convertDistanceToString(
|
||||||
|
tracklistElement.length,
|
||||||
|
useImperial
|
||||||
|
)} • ${tracklistElement.durationString}"
|
||||||
// CASE: no individual name set - include date
|
// CASE: no individual name set - include date
|
||||||
false -> trackDataString = "${tracklistElement.dateString} • ${LengthUnitHelper.convertDistanceToString(tracklistElement.length, useImperial)} • ${tracklistElement.durationString}"
|
false -> trackDataString =
|
||||||
|
"${tracklistElement.dateString} • ${LengthUnitHelper.convertDistanceToString(
|
||||||
|
tracklistElement.length,
|
||||||
|
useImperial
|
||||||
|
)} • ${tracklistElement.durationString}"
|
||||||
}
|
}
|
||||||
return trackDataString
|
return trackDataString
|
||||||
}
|
}
|
||||||
|
@ -165,7 +178,8 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
/*
|
/*
|
||||||
* Inner class: DiffUtil.Callback that determines changes in data - improves list performance
|
* Inner class: DiffUtil.Callback that determines changes in data - improves list performance
|
||||||
*/
|
*/
|
||||||
private inner class DiffCallback(val oldList: Tracklist, val newList: Tracklist): DiffUtil.Callback() {
|
private inner class DiffCallback(val oldList: Tracklist, val newList: Tracklist) :
|
||||||
|
DiffUtil.Callback() {
|
||||||
|
|
||||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||||
val oldItem = oldList.tracklistElements[oldItemPosition]
|
val oldItem = oldList.tracklistElements[oldItemPosition]
|
||||||
|
@ -195,7 +209,8 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
||||||
/*
|
/*
|
||||||
* Inner class: ViewHolder for a track element
|
* Inner class: ViewHolder for a track element
|
||||||
*/
|
*/
|
||||||
private inner class TrackElementViewHolder (trackElementLayout: View): RecyclerView.ViewHolder(trackElementLayout) {
|
private inner class TrackElementViewHolder(trackElementLayout: View) :
|
||||||
|
RecyclerView.ViewHolder(trackElementLayout) {
|
||||||
val trackElement: ConstraintLayout = trackElementLayout.findViewById(R.id.track_element)
|
val trackElement: ConstraintLayout = trackElementLayout.findViewById(R.id.track_element)
|
||||||
val trackNameView: TextView = trackElementLayout.findViewById(R.id.track_name)
|
val trackNameView: TextView = trackElementLayout.findViewById(R.id.track_name)
|
||||||
val trackDataView: TextView = trackElementLayout.findViewById(R.id.track_data)
|
val trackDataView: TextView = trackElementLayout.findViewById(R.id.track_data)
|
||||||
|
|
|
@ -51,7 +51,14 @@ import org.y20k.trackbook.helpers.PreferencesHelper
|
||||||
/*
|
/*
|
||||||
* MapFragmentLayoutHolder class
|
* MapFragmentLayoutHolder class
|
||||||
*/
|
*/
|
||||||
data class MapFragmentLayoutHolder(private var context: Context, private var markerListener: MapOverlay.MarkerListener, private var inflater: LayoutInflater, private var container: ViewGroup?, private val startLocation: Location, private val trackingState: Int) {
|
data class MapFragmentLayoutHolder(
|
||||||
|
private var context: Context,
|
||||||
|
private var markerListener: MapOverlay.MarkerListener,
|
||||||
|
private var inflater: LayoutInflater,
|
||||||
|
private var container: ViewGroup?,
|
||||||
|
private val startLocation: Location,
|
||||||
|
private val trackingState: Int
|
||||||
|
) {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(MapFragmentLayoutHolder::class.java)
|
private val TAG: String = LogHelper.makeLogTag(MapFragmentLayoutHolder::class.java)
|
||||||
|
@ -102,14 +109,19 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
||||||
}
|
}
|
||||||
|
|
||||||
// add compass to map
|
// add compass to map
|
||||||
val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), mapView)
|
val compassOverlay =
|
||||||
|
CompassOverlay(context, InternalCompassOrientationProvider(context), mapView)
|
||||||
compassOverlay.enableCompass()
|
compassOverlay.enableCompass()
|
||||||
compassOverlay.setCompassCenter(36f, 60f)
|
compassOverlay.setCompassCenter(36f, 60f)
|
||||||
|
|
||||||
mapView.overlays.add(compassOverlay)
|
mapView.overlays.add(compassOverlay)
|
||||||
|
|
||||||
// add my location overlay
|
// add my location overlay
|
||||||
currentPositionOverlay = MapOverlay(markerListener).createMyLocationOverlay(context, startLocation, trackingState)
|
currentPositionOverlay = MapOverlay(markerListener).createMyLocationOverlay(
|
||||||
|
context,
|
||||||
|
startLocation,
|
||||||
|
trackingState
|
||||||
|
)
|
||||||
mapView.overlays.add(currentPositionOverlay)
|
mapView.overlays.add(currentPositionOverlay)
|
||||||
centerMap(startLocation)
|
centerMap(startLocation)
|
||||||
|
|
||||||
|
@ -157,7 +169,8 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
||||||
/* Mark current position on map */
|
/* Mark current position on map */
|
||||||
fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_NOT) {
|
fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_NOT) {
|
||||||
mapView.overlays.remove(currentPositionOverlay)
|
mapView.overlays.remove(currentPositionOverlay)
|
||||||
currentPositionOverlay = MapOverlay(markerListener).createMyLocationOverlay(context, location, trackingState)
|
currentPositionOverlay =
|
||||||
|
MapOverlay(markerListener).createMyLocationOverlay(context, location, trackingState)
|
||||||
mapView.overlays.add(currentPositionOverlay)
|
mapView.overlays.add(currentPositionOverlay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +181,8 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
||||||
mapView.overlays.remove(currentTrackOverlay)
|
mapView.overlays.remove(currentTrackOverlay)
|
||||||
}
|
}
|
||||||
if (track.wayPoints.isNotEmpty()) {
|
if (track.wayPoints.isNotEmpty()) {
|
||||||
currentTrackOverlay = MapOverlay(markerListener).createTrackOverlay(context, track, trackingState)
|
currentTrackOverlay =
|
||||||
|
MapOverlay(markerListener).createTrackOverlay(context, track, trackingState)
|
||||||
mapView.overlays.add(currentTrackOverlay)
|
mapView.overlays.add(currentTrackOverlay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,10 +215,13 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Toggles content and visibility of the location error snackbar */
|
/* Toggles content and visibility of the location error snackbar */
|
||||||
fun toggleLocationErrorBar(gpsProviderActive: Boolean, networkProviderActive: Boolean) {
|
fun toggleLocationErrorBar(gpsProviderActive: Boolean, networkProviderActive: Boolean) {
|
||||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) {
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
context,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_DENIED
|
||||||
|
) {
|
||||||
// CASE: Location permission not granted
|
// CASE: Location permission not granted
|
||||||
locationErrorBar.setText(R.string.snackbar_message_location_permission_denied)
|
locationErrorBar.setText(R.string.snackbar_message_location_permission_denied)
|
||||||
locationErrorBar.show()
|
locationErrorBar.show()
|
||||||
|
|
|
@ -50,7 +50,13 @@ import kotlin.math.roundToInt
|
||||||
/*
|
/*
|
||||||
* TrackFragmentLayoutHolder class
|
* TrackFragmentLayoutHolder class
|
||||||
*/
|
*/
|
||||||
data class TrackFragmentLayoutHolder(private var context: Context, private var markerListener: MapOverlay.MarkerListener, private var inflater: LayoutInflater, private var container: ViewGroup?, var track: Track) {
|
data class TrackFragmentLayoutHolder(
|
||||||
|
private var context: Context,
|
||||||
|
private var markerListener: MapOverlay.MarkerListener,
|
||||||
|
private var inflater: LayoutInflater,
|
||||||
|
private var container: ViewGroup?,
|
||||||
|
var track: Track
|
||||||
|
) {
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
private val TAG: String = LogHelper.makeLogTag(TrackFragmentLayoutHolder::class.java)
|
private val TAG: String = LogHelper.makeLogTag(TrackFragmentLayoutHolder::class.java)
|
||||||
|
@ -134,13 +140,15 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
}
|
}
|
||||||
|
|
||||||
// add compass to map
|
// add compass to map
|
||||||
val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), mapView)
|
val compassOverlay =
|
||||||
|
CompassOverlay(context, InternalCompassOrientationProvider(context), mapView)
|
||||||
compassOverlay.enableCompass()
|
compassOverlay.enableCompass()
|
||||||
compassOverlay.setCompassCenter(36f, 60f)
|
compassOverlay.setCompassCenter(36f, 60f)
|
||||||
mapView.overlays.add(compassOverlay)
|
mapView.overlays.add(compassOverlay)
|
||||||
|
|
||||||
// create map overlay
|
// create map overlay
|
||||||
trackOverlay = MapOverlay(markerListener).createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT)
|
trackOverlay =
|
||||||
|
MapOverlay(markerListener).createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT)
|
||||||
if (track.wayPoints.isNotEmpty()) {
|
if (track.wayPoints.isNotEmpty()) {
|
||||||
mapView.overlays.add(trackOverlay)
|
mapView.overlays.add(trackOverlay)
|
||||||
}
|
}
|
||||||
|
@ -168,7 +176,11 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
mapView.overlays.remove(trackOverlay)
|
mapView.overlays.remove(trackOverlay)
|
||||||
}
|
}
|
||||||
if (track.wayPoints.isNotEmpty()) {
|
if (track.wayPoints.isNotEmpty()) {
|
||||||
trackOverlay = MapOverlay(markerListener).createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT)
|
trackOverlay = MapOverlay(markerListener).createTrackOverlay(
|
||||||
|
context,
|
||||||
|
track,
|
||||||
|
Keys.STATE_TRACKING_NOT
|
||||||
|
)
|
||||||
mapView.overlays.add(trackOverlay)
|
mapView.overlays.add(trackOverlay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,7 +203,8 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
|
|
||||||
// get step count string
|
// get step count string
|
||||||
val steps: String
|
val steps: String
|
||||||
if (track.stepCount == -1f) steps = context.getString(R.string.statistics_sheet_p_steps_no_pedometer)
|
if (track.stepCount == -1f) steps =
|
||||||
|
context.getString(R.string.statistics_sheet_p_steps_no_pedometer)
|
||||||
else steps = track.stepCount.roundToInt().toString()
|
else steps = track.stepCount.roundToInt().toString()
|
||||||
|
|
||||||
// populate views
|
// populate views
|
||||||
|
@ -200,19 +213,29 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
stepsView.text = steps
|
stepsView.text = steps
|
||||||
waypointsView.text = track.wayPoints.size.toString()
|
waypointsView.text = track.wayPoints.size.toString()
|
||||||
durationView.text = DateTimeHelper.convertToReadableTime(context, track.duration)
|
durationView.text = DateTimeHelper.convertToReadableTime(context, track.duration)
|
||||||
velocityView.text = LengthUnitHelper.convertToVelocityString(track.duration, track.recordingPaused, track.length, useImperialUnits)
|
velocityView.text = LengthUnitHelper.convertToVelocityString(
|
||||||
|
track.duration,
|
||||||
|
track.recordingPaused,
|
||||||
|
track.length,
|
||||||
|
useImperialUnits
|
||||||
|
)
|
||||||
recordingStartView.text = DateTimeHelper.convertToReadableDateAndTime(track.recordingStart)
|
recordingStartView.text = DateTimeHelper.convertToReadableDateAndTime(track.recordingStart)
|
||||||
recordingStopView.text = DateTimeHelper.convertToReadableDateAndTime(track.recordingStart)
|
recordingStopView.text = DateTimeHelper.convertToReadableDateAndTime(track.recordingStart)
|
||||||
maxAltitudeView.text = LengthUnitHelper.convertDistanceToString(track.maxAltitude, useImperialUnits)
|
maxAltitudeView.text =
|
||||||
minAltitudeView.text = LengthUnitHelper.convertDistanceToString(track.minAltitude, useImperialUnits)
|
LengthUnitHelper.convertDistanceToString(track.maxAltitude, useImperialUnits)
|
||||||
positiveElevationView.text = LengthUnitHelper.convertDistanceToString(track.positiveElevation, useImperialUnits)
|
minAltitudeView.text =
|
||||||
negativeElevationView.text = LengthUnitHelper.convertDistanceToString(track.negativeElevation, useImperialUnits)
|
LengthUnitHelper.convertDistanceToString(track.minAltitude, useImperialUnits)
|
||||||
|
positiveElevationView.text =
|
||||||
|
LengthUnitHelper.convertDistanceToString(track.positiveElevation, useImperialUnits)
|
||||||
|
negativeElevationView.text =
|
||||||
|
LengthUnitHelper.convertDistanceToString(track.negativeElevation, useImperialUnits)
|
||||||
|
|
||||||
// show / hide recording pause
|
// show / hide recording pause
|
||||||
if (track.recordingPaused != 0L) {
|
if (track.recordingPaused != 0L) {
|
||||||
recordingPausedLabelView.visibility = View.VISIBLE
|
recordingPausedLabelView.visibility = View.VISIBLE
|
||||||
recordingPausedView.visibility = View.VISIBLE
|
recordingPausedView.visibility = View.VISIBLE
|
||||||
recordingPausedView.text = DateTimeHelper.convertToReadableTime(context, track.recordingPaused)
|
recordingPausedView.text =
|
||||||
|
DateTimeHelper.convertToReadableTime(context, track.recordingPaused)
|
||||||
} else {
|
} else {
|
||||||
recordingPausedLabelView.visibility = View.GONE
|
recordingPausedLabelView.visibility = View.GONE
|
||||||
recordingPausedView.visibility = View.GONE
|
recordingPausedView.visibility = View.GONE
|
||||||
|
@ -220,8 +243,9 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
|
|
||||||
// inform user about possible accuracy issues with altitude measurements
|
// inform user about possible accuracy issues with altitude measurements
|
||||||
elevationDataViews.referencedIds.forEach { id ->
|
elevationDataViews.referencedIds.forEach { id ->
|
||||||
(rootView.findViewById(id) as View).setOnClickListener{
|
(rootView.findViewById(id) as View).setOnClickListener {
|
||||||
Toast.makeText(context, R.string.toast_message_elevation_info, Toast.LENGTH_LONG).show()
|
Toast.makeText(context, R.string.toast_message_elevation_info, Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// make track name on statistics sheet clickable
|
// make track name on statistics sheet clickable
|
||||||
|
@ -234,7 +258,8 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
/* Shows/hides the statistics sheet */
|
/* Shows/hides the statistics sheet */
|
||||||
private fun toggleStatisticsSheetVisibility() {
|
private fun toggleStatisticsSheetVisibility() {
|
||||||
when (statisticsSheetBehavior.state) {
|
when (statisticsSheetBehavior.state) {
|
||||||
BottomSheetBehavior.STATE_EXPANDED -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
BottomSheetBehavior.STATE_EXPANDED -> statisticsSheetBehavior.state =
|
||||||
|
BottomSheetBehavior.STATE_COLLAPSED
|
||||||
else -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
else -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,26 +271,31 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
|
||||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||||
when (newState) {
|
when (newState) {
|
||||||
BottomSheetBehavior.STATE_EXPANDED -> {
|
BottomSheetBehavior.STATE_EXPANDED -> {
|
||||||
statisticsSheet.background = context.getDrawable(R.drawable.shape_statistics_background_expanded)
|
statisticsSheet.background =
|
||||||
|
context.getDrawable(R.drawable.shape_statistics_background_expanded)
|
||||||
trackManagementViews.visibility = View.VISIBLE
|
trackManagementViews.visibility = View.VISIBLE
|
||||||
shareButton.visibility = View.GONE
|
shareButton.visibility = View.GONE
|
||||||
// bottomSheet.setPadding(0,24,0,0)
|
// bottomSheet.setPadding(0,24,0,0)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
statisticsSheet.background = context.getDrawable(R.drawable.shape_statistics_background_collapsed)
|
statisticsSheet.background =
|
||||||
|
context.getDrawable(R.drawable.shape_statistics_background_collapsed)
|
||||||
trackManagementViews.visibility = View.GONE
|
trackManagementViews.visibility = View.GONE
|
||||||
shareButton.visibility = View.VISIBLE
|
shareButton.visibility = View.VISIBLE
|
||||||
// bottomSheet.setPadding(0,0,0,0)
|
// bottomSheet.setPadding(0,0,0,0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||||
if (slideOffset < 0.125f) {
|
if (slideOffset < 0.125f) {
|
||||||
statisticsSheet.background = context.getDrawable(R.drawable.shape_statistics_background_collapsed)
|
statisticsSheet.background =
|
||||||
|
context.getDrawable(R.drawable.shape_statistics_background_collapsed)
|
||||||
trackManagementViews.visibility = View.GONE
|
trackManagementViews.visibility = View.GONE
|
||||||
shareButton.visibility = View.VISIBLE
|
shareButton.visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
statisticsSheet.background = context.getDrawable(R.drawable.shape_statistics_background_expanded)
|
statisticsSheet.background =
|
||||||
|
context.getDrawable(R.drawable.shape_statistics_background_expanded)
|
||||||
trackManagementViews.visibility = View.VISIBLE
|
trackManagementViews.visibility = View.VISIBLE
|
||||||
shareButton.visibility = View.GONE
|
shareButton.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_default"
|
android:fillColor="@color/icon_default"
|
||||||
android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5s-0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM16,12v3c0,0.22 -0.03,0.47 -0.07,0.7l-0.1,0.65 -0.37,0.65c-0.72,1.24 -2.04,2 -3.46,2s-2.74,-0.77 -3.46,-2l-0.37,-0.64 -0.1,-0.65C8.03,15.48 8,15.23 8,15v-4c0,-0.23 0.03,-0.48 0.07,-0.7l0.1,-0.65 0.37,-0.65c0.3,-0.52 0.72,-0.97 1.21,-1.31l0.57,-0.39 0.74,-0.18c0.31,-0.08 0.63,-0.12 0.94,-0.12 0.32,0 0.63,0.04 0.95,0.12l0.68,0.16 0.61,0.42c0.5,0.34 0.91,0.78 1.21,1.31l0.38,0.65 0.1,0.65c0.04,0.22 0.07,0.47 0.07,0.69v1zM10,14h4v2h-4zM10,10h4v2h-4z"/>
|
android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5s-0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM16,12v3c0,0.22 -0.03,0.47 -0.07,0.7l-0.1,0.65 -0.37,0.65c-0.72,1.24 -2.04,2 -3.46,2s-2.74,-0.77 -3.46,-2l-0.37,-0.64 -0.1,-0.65C8.03,15.48 8,15.23 8,15v-4c0,-0.23 0.03,-0.48 0.07,-0.7l0.1,-0.65 0.37,-0.65c0.3,-0.52 0.72,-0.97 1.21,-1.31l0.57,-0.39 0.74,-0.18c0.31,-0.08 0.63,-0.12 0.94,-0.12 0.32,0 0.63,0.04 0.95,0.12l0.68,0.16 0.61,0.42c0.5,0.34 0.91,0.78 1.21,1.31l0.38,0.65 0.1,0.65c0.04,0.22 0.07,0.47 0.07,0.69v1zM10,14h4v2h-4zM10,10h4v2h-4z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"
|
android:fillColor="@color/trackbook_white"
|
||||||
android:fillColor="@color/trackbook_white" />
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_black"
|
android:fillColor="@color/trackbook_black"
|
||||||
android:pathData="M21,3L3,10.53v0.98l6.84,2.65L12.48,21h0.98L21,3z"/>
|
android:pathData="M21,3L3,10.53v0.98l6.84,2.65L12.48,21h0.98L21,3z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/location_button_background"
|
android:fillColor="@color/location_button_background"
|
||||||
android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
|
android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_default"
|
android:fillColor="@color/icon_default"
|
||||||
android:pathData="M16,9v10H8V9h8m-1.5,-6h-5l-1,1H5v2h14V4h-3.5l-1,-1zM18,7H6v12c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7z"/>
|
android:pathData="M16,9v10H8V9h8m-1.5,-6h-5l-1,1H5v2h14V4h-3.5l-1,-1zM18,7H6v12c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_default"
|
android:fillColor="@color/icon_default"
|
||||||
android:pathData="M14.06,9.02l0.92,0.92L5.92,19L5,19v-0.92l9.06,-9.06M17.66,3c-0.25,0 -0.51,0.1 -0.7,0.29l-1.83,1.83 3.75,3.75 1.83,-1.83c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.2,-0.2 -0.45,-0.29 -0.71,-0.29zM14.06,6.19L3,17.25L3,21h3.75L17.81,9.94l-3.75,-3.75z"/>
|
android:pathData="M14.06,9.02l0.92,0.92L5.92,19L5,19v-0.92l9.06,-9.06M17.66,3c-0.25,0 -0.51,0.1 -0.7,0.29l-1.83,1.83 3.75,3.75 1.83,-1.83c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.2,-0.2 -0.45,-0.29 -0.71,-0.29zM14.06,6.19L3,17.25L3,21h3.75L17.81,9.94l-3.75,-3.75z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
|
android:fillColor="#DC2B00"
|
||||||
android:fillColor="#DC2B00"/>
|
android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
|
android:fillColor="@color/trackbook_white"
|
||||||
android:fillColor="@color/trackbook_white" />
|
android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_default"
|
android:fillColor="@color/icon_default"
|
||||||
android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
|
android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_default"
|
android:fillColor="@color/icon_default"
|
||||||
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
|
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:height="24dp"
|
android:width="24dp"
|
||||||
android:viewportHeight="96.0"
|
android:height="24dp"
|
||||||
android:viewportWidth="96.0"
|
android:viewportWidth="96.0"
|
||||||
android:width="24dp">
|
android:viewportHeight="96.0">
|
||||||
<path
|
<path
|
||||||
android:fillAlpha="0.33"
|
android:fillAlpha="0.33"
|
||||||
android:fillColor="@color/trackbook_blue"
|
android:fillColor="@color/trackbook_blue"
|
||||||
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
|
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_blue"
|
android:fillColor="@color/trackbook_blue"
|
||||||
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
|
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:height="24dp"
|
android:width="24dp"
|
||||||
android:viewportHeight="96.0"
|
android:height="24dp"
|
||||||
android:viewportWidth="96.0"
|
android:viewportWidth="96.0"
|
||||||
android:width="24dp">
|
android:viewportHeight="96.0">
|
||||||
<path
|
<path
|
||||||
android:fillAlpha="0.33"
|
android:fillAlpha="0.33"
|
||||||
android:fillColor="@color/trackbook_blue"
|
android:fillColor="@color/trackbook_blue"
|
||||||
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
|
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_grey_light"
|
android:fillColor="@color/trackbook_grey_light"
|
||||||
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
|
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:height="24dp"
|
android:width="24dp"
|
||||||
android:viewportHeight="96.0"
|
android:height="24dp"
|
||||||
android:viewportWidth="96.0"
|
android:viewportWidth="96.0"
|
||||||
android:width="24dp">
|
android:viewportHeight="96.0">
|
||||||
<path
|
<path
|
||||||
android:fillAlpha="0.33"
|
android:fillAlpha="0.33"
|
||||||
android:fillColor="@color/trackbook_red"
|
android:fillColor="@color/trackbook_red"
|
||||||
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
|
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_red"
|
android:fillColor="@color/trackbook_red"
|
||||||
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
|
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:height="24dp"
|
android:width="24dp"
|
||||||
android:viewportHeight="96.0"
|
android:height="24dp"
|
||||||
android:viewportWidth="96.0"
|
android:viewportWidth="96.0"
|
||||||
android:width="24dp">
|
android:viewportHeight="96.0">
|
||||||
<path
|
<path
|
||||||
android:fillAlpha="0.33"
|
android:fillAlpha="0.33"
|
||||||
android:fillColor="@color/trackbook_red"
|
android:fillColor="@color/trackbook_red"
|
||||||
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
|
android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_grey_light"
|
android:fillColor="@color/trackbook_grey_light"
|
||||||
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
|
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:height="24dp"
|
android:width="24dp"
|
||||||
android:viewportHeight="96.0"
|
android:height="24dp"
|
||||||
android:viewportWidth="96.0"
|
android:viewportWidth="96.0"
|
||||||
android:width="24dp">
|
android:viewportHeight="96.0">
|
||||||
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_blue"
|
android:fillColor="@color/trackbook_blue"
|
||||||
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
|
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:height="24dp"
|
android:width="24dp"
|
||||||
android:viewportHeight="96.0"
|
android:height="24dp"
|
||||||
android:viewportWidth="96.0"
|
android:viewportWidth="96.0"
|
||||||
android:width="24dp">
|
android:viewportHeight="96.0">
|
||||||
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_grey_light"
|
android:fillColor="@color/trackbook_grey_light"
|
||||||
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
|
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:height="24dp"
|
android:width="24dp"
|
||||||
android:viewportHeight="96.0"
|
android:height="24dp"
|
||||||
android:viewportWidth="96.0"
|
android:viewportWidth="96.0"
|
||||||
android:width="24dp">
|
android:viewportHeight="96.0">
|
||||||
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_red"
|
android:fillColor="@color/trackbook_red"
|
||||||
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
|
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportHeight="96.0"
|
android:width="24dp"
|
||||||
android:viewportWidth="96.0"
|
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:width="24dp">
|
android:viewportWidth="96.0"
|
||||||
|
android:viewportHeight="96.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_black"
|
android:fillColor="@color/trackbook_black"
|
||||||
android:pathData="M44,12.99L20.69,8.74L5.12,5.89C2.29,5.38 0,7.44 0,10.49v67.38c0,3.06 2.29,5.96 5.12,6.47l15.57,2.85l22.19,4.05L44,91.46V12.99z"/>
|
android:pathData="M44,12.99L20.69,8.74L5.12,5.89C2.29,5.38 0,7.44 0,10.49v67.38c0,3.06 2.29,5.96 5.12,6.47l15.57,2.85l22.19,4.05L44,91.46V12.99z" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_black"
|
android:fillColor="@color/trackbook_black"
|
||||||
android:pathData="M90.88,5.89L75.31,8.74L53.12,12.79L52,12.99v78.46l23.31,-4.26l15.57,-2.85c2.83,-0.52 5.12,-3.41 5.12,-6.47V10.49C96,7.44 93.71,5.38 90.88,5.89z"/>
|
android:pathData="M90.88,5.89L75.31,8.74L53.12,12.79L52,12.99v78.46l23.31,-4.26l15.57,-2.85c2.83,-0.52 5.12,-3.41 5.12,-6.47V10.49C96,7.44 93.71,5.38 90.88,5.89z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="36dp"
|
||||||
android:height="36dp"
|
android:height="36dp"
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:width="36dp">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_black"
|
android:fillColor="@color/trackbook_black"
|
||||||
android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0" />
|
||||||
</vector>
|
</vector>
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="36dp"
|
||||||
android:height="36dp"
|
android:height="36dp"
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:width="36dp">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_black"
|
android:fillColor="@color/trackbook_black"
|
||||||
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
|
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="36dp"
|
||||||
android:height="36dp"
|
android:height="36dp"
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:width="36dp">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_black"
|
android:fillColor="@color/trackbook_black"
|
||||||
android:pathData="M6,6h12v12H6z"/>
|
android:pathData="M6,6h12v12H6z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
android:height="48dp"
|
android:height="48dp"
|
||||||
android:viewportHeight="192.0"
|
|
||||||
android:viewportWidth="192.0"
|
android:viewportWidth="192.0"
|
||||||
android:width="48dp" >
|
android:viewportHeight="192.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_blue"
|
android:fillColor="@color/trackbook_blue"
|
||||||
android:pathData="M96,96m-96,0a96,96 0,1 1,192 0a96,96 0,1 1,-192 0"/>
|
android:pathData="M96,96m-96,0a96,96 0,1 1,192 0a96,96 0,1 1,-192 0" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_red_dark"
|
android:fillColor="@color/trackbook_red_dark"
|
||||||
android:pathData="M48,96a48,46.5 0,1 0,96 0a48,46.5 0,1 0,-96 0z"/>
|
android:pathData="M48,96a48,46.5 0,1 0,96 0a48,46.5 0,1 0,-96 0z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
android:height="48dp"
|
android:height="48dp"
|
||||||
android:viewportHeight="192.0"
|
|
||||||
android:viewportWidth="192.0"
|
android:viewportWidth="192.0"
|
||||||
android:width="48dp" >
|
android:viewportHeight="192.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_blue"
|
android:fillColor="@color/trackbook_blue"
|
||||||
android:pathData="M96,96m-96,0a96,96 0,1 1,192 0a96,96 0,1 1,-192 0"/>
|
android:pathData="M96,96m-96,0a96,96 0,1 1,192 0a96,96 0,1 1,-192 0" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_white"
|
android:fillColor="@color/trackbook_white"
|
||||||
android:pathData="M48,96a48,46.5 0,1 0,96 0a48,46.5 0,1 0,-96 0z"/>
|
android:pathData="M48,96a48,46.5 0,1 0,96 0a48,46.5 0,1 0,-96 0z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportHeight="96.0"
|
android:width="24dp"
|
||||||
android:viewportWidth="96.0"
|
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:width="24dp">
|
android:viewportWidth="96.0"
|
||||||
|
android:viewportHeight="96.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_white"
|
android:fillColor="@color/trackbook_white"
|
||||||
android:pathData="M44,12.99L20.69,8.74L5.12,5.89C2.29,5.38 0,7.44 0,10.49v67.38c0,3.06 2.29,5.96 5.12,6.47l15.57,2.85l22.19,4.05L44,91.46V12.99z"/>
|
android:pathData="M44,12.99L20.69,8.74L5.12,5.89C2.29,5.38 0,7.44 0,10.49v67.38c0,3.06 2.29,5.96 5.12,6.47l15.57,2.85l22.19,4.05L44,91.46V12.99z" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_white"
|
android:fillColor="@color/trackbook_white"
|
||||||
android:pathData="M90.88,5.89L75.31,8.74L53.12,12.79L52,12.99v78.46l23.31,-4.26l15.57,-2.85c2.83,-0.52 5.12,-3.41 5.12,-6.47V10.49C96,7.44 93.71,5.38 90.88,5.89z"/>
|
android:pathData="M90.88,5.89L75.31,8.74L53.12,12.79L52,12.99v78.46l23.31,-4.26l15.57,-2.85c2.83,-0.52 5.12,-3.41 5.12,-6.47V10.49C96,7.44 93.71,5.38 90.88,5.89z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_white"
|
android:fillColor="@color/trackbook_white"
|
||||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13L7,13v-2h10v2z"/>
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13L7,13v-2h10v2z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_default"
|
android:fillColor="@color/icon_default"
|
||||||
android:pathData="M19,12v7L5,19v-7L3,12v7c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2zM13,12.67l2.59,-2.58L17,11.5l-5,5 -5,-5 1.41,-1.41L11,12.67L11,3h2z"/>
|
android:pathData="M19,12v7L5,19v-7L3,12v7c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2zM13,12.67l2.59,-2.58L17,11.5l-5,5 -5,-5 1.41,-1.41L11,12.67L11,3h2z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_white"
|
android:fillColor="@color/trackbook_white"
|
||||||
android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
|
android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_black"
|
android:fillColor="@color/trackbook_black"
|
||||||
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
|
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_default"
|
android:fillColor="@color/icon_default"
|
||||||
android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
|
android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_default"
|
android:fillColor="@color/icon_default"
|
||||||
android:pathData="M17.66,17.66l-1.06,1.06l-0.71,-0.71l1.06,-1.06l-1.94,-1.94l-1.06,1.06l-0.71,-0.71l1.06,-1.06l-1.94,-1.94l-1.06,1.06l-0.71,-0.71l1.06,-1.06L9.7,9.7l-1.06,1.06l-0.71,-0.71l1.06,-1.06L7.05,7.05L5.99,8.11L5.28,7.4l1.06,-1.06L4,4v14c0,1.1 0.9,2 2,2h14L17.66,17.66zM7,17v-5.76L12.76,17H7z"/>
|
android:pathData="M17.66,17.66l-1.06,1.06l-0.71,-0.71l1.06,-1.06l-1.94,-1.94l-1.06,1.06l-0.71,-0.71l1.06,-1.06l-1.94,-1.94l-1.06,1.06l-0.71,-0.71l1.06,-1.06L9.7,9.7l-1.06,1.06l-0.71,-0.71l1.06,-1.06L7.05,7.05L5.99,8.11L5.28,7.4l1.06,-1.06L4,4v14c0,1.1 0.9,2 2,2h14L17.66,17.66zM7,17v-5.76L12.76,17H7z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_blue"
|
android:fillColor="@color/trackbook_blue"
|
||||||
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_star_selected"
|
android:fillColor="@color/icon_star_selected"
|
||||||
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_lightweight"
|
android:fillColor="@color/icon_lightweight"
|
||||||
android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/trackbook_red"
|
android:fillColor="@color/trackbook_red"
|
||||||
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_default"
|
android:fillColor="@color/icon_default"
|
||||||
android:pathData="M21,6L3,6c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,8c0,-1.1 -0.9,-2 -2,-2zM21,16L3,16L3,8h2v4h2L7,8h2v4h2L11,8h2v4h2L15,8h2v4h2L19,8h2v8z"/>
|
android:pathData="M21,6L3,6c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,8c0,-1.1 -0.9,-2 -2,-2zM21,16L3,16L3,8h2v4h2L7,8h2v4h2L11,8h2v4h2L15,8h2v4h2L19,8h2v8z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/icon_default"
|
android:fillColor="@color/icon_default"
|
||||||
android:pathData="M12.5,8c-2.65,0 -5.05,0.99 -6.9,2.6L2,7v9h9l-3.62,-3.62c1.39,-1.16 3.16,-1.88 5.12,-1.88 3.54,0 6.55,2.31 7.6,5.5l2.37,-0.78C21.08,11.03 17.15,8 12.5,8z"/>
|
android:pathData="M12.5,8c-2.65,0 -5.05,0.99 -6.9,2.6L2,7v9h9l-3.62,-3.62c1.39,-1.16 3.16,-1.88 5.12,-1.88 3.54,0 6.55,2.31 7.6,5.5l2.37,-0.78C21.08,11.03 17.15,8 12.5,8z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:state_checked="false" android:color="@color/bottom_navigation_element" />
|
<item android:color="@color/bottom_navigation_element" android:state_checked="false" />
|
||||||
<item android:state_checked="true" android:color="@color/bottom_navigation_element_selected" />
|
<item android:color="@color/bottom_navigation_element_selected" android:state_checked="true" />
|
||||||
</selector>
|
</selector>
|
||||||
|
|
|
@ -2,17 +2,20 @@
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:top="0dp"
|
android:bottom="-2dp"
|
||||||
android:left="-2dp"
|
android:left="-2dp"
|
||||||
android:right="-2dp"
|
android:right="-2dp"
|
||||||
android:bottom="-2dp">
|
android:top="0dp">
|
||||||
|
|
||||||
<shape
|
<shape android:shape="rectangle">
|
||||||
android:shape="rectangle">
|
|
||||||
|
|
||||||
<solid android:color="@color/statistic_sheet_background_collapsed" />
|
<solid android:color="@color/statistic_sheet_background_collapsed" />
|
||||||
<corners android:topLeftRadius="20dp" android:topRightRadius="20dp" />
|
<corners
|
||||||
<stroke android:width="1dp" android:color="@color/statistic_sheet_background_border"/>
|
android:topLeftRadius="20dp"
|
||||||
|
android:topRightRadius="20dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/statistic_sheet_background_border" />
|
||||||
|
|
||||||
</shape>
|
</shape>
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,20 @@
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:top="0dp"
|
android:bottom="-2dp"
|
||||||
android:left="-2dp"
|
android:left="-2dp"
|
||||||
android:right="-2dp"
|
android:right="-2dp"
|
||||||
android:bottom="-2dp">
|
android:top="0dp">
|
||||||
|
|
||||||
<shape
|
<shape android:shape="rectangle">
|
||||||
android:shape="rectangle">
|
|
||||||
|
|
||||||
<solid android:color="@color/statistic_sheet_background_expanded" />
|
<solid android:color="@color/statistic_sheet_background_expanded" />
|
||||||
<corners android:topLeftRadius="20dp" android:topRightRadius="20dp" />
|
<corners
|
||||||
<stroke android:width="1dp" android:color="@color/statistic_sheet_background_border"/>
|
android:topLeftRadius="20dp"
|
||||||
|
android:topRightRadius="20dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/statistic_sheet_background_border" />
|
||||||
|
|
||||||
</shape>
|
</shape>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/map_fragment"
|
android:id="@+id/map_fragment"
|
||||||
|
@ -14,8 +13,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:contentDescription="@string/descr_map_current_track"
|
android:contentDescription="@string/descr_map_current_track"
|
||||||
android:visibility="visible">
|
android:visibility="visible"></org.osmdroid.views.MapView>
|
||||||
</org.osmdroid.views.MapView>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/container"
|
android:id="@+id/container"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -8,26 +7,26 @@
|
||||||
android:background="@color/app_window_background"
|
android:background="@color/app_window_background"
|
||||||
tools:context=".TracklistFragment">
|
tools:context=".TracklistFragment">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/track_element_list"
|
android:id="@+id/track_element_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginTop="28dp"
|
android:layout_marginTop="28dp"
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="4dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<!-- ONBOARDING -->
|
<!-- ONBOARDING -->
|
||||||
<include
|
<include
|
||||||
layout="@layout/track_onboarding"
|
layout="@layout/track_onboarding"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/track_element_list"
|
app:layout_constraintTop_toBottomOf="@+id/track_element_list"
|
||||||
tools:visibility="gone" />
|
tools:visibility="gone" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/track_list_onboarding"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:id="@+id/track_list_onboarding">
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
|
@ -27,7 +27,7 @@
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/settings_fragment"
|
android:id="@+id/settings_fragment"
|
||||||
android:name="org.y20k.trackbook.SettingsFragment"
|
android:name="org.y20k.trackbook.SettingsFragment"
|
||||||
android:label="Settings"/>
|
android:label="Settings" />
|
||||||
|
|
||||||
<!-- TRACKS (LIST OF TRACKS) -->
|
<!-- TRACKS (LIST OF TRACKS) -->
|
||||||
<fragment
|
<fragment
|
||||||
|
@ -46,11 +46,11 @@
|
||||||
android:id="@+id/fragment_track"
|
android:id="@+id/fragment_track"
|
||||||
android:name="org.y20k.trackbook.TrackFragment"
|
android:name="org.y20k.trackbook.TrackFragment"
|
||||||
android:label="Track"
|
android:label="Track"
|
||||||
tools:layout="@layout/fragment_track" >
|
tools:layout="@layout/fragment_track">
|
||||||
<argument
|
<argument
|
||||||
android:name="delete_track_id"
|
android:name="delete_track_id"
|
||||||
app:argType="long"
|
android:defaultValue="-1L"
|
||||||
android:defaultValue="-1L" />
|
app:argType="long" />
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
|
|
|
@ -1,95 +1,95 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<!-- placeholder text - todo remove -->
|
<!-- placeholder text - todo remove -->
|
||||||
<!-- App Name -->
|
<!-- App Name -->
|
||||||
<string name="app_name">Trackbook</string>
|
<string name="app_name">Trackbook</string>
|
||||||
<!-- please do not translate app_name - transcription into different alphabet types is fine though -->
|
<!-- please do not translate app_name - transcription into different alphabet types is fine though -->
|
||||||
<!-- Tabs -->
|
<!-- Tabs -->
|
||||||
<string name="tab_map">地图</string>
|
<string name="tab_map">地图</string>
|
||||||
<string name="tab_tracks">记录</string>
|
<string name="tab_tracks">记录</string>
|
||||||
<string name="tab_settings">设置</string>
|
<string name="tab_settings">设置</string>
|
||||||
<!-- Notification -->
|
<!-- Notification -->
|
||||||
<string name="notification_title_trackbook_running">Trackbook 正在运行</string>
|
<string name="notification_title_trackbook_running">Trackbook 正在运行</string>
|
||||||
<string name="notification_title_trackbook_not_running">Trackbook 已暂停</string>
|
<string name="notification_title_trackbook_not_running">Trackbook 已暂停</string>
|
||||||
<string name="notification_stop">暂停</string>
|
<string name="notification_stop">暂停</string>
|
||||||
<string name="notification_resume">恢复</string>
|
<string name="notification_resume">恢复</string>
|
||||||
<string name="notification_show">显示</string>
|
<string name="notification_show">显示</string>
|
||||||
<string name="notification_channel_recording_name">运动记录状态</string>
|
<string name="notification_channel_recording_name">运动记录状态</string>
|
||||||
<string name="notification_channel_recording_description">显示持续时间和距离.停止移动录制的选项.</string>
|
<string name="notification_channel_recording_description">显示持续时间和距离.停止移动录制的选项.</string>
|
||||||
<!-- Snackbar Messages -->
|
<!-- Snackbar Messages -->
|
||||||
<string name="snackbar_message_location_offline">位置服务已关闭,Trackbook 将不会运行.</string>
|
<string name="snackbar_message_location_offline">位置服务已关闭,Trackbook 将不会运行.</string>
|
||||||
<string name="snackbar_message_location_permission_denied">位置权限未被授予 Trackbook 无法工作.</string>
|
<string name="snackbar_message_location_permission_denied">位置权限未被授予 Trackbook 无法工作.</string>
|
||||||
<!-- FAB Sub Menu -->
|
<!-- FAB Sub Menu -->
|
||||||
<string name="fab_sub_menu_clear">删除</string>
|
<string name="fab_sub_menu_clear">删除</string>
|
||||||
<string name="fab_sub_menu_save">保存</string>
|
<string name="fab_sub_menu_save">保存</string>
|
||||||
<string name="fab_sub_menu_resume">恢复</string>
|
<string name="fab_sub_menu_resume">恢复</string>
|
||||||
<!-- Dialogs -->
|
<!-- Dialogs -->
|
||||||
<string name="dialog_generic_button_cancel">取消</string>
|
<string name="dialog_generic_button_cancel">取消</string>
|
||||||
<string name="dialog_generic_button_okay">OK</string>
|
<string name="dialog_generic_button_okay">OK</string>
|
||||||
<string name="dialog_generic_details_button">显示详情</string>
|
<string name="dialog_generic_details_button">显示详情</string>
|
||||||
<string name="dialog_error_empty_recording_title">无法保存</string>
|
<string name="dialog_error_empty_recording_title">无法保存</string>
|
||||||
<string name="dialog_error_empty_recording_message">Trackbook目前没有记录到任何航点.</string>
|
<string name="dialog_error_empty_recording_message">Trackbook目前没有记录到任何航点.</string>
|
||||||
<string name="dialog_error_empty_recording_action_resume">恢复记录</string>
|
<string name="dialog_error_empty_recording_action_resume">恢复记录</string>
|
||||||
<string name="dialog_rename_track_button">重命名</string>
|
<string name="dialog_rename_track_button">重命名</string>
|
||||||
<string name="dialog_rename_track_input_hint">输入一个新名称</string>
|
<string name="dialog_rename_track_input_hint">输入一个新名称</string>
|
||||||
<string name="dialog_share_gpx">分享GPX文件到</string>
|
<string name="dialog_share_gpx">分享GPX文件到</string>
|
||||||
<string name="dialog_yes_no_positive_button_default">是</string>
|
<string name="dialog_yes_no_positive_button_default">是</string>
|
||||||
<string name="dialog_yes_no_positive_button_delete_recording">删除</string>
|
<string name="dialog_yes_no_positive_button_delete_recording">删除</string>
|
||||||
<string name="dialog_yes_no_message_delete_recording">删除此记录?</string>
|
<string name="dialog_yes_no_message_delete_recording">删除此记录?</string>
|
||||||
<!-- Toast Messages -->
|
<!-- Toast Messages -->
|
||||||
<string name="toast_message_elevation_info">提示:海拔数据的准确性取决于您的设备.测量整个路线的上坡和下坡海拔.</string>
|
<string name="toast_message_elevation_info">提示:海拔数据的准确性取决于您的设备.测量整个路线的上坡和下坡海拔.</string>
|
||||||
<string name="toast_message_install_file_helper">请先安装一个文件管理器或GPX轨迹查看器.</string>
|
<string name="toast_message_install_file_helper">请先安装一个文件管理器或GPX轨迹查看器.</string>
|
||||||
<!-- Map Markers -->
|
<!-- Map Markers -->
|
||||||
<string name="marker_description_time">时间</string>
|
<string name="marker_description_time">时间</string>
|
||||||
<string name="marker_description_accuracy">误差</string>
|
<string name="marker_description_accuracy">误差</string>
|
||||||
<!-- Statistics Sheet -->
|
<!-- Statistics Sheet -->
|
||||||
<string name="statistics_sheet_p_distance">总里程:</string>
|
<string name="statistics_sheet_p_distance">总里程:</string>
|
||||||
<string name="statistics_sheet_p_steps">步数:</string>
|
<string name="statistics_sheet_p_steps">步数:</string>
|
||||||
<string name="statistics_sheet_p_steps_no_pedometer">计步器不可用</string>
|
<string name="statistics_sheet_p_steps_no_pedometer">计步器不可用</string>
|
||||||
<string name="statistics_sheet_p_waypoints">航点数量:</string>
|
<string name="statistics_sheet_p_waypoints">航点数量:</string>
|
||||||
<string name="statistics_sheet_p_duration">用时:</string>
|
<string name="statistics_sheet_p_duration">用时:</string>
|
||||||
<string name="statistics_sheet_p_recording_start">追踪开始:</string>
|
<string name="statistics_sheet_p_recording_start">追踪开始:</string>
|
||||||
<string name="statistics_sheet_p_recording_stop">追踪结束:</string>
|
<string name="statistics_sheet_p_recording_stop">追踪结束:</string>
|
||||||
<string name="statistics_sheet_p_max_altitude">最高航点:</string>
|
<string name="statistics_sheet_p_max_altitude">最高航点:</string>
|
||||||
<string name="statistics_sheet_p_min_altitude">最低航点:</string>
|
<string name="statistics_sheet_p_min_altitude">最低航点:</string>
|
||||||
<string name="statistics_sheet_p_positive_elevation">海拔(上坡):</string>
|
<string name="statistics_sheet_p_positive_elevation">海拔(上坡):</string>
|
||||||
<string name="statistics_sheet_p_negative_elevation">海拔(下坡):</string>
|
<string name="statistics_sheet_p_negative_elevation">海拔(下坡):</string>
|
||||||
<!-- Menu Actions -->
|
<!-- Menu Actions -->
|
||||||
<!-- Onboarding Layout -->
|
<!-- Onboarding Layout -->
|
||||||
<string name="layout_onboarding_description_app_icon">Trackbook 应用图标</string>
|
<string name="layout_onboarding_description_app_icon">Trackbook 应用图标</string>
|
||||||
<!-- Track Tab Onboarding -->
|
<!-- Track Tab Onboarding -->
|
||||||
<string name="track_list_onboarding_h1_part_1">你的运动记录</string>
|
<string name="track_list_onboarding_h1_part_1">你的运动记录</string>
|
||||||
<string name="track_list_onboarding_h1_part_2">... 会显示在这里.</string>
|
<string name="track_list_onboarding_h1_part_2">... 会显示在这里.</string>
|
||||||
<!-- Settings -->
|
<!-- Settings -->
|
||||||
<string name="pref_accuracy_threshold_summary">丢弃误差大于</string>
|
<string name="pref_accuracy_threshold_summary">丢弃误差大于</string>
|
||||||
<string name="pref_accuracy_threshold_title">误差的阈值</string>
|
<string name="pref_accuracy_threshold_title">误差的阈值</string>
|
||||||
<string name="pref_advanced_title">高级</string>
|
<string name="pref_advanced_title">高级</string>
|
||||||
<string name="pref_general_title">一般</string>
|
<string name="pref_general_title">一般</string>
|
||||||
<string name="pref_gps_only_title">仅限于GPS</string>
|
<string name="pref_gps_only_title">仅限于GPS</string>
|
||||||
<string name="pref_gps_only_summary_gps_and_network">当前使用GPS和网络进行定位.</string>
|
<string name="pref_gps_only_summary_gps_and_network">当前使用GPS和网络进行定位.</string>
|
||||||
<string name="pref_gps_only_summary_gps_only">当前使用GPS进行定位.</string>
|
<string name="pref_gps_only_summary_gps_only">当前使用GPS进行定位.</string>
|
||||||
<string name="pref_imperial_measurement_units_summary_metric">当前使用公制单位(千米,米).</string>
|
<string name="pref_imperial_measurement_units_summary_metric">当前使用公制单位(千米,米).</string>
|
||||||
<string name="pref_imperial_measurement_units_summary_imperial">当前使用英制单位(英里,英尺).</string>
|
<string name="pref_imperial_measurement_units_summary_imperial">当前使用英制单位(英里,英尺).</string>
|
||||||
<string name="pref_imperial_measurement_units_title">使用英制</string>
|
<string name="pref_imperial_measurement_units_title">使用英制</string>
|
||||||
<string name="pref_reset_advanced_summary">高级设置重置为默认值.</string>
|
<string name="pref_reset_advanced_summary">高级设置重置为默认值.</string>
|
||||||
<string name="pref_reset_advanced_title">重置</string>
|
<string name="pref_reset_advanced_title">重置</string>
|
||||||
<!-- Abbreviations -->
|
<!-- Abbreviations -->
|
||||||
<string name="abbreviation_hours">时</string>
|
<string name="abbreviation_hours">时</string>
|
||||||
<string name="abbreviation_minutes">分</string>
|
<string name="abbreviation_minutes">分</string>
|
||||||
<string name="abbreviation_seconds">秒</string>
|
<string name="abbreviation_seconds">秒</string>
|
||||||
<!-- Descriptions -->
|
<!-- Descriptions -->
|
||||||
<string name="descr_map_current_track">航迹图</string>
|
<string name="descr_map_current_track">航迹图</string>
|
||||||
<string name="descr_map_last_track">最后一个轨迹的航迹图</string>
|
<string name="descr_map_last_track">最后一个轨迹的航迹图</string>
|
||||||
<string name="descr_fab_main_start">开始录制按钮</string>
|
<string name="descr_fab_main_start">开始录制按钮</string>
|
||||||
<string name="descr_fab_sub_menu_button_save">保存按钮</string>
|
<string name="descr_fab_sub_menu_button_save">保存按钮</string>
|
||||||
<string name="descr_fab_sub_menu_button_clear">删除按钮</string>
|
<string name="descr_fab_sub_menu_button_clear">删除按钮</string>
|
||||||
<string name="descr_fab_sub_menu_button_resume">恢复按钮</string>
|
<string name="descr_fab_sub_menu_button_resume">恢复按钮</string>
|
||||||
<string name="descr_mark_starred_button">加星按钮</string>
|
<string name="descr_mark_starred_button">加星按钮</string>
|
||||||
<string name="descr_statistics_sheet_delete_button">删除记录按钮</string>
|
<string name="descr_statistics_sheet_delete_button">删除记录按钮</string>
|
||||||
<string name="descr_statistics_sheet_edit_button">记录编辑按钮</string>
|
<string name="descr_statistics_sheet_edit_button">记录编辑按钮</string>
|
||||||
<string name="descr_statistics_sheet_save_button">以GPX文件分享按钮</string>
|
<string name="descr_statistics_sheet_save_button">以GPX文件分享按钮</string>
|
||||||
<!-- Sample Text -->
|
<!-- Sample Text -->
|
||||||
<string name="sample_text_track_data" translatable="false">23.0 km • 5 时 23 分 42 秒</string>
|
<string name="sample_text_track_data" translatable="false">23.0 km • 5 时 23 分 42 秒</string>
|
||||||
<string name="sample_text_track_name" translatable="false">1969年7月20日</string>
|
<string name="sample_text_track_name" translatable="false">1969年7月20日</string>
|
||||||
<string name="sample_text_default_data" translatable="false">轨迹数据丢失</string>
|
<string name="sample_text_default_data" translatable="false">轨迹数据丢失</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -31,17 +31,21 @@
|
||||||
|
|
||||||
|
|
||||||
<!-- COLOR NAMES -->
|
<!-- COLOR NAMES -->
|
||||||
<color name="trackbook_grey">#FF595959</color> <!-- derived from recommended dark theme surface color -->
|
|
||||||
|
<!-- derived from recommended dark theme surface color -->
|
||||||
|
<color name="trackbook_grey">#FF595959</color>
|
||||||
<color name="trackbook_grey_light">#FF7D7D7D</color>
|
<color name="trackbook_grey_light">#FF7D7D7D</color>
|
||||||
<color name="trackbook_grey_lighter">#FFDADADA</color>
|
<color name="trackbook_grey_lighter">#FFDADADA</color>
|
||||||
<color name="trackbook_grey_very_light">#FFF2F2F2</color>
|
<color name="trackbook_grey_very_light">#FFF2F2F2</color>
|
||||||
<color name="trackbook_grey_dark">#FF414141</color>
|
<color name="trackbook_grey_dark">#FF414141</color>
|
||||||
<color name="trackbook_grey_darker">#FF2D2D2D</color>
|
<color name="trackbook_grey_darker">#FF2D2D2D</color>
|
||||||
|
|
||||||
<color name="trackbook_red">#DC3D33</color> <!-- Slightly muted variant of -> Material Design 2: Red 600 -->
|
<!-- Slightly muted variant of -> Material Design 2: Red 600 -->
|
||||||
|
<color name="trackbook_red">#DC3D33</color>
|
||||||
<color name="trackbook_red_dark">#FFCA2D23</color>
|
<color name="trackbook_red_dark">#FFCA2D23</color>
|
||||||
|
|
||||||
<color name="trackbook_black">#FF121212</color> <!-- Material Design recommended dark theme surface color -->
|
<!-- Material Design recommended dark theme surface color -->
|
||||||
|
<color name="trackbook_black">#FF121212</color>
|
||||||
<color name="trackbook_white">#FFFFFFFF</color>
|
<color name="trackbook_white">#FFFFFFFF</color>
|
||||||
<color name="trackbook_transparent">#00ffffff</color>
|
<color name="trackbook_transparent">#00ffffff</color>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
<item name="custom_match_parent" type="dimen">-1</item> <!-- CUSTOM MATCH_PARENT - see https://developer.android.com/reference/android/view/ViewGroup.LayoutParams.html#MATCH_PARENT -->
|
|
||||||
|
<!-- CUSTOM MATCH_PARENT - see https://developer.android.com/reference/android/view/ViewGroup.LayoutParams.html#MATCH_PARENT -->
|
||||||
|
<item name="custom_match_parent" type="dimen">-1</item>
|
||||||
|
|
||||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
<dimen name="bottom_sheet_width">@dimen/custom_match_parent</dimen>
|
<dimen name="bottom_sheet_width">@dimen/custom_match_parent</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -20,9 +20,11 @@
|
||||||
<item name="buttonBarNegativeButtonStyle">@style/NegativeButtonStyle</item>
|
<item name="buttonBarNegativeButtonStyle">@style/NegativeButtonStyle</item>
|
||||||
<item name="buttonBarPositiveButtonStyle">@style/PositiveButtonStyle</item>
|
<item name="buttonBarPositiveButtonStyle">@style/PositiveButtonStyle</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="NegativeButtonStyle" parent="Widget.MaterialComponents.Button.TextButton.Dialog">
|
<style name="NegativeButtonStyle" parent="Widget.MaterialComponents.Button.TextButton.Dialog">
|
||||||
<item name="android:textColor">@color/text_default</item>
|
<item name="android:textColor">@color/text_default</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="PositiveButtonStyle" parent="Widget.MaterialComponents.Button.TextButton.Dialog">
|
<style name="PositiveButtonStyle" parent="Widget.MaterialComponents.Button.TextButton.Dialog">
|
||||||
<item name="android:textColor">@color/text_default</item>
|
<item name="android:textColor">@color/text_default</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths>
|
<paths>
|
||||||
<external-path name="external_files" path="."/>
|
<external-path
|
||||||
|
name="external_files"
|
||||||
|
path="." />
|
||||||
</paths>
|
</paths>
|
Loading…
Reference in New Issue