removes the Smothing slider (see #99) & added a "Total Distance Recorded" info at the top of the list of recordings
This commit is contained in:
parent
a9ecdcc3ab
commit
d22638da92
17 changed files with 213 additions and 109 deletions
|
@ -58,18 +58,18 @@ dependencies {
|
|||
|
||||
// AndroidX
|
||||
def navigationVersion = "2.3.5"
|
||||
implementation "androidx.activity:activity-ktx:1.2.3"
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'androidx.core:core-ktx:1.5.0'
|
||||
implementation "androidx.activity:activity-ktx:1.3.1"
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.6.0'
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion"
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
|
||||
// Gson
|
||||
implementation 'com.google.code.gson:gson:2.8.7'
|
||||
implementation 'com.google.code.gson:gson:2.8.8'
|
||||
|
||||
// OpenStreetMap
|
||||
implementation 'org.osmdroid:osmdroid-android:6.1.10'
|
||||
implementation 'org.osmdroid:osmdroid-android:6.1.11'
|
||||
}
|
||||
|
|
|
@ -91,6 +91,9 @@ object Keys {
|
|||
const val TEMP_FILE: String = "temp.json"
|
||||
const val TRACKLIST_FILE: String = "tracklist.json"
|
||||
|
||||
// view types
|
||||
const val VIEW_TYPE_STATISTICS: Int = 1
|
||||
const val VIEW_TYPE_TRACK: Int = 2
|
||||
|
||||
// default values
|
||||
val DEFAULT_DATE: Date = Date(0L)
|
||||
|
@ -108,7 +111,7 @@ object Keys {
|
|||
const val DEFAULT_ACCURACY: Float = 300f // in meters
|
||||
const val DEFAULT_ALTITUDE: Double = 0.0
|
||||
const val DEFAULT_TIME: Long = 0L
|
||||
const val DEFAULT_ALTITUDE_SMOOTHING_VALUE: Int = 10
|
||||
const val DEFAULT_ALTITUDE_SMOOTHING_VALUE: Int = 13
|
||||
const val DEFAULT_THRESHOLD_LOCATION_ACCURACY: Int = 30 // 30 meters
|
||||
const val DEFAULT_THRESHOLD_LOCATION_AGE: Long = 60000000000L // one minute in nanoseconds
|
||||
const val DEFAULT_THRESHOLD_DISTANCE: Float = 15f // 15 meters
|
||||
|
|
|
@ -22,10 +22,7 @@ import android.Manifest
|
|||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Location
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.IBinder
|
||||
import android.os.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -55,7 +52,7 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlayHelpe
|
|||
|
||||
/* Main class variables */
|
||||
private var bound: Boolean = false
|
||||
private val handler: Handler = Handler()
|
||||
private val handler: Handler = Handler(Looper.getMainLooper())
|
||||
private var trackingState: Int = Keys.STATE_TRACKING_NOT
|
||||
private var gpsProviderActive: Boolean = false
|
||||
private var networkProviderActive: Boolean = false
|
||||
|
|
|
@ -116,15 +116,15 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
preferenceRecordingAccuracy.setDefaultValue(false)
|
||||
|
||||
// set up "Altitude Smoothing" preference
|
||||
val preferenceAltitudeSmoothingValue: SeekBarPreference = SeekBarPreference(activity as Context)
|
||||
preferenceAltitudeSmoothingValue.title = getString(R.string.pref_altitude_smoothing_value_title)
|
||||
preferenceAltitudeSmoothingValue.setIcon(R.drawable.ic_bar_chart_24)
|
||||
preferenceAltitudeSmoothingValue.key = Keys.PREF_ALTITUDE_SMOOTHING_VALUE
|
||||
preferenceAltitudeSmoothingValue.summary = getString(R.string.pref_altitude_smoothing_value_summary)
|
||||
preferenceAltitudeSmoothingValue.showSeekBarValue = true
|
||||
preferenceAltitudeSmoothingValue.min = Keys.MIN_NUMBER_OF_WAYPOINTS_FOR_ELEVATION_CALCULATION
|
||||
preferenceAltitudeSmoothingValue.max = Keys.MAX_NUMBER_OF_WAYPOINTS_FOR_ELEVATION_CALCULATION
|
||||
preferenceAltitudeSmoothingValue.setDefaultValue(Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE)
|
||||
// val preferenceAltitudeSmoothingValue: SeekBarPreference = SeekBarPreference(activity as Context)
|
||||
// preferenceAltitudeSmoothingValue.title = getString(R.string.pref_altitude_smoothing_value_title)
|
||||
// preferenceAltitudeSmoothingValue.setIcon(R.drawable.ic_bar_chart_24)
|
||||
// preferenceAltitudeSmoothingValue.key = Keys.PREF_ALTITUDE_SMOOTHING_VALUE
|
||||
// preferenceAltitudeSmoothingValue.summary = getString(R.string.pref_altitude_smoothing_value_summary)
|
||||
// preferenceAltitudeSmoothingValue.showSeekBarValue = true
|
||||
// preferenceAltitudeSmoothingValue.min = Keys.MIN_NUMBER_OF_WAYPOINTS_FOR_ELEVATION_CALCULATION
|
||||
// preferenceAltitudeSmoothingValue.max = Keys.MAX_NUMBER_OF_WAYPOINTS_FOR_ELEVATION_CALCULATION
|
||||
// preferenceAltitudeSmoothingValue.setDefaultValue(Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE)
|
||||
|
||||
|
||||
// set up "Reset" preference
|
||||
|
@ -135,7 +135,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
preferenceResetAdvanced.setOnPreferenceClickListener{
|
||||
// reset "Recording Accuracy" preference
|
||||
preferenceRecordingAccuracy.isChecked = false
|
||||
preferenceAltitudeSmoothingValue.value = Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE
|
||||
// preferenceAltitudeSmoothingValue.value = Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
val preferenceCategoryAdvanced: PreferenceCategory = PreferenceCategory(activity as Context)
|
||||
preferenceCategoryAdvanced.title = getString(R.string.pref_advanced_title)
|
||||
preferenceCategoryAdvanced.contains(preferenceRecordingAccuracy)
|
||||
preferenceCategoryAdvanced.contains(preferenceAltitudeSmoothingValue)
|
||||
// preferenceCategoryAdvanced.contains(preferenceAltitudeSmoothingValue)
|
||||
preferenceCategoryAdvanced.contains(preferenceResetAdvanced)
|
||||
|
||||
val preferenceCategoryAbout: PreferenceCategory = PreferenceCategory(context)
|
||||
|
@ -198,7 +198,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||
screen.addPreference(preferenceDeleteNonStarred)
|
||||
screen.addPreference(preferenceCategoryAdvanced)
|
||||
screen.addPreference(preferenceRecordingAccuracy)
|
||||
screen.addPreference(preferenceAltitudeSmoothingValue)
|
||||
// screen.addPreference(preferenceAltitudeSmoothingValue)
|
||||
screen.addPreference(preferenceResetAdvanced)
|
||||
screen.addPreference(preferenceCategoryAbout)
|
||||
screen.addPreference(preferenceAppVersion)
|
||||
|
|
|
@ -32,10 +32,7 @@ import android.hardware.SensorManager
|
|||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import android.os.Binder
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.IBinder
|
||||
import android.os.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
|
@ -69,7 +66,7 @@ class TrackerService: Service(), SensorEventListener {
|
|||
var networkLocationListenerRegistered: Boolean = false
|
||||
var bound: Boolean = false
|
||||
private val binder = LocalBinder()
|
||||
private val handler: Handler = Handler()
|
||||
private val handler: Handler = Handler(Looper.getMainLooper())
|
||||
private var altitudeValues: SimpleMovingAverageQueue = SimpleMovingAverageQueue(Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE)
|
||||
private lateinit var locationManager: LocationManager
|
||||
private lateinit var sensorManager: SensorManager
|
||||
|
@ -97,7 +94,7 @@ class TrackerService: Service(), SensorEventListener {
|
|||
trackingState = PreferencesHelper.loadTrackingState()
|
||||
currentBestLocation = LocationHelper.getLastKnownLocation(this)
|
||||
track = FileHelper.readTrack(this, FileHelper.getTempFileUri(this))
|
||||
altitudeValues.capacity = PreferencesHelper.loadAltitudeSmoothingValue()
|
||||
// altitudeValues.capacity = PreferencesHelper.loadAltitudeSmoothingValue()
|
||||
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||
}
|
||||
|
||||
|
@ -577,16 +574,16 @@ class TrackerService: Service(), SensorEventListener {
|
|||
}
|
||||
|
||||
|
||||
// TODO remove
|
||||
val testAltitudes: Array<Double> = arrayOf(352.4349365234375, 358.883544921875, 358.6827392578125, 357.31396484375, 354.27459716796875, 354.573486328125, 354.388916015625, 354.6697998046875, 356.534912109375, 355.2772216796875, 356.21246337890625, 352.3499755859375, 350.37646484375, 351.2098388671875, 350.5213623046875, 350.5145263671875, 350.1728515625, 350.9075927734375, 351.5965576171875, 349.55767822265625, 351.548583984375, 357.1195068359375, 362.18634033203125, 366.3153076171875, 366.2218017578125, 362.1046142578125, 357.48291015625, 356.78570556640625, 353.7734375, 352.53936767578125, 351.8125, 353.1099853515625, 354.93035888671875, 355.4337158203125, 354.83270263671875, 352.9859619140625, 352.3006591796875, 351.63470458984375, 350.2501220703125, 351.75726318359375, 350.87664794921875, 350.4185791015625, 350.51568603515625, 349.5537109375, 345.2874755859375, 345.57196044921875, 349.99658203125, 353.3822021484375, 355.19061279296875, 359.1099853515625, 361.74365234375, 363.313232421875, 362.026611328125, 363.20703125, 363.2508544921875, 362.5870361328125, 362.521240234375)
|
||||
var testCounter: Int = 0
|
||||
fun getTestAltitude(): Double {
|
||||
if (testCounter >= testAltitudes.size) testCounter = 0
|
||||
val testAltitude: Double = testAltitudes[testCounter]
|
||||
testCounter ++
|
||||
return testAltitude
|
||||
}
|
||||
// TODO remove
|
||||
// // TODO remove
|
||||
// val testAltitudes: Array<Double> = arrayOf(352.4349365234375, 358.883544921875, 358.6827392578125, 357.31396484375, 354.27459716796875, 354.573486328125, 354.388916015625, 354.6697998046875, 356.534912109375, 355.2772216796875, 356.21246337890625, 352.3499755859375, 350.37646484375, 351.2098388671875, 350.5213623046875, 350.5145263671875, 350.1728515625, 350.9075927734375, 351.5965576171875, 349.55767822265625, 351.548583984375, 357.1195068359375, 362.18634033203125, 366.3153076171875, 366.2218017578125, 362.1046142578125, 357.48291015625, 356.78570556640625, 353.7734375, 352.53936767578125, 351.8125, 353.1099853515625, 354.93035888671875, 355.4337158203125, 354.83270263671875, 352.9859619140625, 352.3006591796875, 351.63470458984375, 350.2501220703125, 351.75726318359375, 350.87664794921875, 350.4185791015625, 350.51568603515625, 349.5537109375, 345.2874755859375, 345.57196044921875, 349.99658203125, 353.3822021484375, 355.19061279296875, 359.1099853515625, 361.74365234375, 363.313232421875, 362.026611328125, 363.20703125, 363.2508544921875, 362.5870361328125, 362.521240234375)
|
||||
// var testCounter: Int = 0
|
||||
// fun getTestAltitude(): Double {
|
||||
// if (testCounter >= testAltitudes.size) testCounter = 0
|
||||
// val testAltitude: Double = testAltitudes[testCounter]
|
||||
// testCounter ++
|
||||
// return testAltitude
|
||||
// }
|
||||
// // TODO remove
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
|||
val swipeHandler = object : UiHelper.SwipeToDeleteCallback(activity as Context) {
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
// ask user
|
||||
val adapterPosition: Int = viewHolder.adapterPosition
|
||||
val adapterPosition: Int = viewHolder.adapterPosition // first position in list is reserved for statistics
|
||||
val dialogMessage: String = "${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)
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
|||
itemTouchHelper.attachToRecyclerView(rootView.findViewById(R.id.track_element_list))
|
||||
|
||||
// toggle onboarding layout
|
||||
toggleOnboardingLayout(tracklistAdapter.itemCount)
|
||||
toggleOnboardingLayout()
|
||||
|
||||
return rootView
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
|||
when (dialogResult) {
|
||||
// user tapped remove track
|
||||
true -> {
|
||||
toggleOnboardingLayout(tracklistAdapter.itemCount -1)
|
||||
toggleOnboardingLayout()
|
||||
tracklistAdapter.removeTrackAtPosition(activity as Context, payload)
|
||||
}
|
||||
// user tapped cancel
|
||||
|
@ -132,8 +132,8 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
|||
|
||||
|
||||
// toggle onboarding layout
|
||||
private fun toggleOnboardingLayout(trackCount: Int) {
|
||||
when (trackCount == 0) {
|
||||
private fun toggleOnboardingLayout() {
|
||||
when (tracklistAdapter.isEmpty()) {
|
||||
true -> {
|
||||
// show onboarding layout
|
||||
tracklistOnboarding.visibility = View.VISIBLE
|
||||
|
@ -166,7 +166,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
|||
if (deleteTrackId != -1L) {
|
||||
CoroutineScope(Main). launch {
|
||||
tracklistAdapter.removeTrackById(this@TracklistFragment.activity as Context, deleteTrackId)
|
||||
toggleOnboardingLayout(tracklistAdapter.itemCount - 1)
|
||||
toggleOnboardingLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ class YesNoDialog (private var yesNoDialogListener: YesNoDialogListener) {
|
|||
}
|
||||
|
||||
// handle outside-click as "no"
|
||||
builder.setOnCancelListener{
|
||||
builder.setOnCancelListener {
|
||||
yesNoDialogListener.onYesNoDialog(type, false, payload, payloadString)
|
||||
}
|
||||
|
||||
|
|
|
@ -182,9 +182,9 @@ object FileHelper {
|
|||
val tracklist: Tracklist = readTracklist(context)
|
||||
tracklist.tracklistElements.add(track.toTracklistElement(context))
|
||||
tracklist.totalDistanceAll += track.length
|
||||
tracklist.totalDurationAll += track.duration
|
||||
tracklist.totalRecordingPausedAll += track.recordingPaused
|
||||
tracklist.totalStepCountAll += track.stepCount
|
||||
// tracklist.totalDurationAll += track.duration // note: TracklistElement does not contain duration
|
||||
// tracklist.totalRecordingPausedAll += track.recordingPaused // note: TracklistElement does not contain recordingPaused
|
||||
// tracklist.totalStepCountAll += track.stepCount // note: TracklistElement does not contain stepCount
|
||||
cont.resume(saveTracklist(context, tracklist, modificationDate))
|
||||
}
|
||||
}
|
||||
|
@ -340,6 +340,8 @@ object FileHelper {
|
|||
// delete track files
|
||||
tracklistElement.trackUriString.toUri().toFile().delete()
|
||||
tracklistElement.gpxUriString.toUri().toFile().delete()
|
||||
// subtract track length from total distance
|
||||
tracklist.totalDistanceAll -= tracklistElement.length
|
||||
}
|
||||
tracklist.tracklistElements.removeAll{ tracklistElements.contains(it) }
|
||||
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
||||
|
@ -353,6 +355,8 @@ object FileHelper {
|
|||
// delete track files
|
||||
tracklistElement.trackUriString.toUri().toFile().delete()
|
||||
tracklistElement.gpxUriString.toUri().toFile().delete()
|
||||
// subtract track length from total distance
|
||||
tracklist.totalDistanceAll -= tracklistElement.length
|
||||
// remove track element from list
|
||||
tracklist.tracklistElements.removeIf { TrackHelper.getTrackId(it) == TrackHelper.getTrackId(tracklistElement) }
|
||||
saveTracklist(context, tracklist, GregorianCalendar.getInstance().time)
|
||||
|
|
|
@ -87,19 +87,19 @@ object PreferencesHelper {
|
|||
return sharedPreferences.getBoolean(Keys.PREF_GPS_ONLY, false)
|
||||
}
|
||||
|
||||
/* Loads accuracy threshold used to determine if location is good enough */
|
||||
fun loadAccuracyThreshold(): Int {
|
||||
// load tracking state
|
||||
return sharedPreferences.getInt(Keys.PREF_LOCATION_ACCURACY_THRESHOLD, Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY)
|
||||
}
|
||||
// /* Loads accuracy threshold used to determine if location is good enough */
|
||||
// fun loadAccuracyThreshold(): Int {
|
||||
// // load tracking state
|
||||
// return sharedPreferences.getInt(Keys.PREF_LOCATION_ACCURACY_THRESHOLD, Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY)
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/* Loads state of recording accuracy */
|
||||
fun loadRecordingAccuracyHigh(): Boolean {
|
||||
// load current setting
|
||||
return sharedPreferences.getBoolean(Keys.PREF_RECORDING_ACCURACY_HIGH, false)
|
||||
}
|
||||
// /* Loads state of recording accuracy */
|
||||
// fun loadRecordingAccuracyHigh(): Boolean {
|
||||
// // load current setting
|
||||
// return sharedPreferences.getBoolean(Keys.PREF_RECORDING_ACCURACY_HIGH, false)
|
||||
// }
|
||||
|
||||
|
||||
/* Loads current accuracy multiplier */
|
||||
|
@ -111,11 +111,11 @@ object PreferencesHelper {
|
|||
}
|
||||
|
||||
|
||||
/* Load altitude smoothing value */
|
||||
fun loadAltitudeSmoothingValue(): Int {
|
||||
// load current setting
|
||||
return sharedPreferences.getInt(Keys.PREF_ALTITUDE_SMOOTHING_VALUE, Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE)
|
||||
}
|
||||
// /* Load altitude smoothing value */
|
||||
// fun loadAltitudeSmoothingValue(): Int {
|
||||
// // load current setting
|
||||
// return sharedPreferences.getInt(Keys.PREF_ALTITUDE_SMOOTHING_VALUE, Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE)
|
||||
// }
|
||||
|
||||
|
||||
/* Loads the state of a map */
|
||||
|
|
|
@ -44,13 +44,11 @@ object TrackHelper {
|
|||
|
||||
|
||||
/* Returns unique ID for Track - currently the start date */
|
||||
fun getTrackId(track: Track): Long =
|
||||
track.recordingStart.time
|
||||
fun getTrackId(track: Track): Long = track.recordingStart.time
|
||||
|
||||
|
||||
/* Returns unique ID for TracklistElement - currently the start date */
|
||||
fun getTrackId(tracklistElement: TracklistElement): Long =
|
||||
tracklistElement.date.time
|
||||
fun getTrackId(tracklistElement: TracklistElement): Long = tracklistElement.date.time
|
||||
|
||||
|
||||
/* Adds given locatiom as waypoint to track */
|
||||
|
@ -258,20 +256,20 @@ object TrackHelper {
|
|||
fun calculateAndSaveTrackTotals(context: Context, tracklist: Tracklist) {
|
||||
CoroutineScope(IO).launch {
|
||||
var totalDistanceAll: Float = 0f
|
||||
var totalDurationAll: Long = 0L
|
||||
var totalRecordingPausedAll: Long = 0L
|
||||
var totalStepCountAll: Float = 0f
|
||||
// var totalDurationAll: Long = 0L
|
||||
// var totalRecordingPausedAll: Long = 0L
|
||||
// var totalStepCountAll: Float = 0f
|
||||
tracklist.tracklistElements.forEach { tracklistElement ->
|
||||
val track: Track = FileHelper.readTrack(context, tracklistElement.trackUriString.toUri())
|
||||
totalDistanceAll += track.length
|
||||
totalDurationAll += track.duration
|
||||
totalRecordingPausedAll += track.recordingPaused
|
||||
totalStepCountAll += track.stepCount
|
||||
// totalDurationAll += track.duration
|
||||
// totalRecordingPausedAll += track.recordingPaused
|
||||
// totalStepCountAll += track.stepCount
|
||||
}
|
||||
tracklist.totalDistanceAll = totalDistanceAll
|
||||
tracklist.totalDurationAll = totalDurationAll
|
||||
tracklist.totalRecordingPausedAll = totalRecordingPausedAll
|
||||
tracklist.totalStepCountAll = totalStepCountAll
|
||||
// tracklist.totalDurationAll = totalDurationAll
|
||||
// tracklist.totalRecordingPausedAll = totalRecordingPausedAll
|
||||
// tracklist.totalStepCountAll = totalStepCountAll
|
||||
FileHelper.saveTracklistSuspended(context, tracklist, GregorianCalendar.getInstance().time)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.tracklist.TracklistAdapter
|
||||
|
||||
|
||||
/*
|
||||
|
@ -101,6 +102,12 @@ object UiHelper {
|
|||
return false
|
||||
}
|
||||
|
||||
override fun getSwipeDirs(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||
// disable swipe for statistics element
|
||||
if (viewHolder is TracklistAdapter.ElementStatisticsViewHolder) return 0
|
||||
return super.getSwipeDirs(recyclerView, viewHolder)
|
||||
}
|
||||
|
||||
override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
|
||||
val itemView = viewHolder.itemView
|
||||
val itemHeight = itemView.bottom - itemView.top
|
||||
|
|
|
@ -32,6 +32,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import org.y20k.trackbook.Keys
|
||||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.core.Tracklist
|
||||
import org.y20k.trackbook.core.TracklistElement
|
||||
|
@ -78,49 +79,89 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
|||
|
||||
/* Overrides onCreateViewHolder from RecyclerView.Adapter */
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val v = LayoutInflater.from(parent.context).inflate(R.layout.track_element, parent, false)
|
||||
return TrackElementViewHolder(v)
|
||||
|
||||
when (viewType) {
|
||||
Keys.VIEW_TYPE_STATISTICS -> {
|
||||
val v = LayoutInflater.from(parent.context).inflate(R.layout.element_statistics, parent, false)
|
||||
return ElementStatisticsViewHolder(v)
|
||||
}
|
||||
else -> {
|
||||
val v = LayoutInflater.from(parent.context).inflate(R.layout.element_track, parent, false)
|
||||
return ElementTrackViewHolder(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Overrides getItemViewType */
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (position == 0) {
|
||||
return Keys.VIEW_TYPE_STATISTICS
|
||||
} else {
|
||||
return Keys.VIEW_TYPE_TRACK
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Overrides getItemCount from RecyclerView.Adapter */
|
||||
override fun getItemCount(): Int {
|
||||
return tracklist.tracklistElements.size
|
||||
// +1 ==> the total statistics element
|
||||
return tracklist.tracklistElements.size + 1
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onBindViewHolder from RecyclerView.Adapter */
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val trackElementViewHolder: TrackElementViewHolder = holder as TrackElementViewHolder
|
||||
trackElementViewHolder.trackNameView.text = tracklist.tracklistElements[position].name
|
||||
trackElementViewHolder.trackDataView.text = createTrackDataString(position)
|
||||
when (tracklist.tracklistElements[position].starred) {
|
||||
true -> trackElementViewHolder.starButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_star_filled_24dp))
|
||||
false -> trackElementViewHolder.starButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_star_outline_24dp))
|
||||
}
|
||||
trackElementViewHolder.trackElement.setOnClickListener {
|
||||
tracklistListener.onTrackElementTapped(tracklist.tracklistElements[position])
|
||||
}
|
||||
trackElementViewHolder.starButton.setOnClickListener {
|
||||
toggleStarred(it, position)
|
||||
|
||||
when (holder) {
|
||||
|
||||
// CASE STATISTICS ELEMENT
|
||||
is ElementStatisticsViewHolder -> {
|
||||
val elementStatisticsViewHolder: ElementStatisticsViewHolder = holder as ElementStatisticsViewHolder
|
||||
elementStatisticsViewHolder.totalDistanceView.text = LengthUnitHelper.convertDistanceToString(tracklist.totalDistanceAll, useImperial)
|
||||
}
|
||||
|
||||
// CASE TRACK ELEMENT
|
||||
is ElementTrackViewHolder -> {
|
||||
val positionInTracklist: Int = position -1
|
||||
val elementTrackViewHolder: ElementTrackViewHolder = holder as ElementTrackViewHolder
|
||||
elementTrackViewHolder.trackNameView.text = tracklist.tracklistElements[positionInTracklist].name
|
||||
elementTrackViewHolder.trackDataView.text = createTrackDataString(positionInTracklist)
|
||||
when (tracklist.tracklistElements[positionInTracklist].starred) {
|
||||
true -> elementTrackViewHolder.starButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_star_filled_24dp))
|
||||
false -> elementTrackViewHolder.starButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_star_outline_24dp))
|
||||
}
|
||||
elementTrackViewHolder.trackElement.setOnClickListener {
|
||||
tracklistListener.onTrackElementTapped(tracklist.tracklistElements[positionInTracklist])
|
||||
}
|
||||
elementTrackViewHolder.starButton.setOnClickListener {
|
||||
toggleStarred(it, positionInTracklist)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Get track name for given position */
|
||||
fun getTrackName(position: Int): String {
|
||||
return tracklist.tracklistElements[position].name
|
||||
fun getTrackName(positionInRecyclerView: Int): String {
|
||||
// first position is always the statistics element
|
||||
return tracklist.tracklistElements[positionInRecyclerView - 1].name
|
||||
}
|
||||
|
||||
|
||||
/* Removes track and track files for given position - used by TracklistFragment */
|
||||
fun removeTrackAtPosition(context: Context, position: Int) {
|
||||
CoroutineScope(IO).launch {
|
||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, position, tracklist) }
|
||||
val positionInTracklist = position - 1
|
||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, positionInTracklist, tracklist) }
|
||||
// wait for result and store in tracklist
|
||||
withContext(Main) {
|
||||
tracklist = deferred.await()
|
||||
notifyItemRemoved(position) }
|
||||
notifyItemRemoved(position)
|
||||
notifyItemChanged(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,16 +171,25 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
|||
CoroutineScope(IO).launch {
|
||||
// reload tracklist //todo check if necessary
|
||||
// tracklist = FileHelper.readTracklist(context)
|
||||
val position: Int = findPosition(trackId)
|
||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, position, tracklist) }
|
||||
val positionInTracklist: Int = findPosition(trackId)
|
||||
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, positionInTracklist, tracklist) }
|
||||
// wait for result and store in tracklist
|
||||
withContext(Main) {
|
||||
tracklist = deferred.await()
|
||||
notifyItemRemoved(position) }
|
||||
val positionInRecyclerView: Int = positionInTracklist + 1 // position 0 is the statistics element
|
||||
notifyItemRemoved(positionInRecyclerView)
|
||||
notifyItemChanged(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Returns if the adapter is empty */
|
||||
fun isEmpty(): Boolean {
|
||||
return tracklist.tracklistElements.size == 0
|
||||
}
|
||||
|
||||
|
||||
/* Finds current position of track element in adapter list */
|
||||
private fun findPosition(trackId: Long): Int {
|
||||
tracklist.tracklistElements.forEachIndexed {index, tracklistElement ->
|
||||
|
@ -215,11 +265,23 @@ class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<Re
|
|||
/*
|
||||
* Inner class: ViewHolder for a track element
|
||||
*/
|
||||
private inner class TrackElementViewHolder (trackElementLayout: View): RecyclerView.ViewHolder(trackElementLayout) {
|
||||
val trackElement: ConstraintLayout = trackElementLayout.findViewById(R.id.track_element)
|
||||
val trackNameView: TextView = trackElementLayout.findViewById(R.id.track_name)
|
||||
val trackDataView: TextView = trackElementLayout.findViewById(R.id.track_data)
|
||||
val starButton: ImageButton = trackElementLayout.findViewById(R.id.star_button)
|
||||
inner class ElementTrackViewHolder (elementTrackLayout: View): RecyclerView.ViewHolder(elementTrackLayout) {
|
||||
val trackElement: ConstraintLayout = elementTrackLayout.findViewById(R.id.track_element)
|
||||
val trackNameView: TextView = elementTrackLayout.findViewById(R.id.track_name)
|
||||
val trackDataView: TextView = elementTrackLayout.findViewById(R.id.track_data)
|
||||
val starButton: ImageButton = elementTrackLayout.findViewById(R.id.star_button)
|
||||
|
||||
}
|
||||
/*
|
||||
* End of inner class
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Inner class: ViewHolder for a statistics element
|
||||
*/
|
||||
inner class ElementStatisticsViewHolder (elementStatisticsLayout: View): RecyclerView.ViewHolder(elementStatisticsLayout) {
|
||||
val totalDistanceView: TextView = elementStatisticsLayout.findViewById(R.id.total_distance_data)
|
||||
}
|
||||
/*
|
||||
* End of inner class
|
||||
|
|
33
app/src/main/res/layout/element_statistics.xml
Normal file
33
app/src/main/res/layout/element_statistics.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/total_distance_p"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:text="@string/track_list_p_element_statistics"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/total_distance_data"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/total_distance_p"
|
||||
app:layout_constraintStart_toStartOf="@+id/total_distance_p"
|
||||
app:layout_constraintTop_toBottomOf="@+id/total_distance_p"
|
||||
tools:text="@string/sample_text_default_total_distance" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -72,6 +72,8 @@
|
|||
<!-- Track Tab Onboarding -->
|
||||
<string name="track_list_onboarding_h1_part_1">Your recorded tracks</string>
|
||||
<string name="track_list_onboarding_h1_part_2">… will show up here.</string>
|
||||
<!-- Track List -->
|
||||
<string name="track_list_p_element_statistics">Total Distance Recorded</string>
|
||||
<!-- Settings -->
|
||||
<string name="pref_about_title">About</string>
|
||||
<string name="pref_app_version_summary">Version</string>
|
||||
|
@ -124,4 +126,5 @@
|
|||
<string name="sample_text_track_data" translatable="false">23.0 km • 5 hrs 23 min 42 sec</string>
|
||||
<string name="sample_text_track_name" translatable="false">July 20, 1969</string>
|
||||
<string name="sample_text_default_data" translatable="false">track data missing</string>
|
||||
<string name="sample_text_default_total_distance" translatable="false">6357.23 km</string>
|
||||
</resources>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.5.20'
|
||||
kotlin_version = '1.5.30'
|
||||
navigation_version = '2.3.3'
|
||||
}
|
||||
repositories {
|
||||
|
@ -10,7 +10,7 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
classpath 'com.android.tools.build:gradle:7.0.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigation_version"
|
||||
|
||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
Loading…
Reference in a new issue