parent
c4d6a0479b
commit
7d47068bb0
18 changed files with 294 additions and 681 deletions
|
@ -63,7 +63,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
val screen = preferenceManager.createPreferenceScreen(context)
|
val screen = preferenceManager.createPreferenceScreen(context)
|
||||||
|
|
||||||
// set up "Restrict to GPS" preference
|
// set up "Restrict to GPS" preference
|
||||||
val preferenceGpsOnly = SwitchPreferenceCompat(activity as Context)
|
val preferenceGpsOnly: SwitchPreferenceCompat = SwitchPreferenceCompat(activity as Context)
|
||||||
preferenceGpsOnly.title = getString(R.string.pref_gps_only_title)
|
preferenceGpsOnly.title = getString(R.string.pref_gps_only_title)
|
||||||
preferenceGpsOnly.setIcon(R.drawable.ic_gps_24dp)
|
preferenceGpsOnly.setIcon(R.drawable.ic_gps_24dp)
|
||||||
preferenceGpsOnly.key = Keys.PREF_GPS_ONLY
|
preferenceGpsOnly.key = Keys.PREF_GPS_ONLY
|
||||||
|
@ -72,41 +72,26 @@ 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(activity as Context)
|
val preferenceImperialMeasurementUnits: SwitchPreferenceCompat = SwitchPreferenceCompat(activity as Context)
|
||||||
preferenceImperialMeasurementUnits.title =
|
preferenceImperialMeasurementUnits.title = getString(R.string.pref_imperial_measurement_units_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 =
|
preferenceImperialMeasurementUnits.summaryOn = getString(R.string.pref_imperial_measurement_units_summary_imperial)
|
||||||
getString(R.string.pref_imperial_measurement_units_summary_imperial)
|
preferenceImperialMeasurementUnits.summaryOff = getString(R.string.pref_imperial_measurement_units_summary_metric)
|
||||||
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
|
||||||
val preferenceThemeSelection = ListPreference(activity as Context)
|
val preferenceThemeSelection: ListPreference = ListPreference(activity as Context)
|
||||||
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 =
|
preferenceThemeSelection.summary = "${getString(R.string.pref_theme_selection_summary)} ${AppThemeHelper.getCurrentTheme(activity as Context)}"
|
||||||
"${getString(R.string.pref_theme_selection_summary)} ${AppThemeHelper.getCurrentTheme(
|
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))
|
||||||
activity as Context
|
preferenceThemeSelection.entryValues = arrayOf(Keys.STATE_THEME_FOLLOW_SYSTEM, Keys.STATE_THEME_LIGHT_MODE, Keys.STATE_THEME_DARK_MODE)
|
||||||
)}"
|
|
||||||
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 =
|
preferenceThemeSelection.summary = "${getString(R.string.pref_theme_selection_summary)} ${preference.entries.get(index)}"
|
||||||
"${getString(R.string.pref_theme_selection_summary)} ${preference.entries[index]}"
|
|
||||||
return@setOnPreferenceChangeListener true
|
return@setOnPreferenceChangeListener true
|
||||||
} else {
|
} else {
|
||||||
return@setOnPreferenceChangeListener false
|
return@setOnPreferenceChangeListener false
|
||||||
|
@ -114,22 +99,17 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up "Delete Non-Starred" preference
|
// set up "Delete Non-Starred" preference
|
||||||
val preferenceDeleteNonStarred = Preference(activity as Context)
|
val preferenceDeleteNonStarred: Preference = Preference(activity as Context)
|
||||||
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(
|
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)
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up "Accuracy Threshold" preference
|
// set up "Accuracy Threshold" preference
|
||||||
val preferenceAccuracyThreshold = SeekBarPreference(activity as Context)
|
val preferenceAccuracyThreshold: SeekBarPreference = SeekBarPreference(activity as Context)
|
||||||
preferenceAccuracyThreshold.title = getString(R.string.pref_accuracy_threshold_title)
|
preferenceAccuracyThreshold.title = getString(R.string.pref_accuracy_threshold_title)
|
||||||
preferenceAccuracyThreshold.setIcon(R.drawable.ic_timeline_24dp)
|
preferenceAccuracyThreshold.setIcon(R.drawable.ic_timeline_24dp)
|
||||||
preferenceAccuracyThreshold.key = Keys.PREF_LOCATION_ACCURACY_THRESHOLD
|
preferenceAccuracyThreshold.key = Keys.PREF_LOCATION_ACCURACY_THRESHOLD
|
||||||
|
@ -139,39 +119,32 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
preferenceAccuracyThreshold.setDefaultValue(Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY)
|
preferenceAccuracyThreshold.setDefaultValue(Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY)
|
||||||
|
|
||||||
// set up "Reset" preference
|
// set up "Reset" preference
|
||||||
val preferenceResetAdvanced = Preference(activity as Context)
|
val preferenceResetAdvanced: Preference = Preference(activity as Context)
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up "App Version" preference
|
// set up "App Version" preference
|
||||||
val preferenceAppVersion = 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 =
|
preferenceAppVersion.summary = "${getString(R.string.pref_app_version_summary)} ${BuildConfig.VERSION_NAME} (${getString(
|
||||||
"${getString(R.string.pref_app_version_summary)} ${BuildConfig.VERSION_NAME} (${getString(
|
R.string.app_version_name)})"
|
||||||
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 =
|
val cm: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
||||||
cm.setPrimaryClip(clip)
|
cm.setPrimaryClip(clip)
|
||||||
Toast.makeText(
|
Toast.makeText(activity as Context, R.string.toast_message_copied_to_clipboard, Toast.LENGTH_LONG).show()
|
||||||
activity as Context,
|
|
||||||
R.string.toast_message_copied_to_clipboard,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up "Report Issue" preference
|
// set up "Report Issue" preference
|
||||||
val preferenceReportIssue = Preference(context)
|
val preferenceReportIssue: Preference = Preference(context)
|
||||||
preferenceReportIssue.title = getString(R.string.pref_report_issue_title)
|
preferenceReportIssue.title = getString(R.string.pref_report_issue_title)
|
||||||
preferenceReportIssue.setIcon(R.drawable.ic_bug_report_24dp)
|
preferenceReportIssue.setIcon(R.drawable.ic_bug_report_24dp)
|
||||||
preferenceReportIssue.summary = getString(R.string.pref_report_issue_summary)
|
preferenceReportIssue.summary = getString(R.string.pref_report_issue_summary)
|
||||||
|
@ -186,21 +159,20 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
}
|
}
|
||||||
|
|
||||||
// set preference categories
|
// set preference categories
|
||||||
val preferenceCategoryGeneral = PreferenceCategory(activity as Context)
|
val preferenceCategoryGeneral: PreferenceCategory = PreferenceCategory(activity as Context)
|
||||||
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 =
|
val preferenceCategoryMaintenance: PreferenceCategory = PreferenceCategory(activity as Context)
|
||||||
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)
|
||||||
|
|
||||||
val preferenceCategoryAdvanced = PreferenceCategory(activity as Context)
|
val preferenceCategoryAdvanced: PreferenceCategory = PreferenceCategory(activity as Context)
|
||||||
preferenceCategoryAdvanced.title = getString(R.string.pref_advanced_title)
|
preferenceCategoryAdvanced.title = getString(R.string.pref_advanced_title)
|
||||||
preferenceCategoryAdvanced.contains(preferenceAccuracyThreshold)
|
preferenceCategoryAdvanced.contains(preferenceAccuracyThreshold)
|
||||||
preferenceCategoryAdvanced.contains(preferenceResetAdvanced)
|
preferenceCategoryAdvanced.contains(preferenceResetAdvanced)
|
||||||
|
|
||||||
val preferenceCategoryAbout = PreferenceCategory(context)
|
val preferenceCategoryAbout: PreferenceCategory = PreferenceCategory(context)
|
||||||
preferenceCategoryAbout.title = getString(R.string.pref_about_title)
|
preferenceCategoryAbout.title = getString(R.string.pref_about_title)
|
||||||
preferenceCategoryAbout.contains(preferenceAppVersion)
|
preferenceCategoryAbout.contains(preferenceAppVersion)
|
||||||
preferenceCategoryAbout.contains(preferenceReportIssue)
|
preferenceCategoryAbout.contains(preferenceReportIssue)
|
||||||
|
@ -223,12 +195,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onYesNoDialog from YesNoDialogListener */
|
/* Overrides onYesNoDialog from YesNoDialogListener */
|
||||||
override fun onYesNoDialog(
|
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String) {
|
||||||
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) {
|
||||||
|
@ -246,13 +213,12 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
|
|
||||||
|
|
||||||
/* Removes track and track files for given position - used by TracklistFragment */
|
/* Removes track and track files for given position - used by TracklistFragment */
|
||||||
private fun deleteNonStarred(context: Context) {
|
fun deleteNonStarred(context: Context) {
|
||||||
val backgroundJob = Job()
|
val backgroundJob = Job()
|
||||||
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> =
|
val deferred: Deferred<Tracklist> = async { FileHelper.deleteNonStarredSuspended(context, 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()
|
||||||
|
|
|
@ -24,9 +24,7 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.VibrationEffect
|
|
||||||
import android.os.Vibrator
|
import android.os.Vibrator
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -48,8 +46,7 @@ import org.y20k.trackbook.helpers.TrackHelper
|
||||||
import org.y20k.trackbook.ui.TrackFragmentLayoutHolder
|
import org.y20k.trackbook.ui.TrackFragmentLayoutHolder
|
||||||
|
|
||||||
|
|
||||||
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener,
|
class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDialog.YesNoDialogListener, MapOverlay.MarkerListener {
|
||||||
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)
|
||||||
|
@ -64,30 +61,19 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener,
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
// get track
|
// get track
|
||||||
val fileUriString: String =
|
val fileUriString: String = arguments?.getString(Keys.ARG_TRACK_FILE_URI, String()) ?: String()
|
||||||
arguments?.getString(Keys.ARG_TRACK_FILE_URI, String()) ?: String()
|
if (fileUriString.isNotBlank()) {
|
||||||
track = if (fileUriString.isNotBlank()) {
|
track = FileHelper.readTrack(activity as Context, Uri.parse(fileUriString))
|
||||||
FileHelper.readTrack(Uri.parse(fileUriString))
|
|
||||||
} else {
|
} else {
|
||||||
Track()
|
track = Track()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onCreateView from Fragment */
|
/* Overrides onCreateView from Fragment */
|
||||||
override fun onCreateView(
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
// initialize layout
|
// initialize layout
|
||||||
layout = TrackFragmentLayoutHolder(
|
layout = TrackFragmentLayoutHolder(activity as Context, this as MapOverlay.MarkerListener, inflater, container, track)
|
||||||
activity as Context,
|
|
||||||
this as MapOverlay.MarkerListener,
|
|
||||||
inflater,
|
|
||||||
container,
|
|
||||||
track
|
|
||||||
)
|
|
||||||
|
|
||||||
// set up share button
|
// set up share button
|
||||||
layout.shareButton.setOnClickListener {
|
layout.shareButton.setOnClickListener {
|
||||||
|
@ -95,31 +81,18 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener,
|
||||||
}
|
}
|
||||||
layout.shareButton.setOnLongClickListener {
|
layout.shareButton.setOnLongClickListener {
|
||||||
val v = (activity as Context).getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
val v = (activity as Context).getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
v.vibrate(VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE))
|
|
||||||
} else {
|
|
||||||
v.vibrate(50)
|
v.vibrate(50)
|
||||||
}
|
|
||||||
shareGpxTrack()
|
shareGpxTrack()
|
||||||
return@setOnLongClickListener true
|
return@setOnLongClickListener true
|
||||||
}
|
}
|
||||||
// set up delete button
|
// set up delete button
|
||||||
layout.deleteButton.setOnClickListener {
|
layout.deleteButton.setOnClickListener {
|
||||||
val dialogMessage =
|
val dialogMessage: String = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${layout.trackNameView.text}"
|
||||||
"${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)
|
||||||
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(
|
RenameTrackDialog(this as RenameTrackDialog.RenameTrackListener).show(activity as Context, layout.trackNameView.text.toString())
|
||||||
activity as Context,
|
|
||||||
layout.trackNameView.text.toString()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return layout.rootView
|
return layout.rootView
|
||||||
|
@ -152,18 +125,8 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener,
|
||||||
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 {
|
GlobalScope.launch { FileHelper.saveCopyOfFileSuspended( activity as Context, originalFileUri = sourceUri, targetFileUri = targetUri) }
|
||||||
FileHelper.saveCopyOfFileSuspended(
|
Toast.makeText(activity as Context, R.string.toast_message_save_gpx, Toast.LENGTH_LONG).show()
|
||||||
activity as Context,
|
|
||||||
originalFileUri = sourceUri,
|
|
||||||
targetFileUri = targetUri
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Toast.makeText(
|
|
||||||
activity as Context,
|
|
||||||
R.string.toast_message_save_gpx,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,13 +139,7 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener,
|
||||||
/* 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 {
|
GlobalScope.launch { FileHelper.renameTrackSuspended(activity as Context, layout.track, textInput) }
|
||||||
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
|
||||||
|
@ -190,12 +147,7 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener,
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onYesNoDialog from YesNoDialogListener */
|
/* Overrides onYesNoDialog from YesNoDialogListener */
|
||||||
override fun onYesNoDialog(
|
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String) {
|
||||||
type: Int,
|
|
||||||
dialogResult: Boolean,
|
|
||||||
payload: Int,
|
|
||||||
payloadString: String
|
|
||||||
) {
|
|
||||||
when (type) {
|
when (type) {
|
||||||
Keys.DIALOG_DELETE_TRACK -> {
|
Keys.DIALOG_DELETE_TRACK -> {
|
||||||
when (dialogResult) {
|
when (dialogResult) {
|
||||||
|
@ -237,11 +189,7 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener,
|
||||||
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(
|
Toast.makeText(activity as Context, R.string.toast_message_install_file_helper, Toast.LENGTH_LONG).show()
|
||||||
activity as Context,
|
|
||||||
R.string.toast_message_install_file_helper,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,11 +197,7 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener,
|
||||||
/* 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(
|
val gpxShareUri = FileProvider.getUriForFile(this.activity as Context, "${requireActivity().applicationContext.packageName}.provider", gpxFile)
|
||||||
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
|
||||||
|
@ -267,8 +211,7 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener,
|
||||||
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)
|
Toast.makeText(activity, R.string.toast_message_install_file_helper, Toast.LENGTH_LONG).show()
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -58,16 +58,16 @@ class TrackerService : Service(), CoroutineScope, SensorEventListener {
|
||||||
var trackingState: Int = Keys.STATE_TRACKING_NOT
|
var trackingState: Int = Keys.STATE_TRACKING_NOT
|
||||||
var gpsProviderActive: Boolean = false
|
var gpsProviderActive: Boolean = false
|
||||||
var networkProviderActive: Boolean = false
|
var networkProviderActive: Boolean = false
|
||||||
private var useImperial: Boolean = false
|
var useImperial: Boolean = false
|
||||||
private var gpsOnly: Boolean = false
|
var gpsOnly: Boolean = false
|
||||||
var locationAccuracyThreshold: Int = Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY
|
var locationAccuracyThreshold: Int = Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY
|
||||||
var currentBestLocation: Location = LocationHelper.getDefaultLocation()
|
var currentBestLocation: Location = LocationHelper.getDefaultLocation()
|
||||||
private var stepCountOffset: Float = 0f
|
var stepCountOffset: Float = 0f
|
||||||
var resumed: Boolean = false
|
var resumed: Boolean = false
|
||||||
var track: Track = Track()
|
var track: Track = Track()
|
||||||
private var gpsLocationListenerRegistered: Boolean = false
|
var gpsLocationListenerRegistered: Boolean = false
|
||||||
private var networkLocationListenerRegistered: Boolean = false
|
var networkLocationListenerRegistered: Boolean = false
|
||||||
private var bound: Boolean = false
|
var bound: Boolean = false
|
||||||
private val binder = LocalBinder()
|
private val binder = LocalBinder()
|
||||||
private val handler: Handler = Handler()
|
private val handler: Handler = Handler()
|
||||||
private lateinit var locationManager: LocationManager
|
private lateinit var locationManager: LocationManager
|
||||||
|
@ -99,10 +99,9 @@ class TrackerService : Service(), CoroutineScope, SensorEventListener {
|
||||||
networkLocationListener = createLocationListener()
|
networkLocationListener = createLocationListener()
|
||||||
trackingState = PreferencesHelper.loadTrackingState(this)
|
trackingState = PreferencesHelper.loadTrackingState(this)
|
||||||
currentBestLocation = LocationHelper.getLastKnownLocation(this)
|
currentBestLocation = LocationHelper.getLastKnownLocation(this)
|
||||||
track = FileHelper.readTrack(FileHelper.getTempFileUri(this))
|
track = FileHelper.readTrack(this, FileHelper.getTempFileUri(this))
|
||||||
backgroundJob = Job()
|
backgroundJob = Job()
|
||||||
PreferenceManager.getDefaultSharedPreferences(this)
|
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,10 +111,7 @@ 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(
|
LogHelper.w(TAG, "Trackbook has been killed by the operating system. Trying to resume recording.")
|
||||||
TAG,
|
|
||||||
"Trackbook has been killed by the operating system. Trying to resume recording."
|
|
||||||
)
|
|
||||||
resumeTracking()
|
resumeTracking()
|
||||||
}
|
}
|
||||||
// ACTION STOP
|
// ACTION STOP
|
||||||
|
@ -176,8 +172,7 @@ 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)
|
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
|
||||||
// stop receiving location updates
|
// stop receiving location updates
|
||||||
removeGpsLocationListener()
|
removeGpsLocationListener()
|
||||||
removeNetworkLocationListener()
|
removeNetworkLocationListener()
|
||||||
|
@ -194,12 +189,11 @@ class TrackerService : Service(), CoroutineScope, SensorEventListener {
|
||||||
|
|
||||||
/* Overrides onSensorChanged from SensorEventListener */
|
/* Overrides onSensorChanged from SensorEventListener */
|
||||||
override fun onSensorChanged(sensorEvent: SensorEvent?) {
|
override fun onSensorChanged(sensorEvent: SensorEvent?) {
|
||||||
var steps = 0f
|
var steps: Float = 0f
|
||||||
if (sensorEvent != null) {
|
if (sensorEvent != null) {
|
||||||
if (stepCountOffset == 0f) {
|
if (stepCountOffset == 0f) {
|
||||||
// store steps previously recorded by the system
|
// store steps previously recorded by the system
|
||||||
stepCountOffset =
|
stepCountOffset = (sensorEvent.values[0] - 1) - track.stepCount // subtract any steps recorded during this session in case the app was killed
|
||||||
(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
|
||||||
|
@ -212,17 +206,16 @@ class TrackerService : Service(), CoroutineScope, SensorEventListener {
|
||||||
/* Resume tracking after stop/pause */
|
/* Resume tracking after stop/pause */
|
||||||
fun resumeTracking() {
|
fun resumeTracking() {
|
||||||
// load temp track - returns an empty track if not available
|
// load temp track - returns an empty track if not available
|
||||||
track = FileHelper.readTrack(FileHelper.getTempFileUri(this))
|
track = FileHelper.readTrack(this, FileHelper.getTempFileUri(this))
|
||||||
// try to mark last waypoint as stopover
|
// try to mark last waypoint as stopover
|
||||||
if (track.wayPoints.size > 0) {
|
if (track.wayPoints.size > 0) {
|
||||||
val lastWayPointIndex = track.wayPoints.size - 1
|
val lastWayPointIndex = track.wayPoints.size - 1
|
||||||
track.wayPoints[lastWayPointIndex].isStopOver = true
|
track.wayPoints.get(lastWayPointIndex).isStopOver = true
|
||||||
}
|
}
|
||||||
// set resumed flag
|
// set resumed flag
|
||||||
resumed = true
|
resumed = true
|
||||||
// calculate length of recording break
|
// calculate length of recording break
|
||||||
track.recordingPaused =
|
track.recordingPaused = track.recordingPaused + TrackHelper.calculateDurationOfPause(track.recordingStop)
|
||||||
track.recordingPaused + TrackHelper.calculateDurationOfPause(track.recordingStop)
|
|
||||||
// start tracking
|
// start tracking
|
||||||
startTracking(newTrack = false)
|
startTracking(newTrack = false)
|
||||||
}
|
}
|
||||||
|
@ -308,27 +301,20 @@ 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 =
|
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
|
||||||
LocationHelper.isGpsEnabled(locationManager)
|
LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
||||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive =
|
|
||||||
LocationHelper.isNetworkEnabled(locationManager)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onProviderDisabled(provider: String) {
|
override fun onProviderDisabled(provider: String) {
|
||||||
LogHelper.v(TAG, "onProviderDisabled $provider")
|
LogHelper.v(TAG, "onProviderDisabled $provider")
|
||||||
when (provider) {
|
when (provider) {
|
||||||
LocationManager.GPS_PROVIDER -> gpsProviderActive =
|
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
|
||||||
LocationHelper.isGpsEnabled(locationManager)
|
LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
||||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive =
|
|
||||||
LocationHelper.isNetworkEnabled(locationManager)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
|
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
|
||||||
// deprecated method
|
// deprecated method
|
||||||
}
|
}
|
||||||
|
@ -343,25 +329,13 @@ 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(
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
this,
|
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION
|
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
|
||||||
) {
|
|
||||||
// adds GPS location listener
|
// adds GPS location listener
|
||||||
locationManager.requestLocationUpdates(
|
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f,gpsLocationListener)
|
||||||
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(
|
LogHelper.w(TAG, "Unable to add GPS location listener. Location permission is not granted.")
|
||||||
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.")
|
||||||
|
@ -379,83 +353,50 @@ 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(
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
this,
|
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION
|
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
|
||||||
) {
|
|
||||||
// adds Network location listener
|
// adds Network location listener
|
||||||
locationManager.requestLocationUpdates(
|
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0f, networkLocationListener)
|
||||||
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(
|
LogHelper.w(TAG, "Unable to add Network location listener. Location permission is not granted.")
|
||||||
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(
|
LogHelper.v(TAG, "Skipping registration. Network location listener has already been added.")
|
||||||
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(
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
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(
|
LogHelper.w(TAG, "Unable to remove GPS location listener. Location permission is needed.")
|
||||||
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(
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
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(
|
LogHelper.w(TAG, "Unable to remove Network location listener. Location permission is needed.")
|
||||||
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(
|
val stepCounterAvailable = sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER), SensorManager.SENSOR_DELAY_UI)
|
||||||
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
|
||||||
|
@ -465,12 +406,7 @@ class TrackerService : Service(), CoroutineScope, SensorEventListener {
|
||||||
|
|
||||||
/* Displays / updates notification */
|
/* Displays / updates notification */
|
||||||
private fun displayNotification(): Notification {
|
private fun displayNotification(): Notification {
|
||||||
val notification: Notification = notificationHelper.createNotification(
|
val notification: Notification = notificationHelper.createNotification(trackingState, track.length, track.duration, useImperial)
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -479,8 +415,7 @@ class TrackerService : Service(), CoroutineScope, SensorEventListener {
|
||||||
/*
|
/*
|
||||||
* Defines the listener for changes in shared preferences
|
* Defines the listener for changes in shared preferences
|
||||||
*/
|
*/
|
||||||
private val sharedPreferenceChangeListener =
|
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
|
||||||
when (key) {
|
when (key) {
|
||||||
// preference "Restrict to GPS"
|
// preference "Restrict to GPS"
|
||||||
Keys.PREF_GPS_ONLY -> {
|
Keys.PREF_GPS_ONLY -> {
|
||||||
|
@ -496,8 +431,7 @@ class TrackerService : Service(), CoroutineScope, SensorEventListener {
|
||||||
}
|
}
|
||||||
// preference "Accuracy Threshold"
|
// preference "Accuracy Threshold"
|
||||||
Keys.PREF_LOCATION_ACCURACY_THRESHOLD -> {
|
Keys.PREF_LOCATION_ACCURACY_THRESHOLD -> {
|
||||||
locationAccuracyThreshold =
|
locationAccuracyThreshold = PreferencesHelper.loadAccuracyThreshold(this@TrackerService)
|
||||||
PreferencesHelper.loadAccuracyThreshold(this@TrackerService)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -523,12 +457,7 @@ 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(
|
val result: Pair<Track, Boolean> = TrackHelper.addWayPointToTrack(this@TrackerService, track, currentBestLocation, locationAccuracyThreshold, resumed)
|
||||||
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)
|
||||||
|
@ -52,6 +52,11 @@ class TrackingToggleTileService : TileService() {
|
||||||
updateTile()
|
updateTile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Overrides onTileRemoved from TileService */
|
||||||
|
override fun onTileRemoved() {
|
||||||
|
super.onTileRemoved()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onStartListening from TileService (tile becomes visible) */
|
/* Overrides onStartListening from TileService (tile becomes visible) */
|
||||||
override fun onStartListening() {
|
override fun onStartListening() {
|
||||||
|
@ -61,8 +66,7 @@ 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)
|
PreferenceManager.getDefaultSharedPreferences(this@TrackingToggleTileService).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,8 +84,13 @@ 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)
|
PreferenceManager.getDefaultSharedPreferences(this@TrackingToggleTileService).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Overrides onDestroy from Service */
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,8 +138,7 @@ class TrackingToggleTileService : TileService() {
|
||||||
/*
|
/*
|
||||||
* Defines the listener for changes in shared preferences
|
* Defines the listener for changes in shared preferences
|
||||||
*/
|
*/
|
||||||
private val sharedPreferenceChangeListener =
|
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
|
||||||
when (key) {
|
when (key) {
|
||||||
Keys.PREF_TRACKING_STATE -> {
|
Keys.PREF_TRACKING_STATE -> {
|
||||||
trackingState = PreferencesHelper.loadTrackingState(this)
|
trackingState = PreferencesHelper.loadTrackingState(this)
|
||||||
|
@ -143,4 +151,6 @@ class TrackingToggleTileService : TileService() {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -40,8 +40,7 @@ import org.y20k.trackbook.tracklist.TracklistAdapter
|
||||||
/*
|
/*
|
||||||
* TracklistFragment class
|
* TracklistFragment class
|
||||||
*/
|
*/
|
||||||
class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener, YesNoDialog.YesNoDialogListener {
|
||||||
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)
|
||||||
|
@ -62,11 +61,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onCreateView from Fragment */
|
/* Overrides onCreateView from Fragment */
|
||||||
override fun onCreateView(
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
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)
|
||||||
|
@ -82,17 +77,8 @@ 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 =
|
val dialogMessage: String = "${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${tracklistAdapter.getTrackName(adapterPosition)}"
|
||||||
"${getString(R.string.dialog_yes_no_message_delete_recording)}\n\n- ${tracklistAdapter.getTrackName(
|
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)
|
||||||
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)
|
||||||
|
@ -107,7 +93,7 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
|
|
||||||
/* Overrides onTrackElementTapped from TracklistElementAdapterListener */
|
/* Overrides onTrackElementTapped from TracklistElementAdapterListener */
|
||||||
override fun onTrackElementTapped(tracklistElement: TracklistElement) {
|
override fun onTrackElementTapped(tracklistElement: TracklistElement) {
|
||||||
val bundle = Bundle()
|
val bundle: Bundle = Bundle()
|
||||||
bundle.putString(Keys.ARG_TRACK_TITLE, tracklistElement.name)
|
bundle.putString(Keys.ARG_TRACK_TITLE, tracklistElement.name)
|
||||||
bundle.putString(Keys.ARG_TRACK_FILE_URI, tracklistElement.trackUriString)
|
bundle.putString(Keys.ARG_TRACK_FILE_URI, tracklistElement.trackUriString)
|
||||||
bundle.putString(Keys.ARG_GPX_FILE_URI, tracklistElement.gpxUriString)
|
bundle.putString(Keys.ARG_GPX_FILE_URI, tracklistElement.gpxUriString)
|
||||||
|
@ -117,18 +103,13 @@ class TracklistFragment : Fragment(), TracklistAdapter.TracklistAdapterListener,
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onYesNoDialog from YesNoDialogListener */
|
/* Overrides onYesNoDialog from YesNoDialogListener */
|
||||||
override fun onYesNoDialog(
|
override fun onYesNoDialog(type: Int, dialogResult: Boolean, payload: Int, payloadString: String) {
|
||||||
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
|
||||||
|
@ -158,11 +139,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) :
|
inner class CustomLinearLayoutManager(context: Context): LinearLayoutManager(context, VERTICAL, false) {
|
||||||
LinearLayoutManager(context, VERTICAL, false) {
|
|
||||||
|
|
||||||
override fun supportsPredictiveItemAnimations(): Boolean {
|
override fun supportsPredictiveItemAnimations(): Boolean {
|
||||||
return true
|
return true
|
||||||
|
@ -176,7 +157,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ data class WayPoint(@Expose val provider: String,
|
||||||
|
|
||||||
/* Converts WayPoint into Location */
|
/* Converts WayPoint into Location */
|
||||||
fun toLocation(): Location {
|
fun toLocation(): Location {
|
||||||
val location = Location(provider)
|
val location: Location = Location(provider)
|
||||||
location.latitude = latitude
|
location.latitude = latitude
|
||||||
location.longitude = longitude
|
location.longitude = longitude
|
||||||
location.altitude = altitude
|
location.altitude = altitude
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.y20k.trackbook.dialogs
|
package org.y20k.trackbook.dialogs
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.text.method.ScrollingMovementMethod
|
import android.text.method.ScrollingMovementMethod
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -37,14 +38,9 @@ object ErrorDialog {
|
||||||
|
|
||||||
|
|
||||||
/* Construct and show dialog */
|
/* Construct and show dialog */
|
||||||
fun show(
|
fun show(context: Context, errorTitle: Int, errorMessage: Int, errorDetails: String = String()) {
|
||||||
context: Context,
|
|
||||||
errorTitle: Int,
|
|
||||||
errorMessage: Int,
|
|
||||||
errorDetails: String = String()
|
|
||||||
) {
|
|
||||||
// prepare dialog builder
|
// prepare dialog builder
|
||||||
val builder = 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))
|
||||||
|
@ -85,12 +81,10 @@ object ErrorDialog {
|
||||||
errorMessageView.text = context.getString(errorMessage)
|
errorMessageView.text = context.getString(errorMessage)
|
||||||
|
|
||||||
// add okay button
|
// add okay button
|
||||||
builder.setPositiveButton(
|
builder.setPositiveButton(R.string.dialog_generic_button_okay, DialogInterface.OnClickListener { _, _ ->
|
||||||
R.string.dialog_generic_button_okay
|
|
||||||
) { _, _ ->
|
|
||||||
// listen for click on okay button
|
// listen for click on okay button
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
})
|
||||||
|
|
||||||
// display error dialog
|
// display error dialog
|
||||||
builder.show()
|
builder.show()
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -38,44 +38,31 @@ class YesNoDialog(private var yesNoDialogListener: YesNoDialogListener) {
|
||||||
|
|
||||||
|
|
||||||
/* Construct and show dialog - variant: message from string */
|
/* Construct and show dialog - variant: message from string */
|
||||||
fun show(
|
fun show(context: Context,
|
||||||
context: Context,
|
|
||||||
type: Int,
|
type: Int,
|
||||||
title: Int = Keys.EMPTY_STRING_RESOURCE,
|
title: Int = Keys.EMPTY_STRING_RESOURCE,
|
||||||
message: Int,
|
message: Int,
|
||||||
yesButton: Int = R.string.dialog_yes_no_positive_button_default,
|
yesButton: Int = R.string.dialog_yes_no_positive_button_default,
|
||||||
noButton: Int = R.string.dialog_generic_button_cancel,
|
noButton: Int = R.string.dialog_generic_button_cancel,
|
||||||
payload: Int = Keys.DIALOG_EMPTY_PAYLOAD_INT,
|
payload: Int = Keys.DIALOG_EMPTY_PAYLOAD_INT,
|
||||||
payloadString: String = Keys.DIALOG_EMPTY_PAYLOAD_STRING
|
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(
|
show(context, type, title, context.getString(message), yesButton, noButton, payload, payloadString)
|
||||||
context,
|
|
||||||
type,
|
|
||||||
title,
|
|
||||||
context.getString(message),
|
|
||||||
yesButton,
|
|
||||||
noButton,
|
|
||||||
payload,
|
|
||||||
payloadString
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Construct and show dialog */
|
/* Construct and show dialog */
|
||||||
fun show(
|
fun show(context: Context,
|
||||||
context: Context,
|
|
||||||
type: Int,
|
type: Int,
|
||||||
title: Int = Keys.EMPTY_STRING_RESOURCE,
|
title: Int = Keys.EMPTY_STRING_RESOURCE,
|
||||||
messageString: String,
|
messageString: String,
|
||||||
yesButton: Int = R.string.dialog_yes_no_positive_button_default,
|
yesButton: Int = R.string.dialog_yes_no_positive_button_default,
|
||||||
noButton: Int = R.string.dialog_generic_button_cancel,
|
noButton: Int = R.string.dialog_generic_button_cancel,
|
||||||
payload: Int = Keys.DIALOG_EMPTY_PAYLOAD_INT,
|
payload: Int = Keys.DIALOG_EMPTY_PAYLOAD_INT,
|
||||||
payloadString: String = Keys.DIALOG_EMPTY_PAYLOAD_STRING
|
payloadString: String = Keys.DIALOG_EMPTY_PAYLOAD_STRING) {
|
||||||
) {
|
|
||||||
|
|
||||||
// prepare dialog builder
|
// prepare dialog builder
|
||||||
val builder = 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)
|
||||||
|
@ -97,7 +84,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,11 @@ package org.y20k.trackbook.extensions
|
||||||
|
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import java.lang.Double.doubleToRawLongBits
|
|
||||||
import java.lang.Double.longBitsToDouble
|
|
||||||
|
|
||||||
|
|
||||||
/* Puts a Double value in SharedPreferences */
|
/* Puts a Double value in SharedPreferences */
|
||||||
fun SharedPreferences.Editor.putDouble(key: String, double: Double) =
|
fun SharedPreferences.Editor.putDouble(key: String, double: Double) = putLong(key, java.lang.Double.doubleToRawLongBits(double))
|
||||||
this.putLong(key, doubleToRawLongBits(double))
|
|
||||||
|
|
||||||
|
|
||||||
/* gets a Double value from SharedPreferences */
|
/* gets a Double value from SharedPreferences */
|
||||||
fun SharedPreferences.getDouble(key: String, default: Double) =
|
fun SharedPreferences.getDouble(key: String, default: Double) = java.lang.Double.longBitsToDouble(getLong(key, java.lang.Double.doubleToRawLongBits(default)))
|
||||||
longBitsToDouble(getLong(key, doubleToRawLongBits(default)))
|
|
|
@ -35,8 +35,6 @@ import java.text.NumberFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
import kotlin.math.ln
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -50,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
|
||||||
|
@ -63,14 +61,14 @@ object FileHelper {
|
||||||
/* Get file size for given Uri */
|
/* Get file size for given Uri */
|
||||||
fun getFileSize(context: Context, uri: Uri): Long {
|
fun getFileSize(context: Context, uri: Uri): Long {
|
||||||
val cursor: Cursor? = context.contentResolver.query(uri, null, null, null, null)
|
val cursor: Cursor? = context.contentResolver.query(uri, null, null, null, null)
|
||||||
return if (cursor != null) {
|
if (cursor != null) {
|
||||||
val sizeIndex: Int = cursor.getColumnIndex(OpenableColumns.SIZE)
|
val sizeIndex: Int = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||||
cursor.moveToFirst()
|
cursor.moveToFirst()
|
||||||
val size: Long = cursor.getLong(sizeIndex)
|
val size: Long = cursor.getLong(sizeIndex)
|
||||||
cursor.close()
|
cursor.close()
|
||||||
size
|
return size
|
||||||
} else {
|
} else {
|
||||||
0L
|
return 0L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,14 +76,14 @@ object FileHelper {
|
||||||
/* Get file name for given Uri */
|
/* Get file name for given Uri */
|
||||||
fun getFileName(context: Context, uri: Uri): String {
|
fun getFileName(context: Context, uri: Uri): String {
|
||||||
val cursor: Cursor? = context.contentResolver.query(uri, null, null, null, null)
|
val cursor: Cursor? = context.contentResolver.query(uri, null, null, null, null)
|
||||||
return if (cursor != null) {
|
if (cursor != null) {
|
||||||
val nameIndex: Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
val nameIndex: Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||||
cursor.moveToFirst()
|
cursor.moveToFirst()
|
||||||
val name: String = cursor.getString(nameIndex)
|
val name: String = cursor.getString(nameIndex)
|
||||||
cursor.close()
|
cursor.close()
|
||||||
name
|
return name
|
||||||
} else {
|
} else {
|
||||||
String()
|
return String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +110,8 @@ object FileHelper {
|
||||||
fun readTracklist(context: Context): Tracklist {
|
fun readTracklist(context: Context): Tracklist {
|
||||||
LogHelper.v(TAG, "Reading Tracklist - Thread: ${Thread.currentThread().name}")
|
LogHelper.v(TAG, "Reading Tracklist - Thread: ${Thread.currentThread().name}")
|
||||||
// get JSON from text file
|
// get JSON from text file
|
||||||
val json: String = readTextFile(getTracklistFileUri(context))
|
val json: String = readTextFile(context, getTracklistFileUri(context))
|
||||||
var tracklist = Tracklist()
|
var tracklist: Tracklist = Tracklist()
|
||||||
when (json.isNotBlank()) {
|
when (json.isNotBlank()) {
|
||||||
// convert JSON and return as tracklist
|
// convert JSON and return as tracklist
|
||||||
true -> try {
|
true -> try {
|
||||||
|
@ -127,10 +125,10 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Reads track from storage using GSON */
|
/* Reads track from storage using GSON */
|
||||||
fun readTrack(fileUri: Uri): Track {
|
fun readTrack(context: Context, fileUri: Uri): Track {
|
||||||
// get JSON from text file
|
// get JSON from text file
|
||||||
val json: String = readTextFile(fileUri)
|
val json: String = readTextFile(context, fileUri)
|
||||||
var track = Track()
|
var track: Track = Track()
|
||||||
when (json.isNotEmpty()) {
|
when (json.isNotEmpty()) {
|
||||||
// convert JSON and return as track
|
// convert JSON and return as track
|
||||||
true -> try {
|
true -> try {
|
||||||
|
@ -156,19 +154,16 @@ 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 =
|
fun getGpxFileUri(context: Context, track: Track): Uri = File(context.getExternalFilesDir(Keys.FOLDER_GPX), getGpxFileName(track)).toUri()
|
||||||
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 =
|
fun getGpxFileName(track: Track): String = DateTimeHelper.convertToSortableDateString(track.recordingStart) + Keys.GPX_FILE_EXTENSION
|
||||||
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 =
|
val fileName: String = DateTimeHelper.convertToSortableDateString(track.recordingStart) + Keys.TRACKBOOK_FILE_EXTENSION
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,11 +175,7 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for saveTracklist */
|
/* Suspend function: Wrapper for saveTracklist */
|
||||||
suspend fun addTrackAndSaveTracklistSuspended(
|
suspend fun addTrackAndSaveTracklistSuspended(context: Context, track: Track, modificationDate: Date = track.recordingStop) {
|
||||||
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))
|
||||||
|
@ -202,11 +193,7 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for saveTracklist */
|
/* Suspend function: Wrapper for saveTracklist */
|
||||||
suspend fun saveTracklistSuspended(
|
suspend fun saveTracklistSuspended(context: Context, tracklist: Tracklist, modificationDate: Date) {
|
||||||
context: Context,
|
|
||||||
tracklist: Tracklist,
|
|
||||||
modificationDate: Date
|
|
||||||
) {
|
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
cont.resume(saveTracklist(context, tracklist, modificationDate))
|
cont.resume(saveTracklist(context, tracklist, modificationDate))
|
||||||
}
|
}
|
||||||
|
@ -230,11 +217,7 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Suspend function: Wrapper for deleteTrack */
|
/* Suspend function: Wrapper for deleteTrack */
|
||||||
suspend fun deleteTrackSuspended(
|
suspend fun deleteTrackSuspended(context: Context, position: Int, tracklist: Tracklist): Tracklist {
|
||||||
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))
|
||||||
}
|
}
|
||||||
|
@ -257,19 +240,14 @@ 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(
|
suspend fun saveCopyOfFileSuspended(context: Context, originalFileUri: Uri, targetFileUri: Uri, deleteOriginal: Boolean = false) {
|
||||||
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))
|
||||||
}
|
}
|
||||||
|
@ -307,7 +285,7 @@ object FileHelper {
|
||||||
tracklist.modificationDate = modificationDate
|
tracklist.modificationDate = modificationDate
|
||||||
// convert to JSON
|
// convert to JSON
|
||||||
val gson: Gson = getCustomGson()
|
val gson: Gson = getCustomGson()
|
||||||
var json = String()
|
var json: String = String()
|
||||||
try {
|
try {
|
||||||
json = gson.toJson(tracklist)
|
json = gson.toJson(tracklist)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -326,11 +304,12 @@ 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
|
||||||
val tracklist: Tracklist = readTracklist(context)
|
val tracklist: Tracklist = readTracklist(context)
|
||||||
var trackUriString = String()
|
var trackUriString: String = String()
|
||||||
tracklist.tracklistElements.forEach { tracklistElement ->
|
tracklist.tracklistElements.forEach { tracklistElement ->
|
||||||
if (tracklistElement.getTrackId() == track.getTrackId()) {
|
if (tracklistElement.getTrackId() == track.getTrackId()) {
|
||||||
// rename tracklist element
|
// rename tracklist element
|
||||||
|
@ -350,17 +329,13 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Deletes multiple tracks */
|
/* Deletes multiple tracks */
|
||||||
private fun deleteTracks(
|
private fun deleteTracks(context: Context, tracklistElements: MutableList<TracklistElement>, tracklist: Tracklist): Tracklist {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -373,23 +348,14 @@ 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 {
|
tracklist.tracklistElements.removeIf { TrackHelper.getTrackId(it) == TrackHelper.getTrackId(tracklistElement) }
|
||||||
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(
|
private fun copyFile(context: Context, originalFileUri: Uri, targetFileUri: Uri, deleteOriginal: Boolean = false) {
|
||||||
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) {
|
||||||
|
@ -404,7 +370,7 @@ object FileHelper {
|
||||||
/* Converts track to JSON */
|
/* Converts track to JSON */
|
||||||
private fun getTrackJsonString(track: Track): String {
|
private fun getTrackJsonString(track: Track): String {
|
||||||
val gson: Gson = getCustomGson()
|
val gson: Gson = getCustomGson()
|
||||||
var json = String()
|
var json: String = String()
|
||||||
try {
|
try {
|
||||||
json = gson.toJson(track)
|
json = gson.toJson(track)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -423,6 +389,7 @@ 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 {
|
||||||
|
@ -434,13 +401,13 @@ object FileHelper {
|
||||||
if (bytes < unit) return "$bytes B"
|
if (bytes < unit) return "$bytes B"
|
||||||
|
|
||||||
// calculate exp
|
// calculate exp
|
||||||
val exp: Int = (ln(bytes.toDouble()) / ln(unit.toDouble())).toInt()
|
val exp: Int = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt()
|
||||||
|
|
||||||
// determine prefix symbol
|
// determine prefix symbol
|
||||||
val prefix: String = ((if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i")
|
val prefix: String = ((if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i")
|
||||||
|
|
||||||
// calculate result and set number format
|
// calculate result and set number format
|
||||||
val result: Double = bytes / unit.toDouble().pow(exp.toDouble())
|
val result: Double = bytes / Math.pow(unit.toDouble(), exp.toDouble())
|
||||||
val numberFormat = NumberFormat.getNumberInstance()
|
val numberFormat = NumberFormat.getNumberInstance()
|
||||||
numberFormat.maximumFractionDigits = 1
|
numberFormat.maximumFractionDigits = 1
|
||||||
|
|
||||||
|
@ -449,7 +416,7 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Reads InputStream from file uri and returns it as String */
|
/* Reads InputStream from file uri and returns it as String */
|
||||||
private fun readTextFile(fileUri: Uri): String {
|
private fun readTextFile(context: Context, fileUri: Uri): String {
|
||||||
// todo read https://commonsware.com/blog/2016/03/15/how-consume-content-uri.html
|
// todo read https://commonsware.com/blog/2016/03/15/how-consume-content-uri.html
|
||||||
// https://developer.android.com/training/secure-file-sharing/retrieve-info
|
// https://developer.android.com/training/secure-file-sharing/retrieve-info
|
||||||
val file: File = fileUri.toFile()
|
val file: File = fileUri.toFile()
|
||||||
|
@ -459,12 +426,11 @@ object FileHelper {
|
||||||
}
|
}
|
||||||
// read until last line reached
|
// read until last line reached
|
||||||
val stream: InputStream = file.inputStream()
|
val stream: InputStream = file.inputStream()
|
||||||
val reader = BufferedReader(InputStreamReader(stream))
|
val reader: BufferedReader = BufferedReader(InputStreamReader(stream))
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
@ -478,13 +444,8 @@ object FileHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Writes given bitmap as image file to storage */
|
/* Writes given bitmap as image file to storage */
|
||||||
private fun writeImageFile(
|
private fun writeImageFile(context: Context, bitmap: Bitmap, file: File, format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG, quality: Int = 75) {
|
||||||
bitmap: Bitmap,
|
if (file.exists()) file.delete ()
|
||||||
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)
|
||||||
|
|
|
@ -40,7 +40,7 @@ object LocationHelper {
|
||||||
|
|
||||||
/* Get default location */
|
/* Get default location */
|
||||||
fun getDefaultLocation(): Location {
|
fun getDefaultLocation(): Location {
|
||||||
val defaultLocation = Location(LocationManager.NETWORK_PROVIDER)
|
val defaultLocation: Location = Location(LocationManager.NETWORK_PROVIDER)
|
||||||
defaultLocation.latitude = Keys.DEFAULT_LATITUDE
|
defaultLocation.latitude = Keys.DEFAULT_LATITUDE
|
||||||
defaultLocation.longitude = Keys.DEFAULT_LONGITUDE
|
defaultLocation.longitude = Keys.DEFAULT_LONGITUDE
|
||||||
defaultLocation.accuracy = Keys.DEFAULT_ACCURACY
|
defaultLocation.accuracy = Keys.DEFAULT_ACCURACY
|
||||||
|
@ -62,23 +62,13 @@ 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(
|
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
context,
|
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION
|
val lastKnownLocationGps: Location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) ?: lastKnownLocation
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
val lastKnownLocationNetwork: Location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) ?: lastKnownLocation
|
||||||
) {
|
|
||||||
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
|
|
||||||
lastKnownLocation =
|
|
||||||
when (isBetterLocation(lastKnownLocationGps, lastKnownLocationNetwork)) {
|
when (isBetterLocation(lastKnownLocationGps, lastKnownLocationNetwork)) {
|
||||||
true -> lastKnownLocationGps
|
true -> lastKnownLocation = lastKnownLocationGps
|
||||||
false -> lastKnownLocationNetwork
|
false -> lastKnownLocation = lastKnownLocationNetwork
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lastKnownLocation
|
return lastKnownLocation
|
||||||
|
@ -97,7 +87,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
|
||||||
|
@ -128,24 +118,25 @@ object LocationHelper {
|
||||||
|
|
||||||
/* Checks if GPS location provider is available and enabled */
|
/* Checks if GPS location provider is available and enabled */
|
||||||
fun isGpsEnabled(locationManager: LocationManager): Boolean {
|
fun isGpsEnabled(locationManager: LocationManager): Boolean {
|
||||||
return if (locationManager.allProviders.contains(LocationManager.GPS_PROVIDER)) {
|
if (locationManager.allProviders.contains(LocationManager.GPS_PROVIDER)) {
|
||||||
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
||||||
} else {
|
} else {
|
||||||
false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Checks if Network location provider is available and enabled */
|
/* Checks if Network location provider is available and enabled */
|
||||||
fun isNetworkEnabled(locationManager: LocationManager): Boolean {
|
fun isNetworkEnabled(locationManager: LocationManager): Boolean {
|
||||||
return if (locationManager.allProviders.contains(LocationManager.NETWORK_PROVIDER)) {
|
if (locationManager.allProviders.contains(LocationManager.NETWORK_PROVIDER)) {
|
||||||
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
||||||
} else {
|
} else {
|
||||||
false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* 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
|
||||||
|
@ -155,35 +146,26 @@ object LocationHelper {
|
||||||
|
|
||||||
/* Checks if given location is accurate */
|
/* Checks if given location is accurate */
|
||||||
fun isAccurateEnough(location: Location, locationAccuracyThreshold: Int): Boolean {
|
fun isAccurateEnough(location: Location, locationAccuracyThreshold: Int): Boolean {
|
||||||
return when (location.provider) {
|
val isAccurate: Boolean
|
||||||
LocationManager.GPS_PROVIDER -> location.accuracy < locationAccuracyThreshold
|
when (location.provider) {
|
||||||
else -> location.accuracy < locationAccuracyThreshold + 10 // a bit more relaxed when location comes from network provider
|
LocationManager.GPS_PROVIDER -> isAccurate = location.accuracy < locationAccuracyThreshold
|
||||||
|
else -> isAccurate = location.accuracy < locationAccuracyThreshold + 10 // a bit more relaxed when location comes from network provider
|
||||||
}
|
}
|
||||||
|
return isAccurate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 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(
|
val speed: Double = calculateSpeed(firstLocation = track.wayPoints[0].toLocation(), secondLocation = secondLocation, firstTimestamp = track.recordingStart.time, secondTimestamp = GregorianCalendar.getInstance().time.time)
|
||||||
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(
|
private fun calculateSpeed(firstLocation: Location, secondLocation: Location, firstTimestamp: Long, secondTimestamp: Long, useImperial: Boolean = false): Double {
|
||||||
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
|
||||||
|
@ -201,10 +183,10 @@ object LocationHelper {
|
||||||
val distanceThreshold: Float
|
val distanceThreshold: Float
|
||||||
val averageAccuracy: Float = (previousLocation.accuracy + location.accuracy) / 2
|
val averageAccuracy: Float = (previousLocation.accuracy + location.accuracy) / 2
|
||||||
// increase the distance threshold if one or both locations are
|
// increase the distance threshold if one or both locations are
|
||||||
distanceThreshold = if (averageAccuracy > Keys.DEFAULT_THRESHOLD_DISTANCE) {
|
if (averageAccuracy > Keys.DEFAULT_THRESHOLD_DISTANCE) {
|
||||||
averageAccuracy
|
distanceThreshold = averageAccuracy
|
||||||
} else {
|
} else {
|
||||||
Keys.DEFAULT_THRESHOLD_DISTANCE
|
distanceThreshold = Keys.DEFAULT_THRESHOLD_DISTANCE
|
||||||
}
|
}
|
||||||
// location is different when far enough away from previous location
|
// location is different when far enough away from previous location
|
||||||
return calculateDistance(previousLocation, location) > distanceThreshold
|
return calculateDistance(previousLocation, location) > distanceThreshold
|
||||||
|
@ -213,7 +195,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 = 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) {
|
||||||
// add up distance
|
// add up distance
|
||||||
|
@ -224,26 +206,20 @@ object LocationHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Calculate elevation differences */
|
/* Calculate elevation differences */
|
||||||
fun calculateElevationDifferences(
|
fun calculateElevationDifferences(previousLocation: Location?, location: Location, track: Track): Pair<Double, Double> {
|
||||||
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 =
|
val timeDifferenceFactor: Long = (location.time - previousLocation.time) / Keys.ADD_WAYPOINT_TO_TRACK_INTERVAL
|
||||||
(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 =
|
negativeElevation = track.negativeElevation + altitudeDifference // downwards movement
|
||||||
track.negativeElevation + altitudeDifference // downwards movement
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Pair(positiveElevation, negativeElevation)
|
return Pair(positiveElevation, negativeElevation)
|
||||||
|
|
|
@ -78,9 +78,9 @@ object LogHelper {
|
||||||
|
|
||||||
private fun log(tag: String, level: Int, t: Throwable?, vararg messages: Any) {
|
private fun log(tag: String, level: Int, t: Throwable?, vararg messages: Any) {
|
||||||
val message: String
|
val message: String
|
||||||
message = if (t == null && messages.size == 1) {
|
if (t == null && messages.size == 1) {
|
||||||
// handle this common case without the extra cost of creating a stringbuffer:
|
// handle this common case without the extra cost of creating a stringbuffer:
|
||||||
messages[0].toString()
|
message = messages[0].toString()
|
||||||
} else {
|
} else {
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
for (m in messages) {
|
for (m in messages) {
|
||||||
|
@ -89,7 +89,7 @@ object LogHelper {
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
sb.append("\n").append(Log.getStackTraceString(t))
|
sb.append("\n").append(Log.getStackTraceString(t))
|
||||||
}
|
}
|
||||||
sb.toString()
|
message = sb.toString()
|
||||||
}
|
}
|
||||||
Log.println(level, tag, message)
|
Log.println(level, tag, message)
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ object PreferencesHelper {
|
||||||
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(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 = settings.getDouble(Keys.PREF_CURRENT_BEST_LOCATION_LATITUDE, Keys.DEFAULT_LATITUDE)
|
||||||
currentBestLocation.longitude = settings.getDouble(Keys.PREF_CURRENT_BEST_LOCATION_LONGITUDE, Keys.DEFAULT_LONGITUDE)
|
currentBestLocation.longitude = settings.getDouble(Keys.PREF_CURRENT_BEST_LOCATION_LONGITUDE, Keys.DEFAULT_LONGITUDE)
|
||||||
|
|
|
@ -47,13 +47,8 @@ object TrackHelper {
|
||||||
tracklistElement.date.time
|
tracklistElement.date.time
|
||||||
|
|
||||||
|
|
||||||
/* Adds given location as waypoint to track */
|
/* Adds given locatiom as waypoint to track */
|
||||||
fun addWayPointToTrack(
|
fun addWayPointToTrack(context: Context, track: Track, location: Location, locationAccuracyThreshold: Int, resumed: Boolean): Pair<Track, Boolean> {
|
||||||
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
|
||||||
|
@ -63,11 +58,7 @@ 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(
|
else if (numberOfWayPoints == 1 && !LocationHelper.isFirstLocationPlausible(location, track)) {
|
||||||
location,
|
|
||||||
track
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
previousLocation = null
|
previousLocation = null
|
||||||
numberOfWayPoints = 0
|
numberOfWayPoints = 0
|
||||||
track.wayPoints.removeAt(0)
|
track.wayPoints.removeAt(0)
|
||||||
|
@ -104,8 +95,7 @@ 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 = track.length + LocationHelper.calculateDistance(previousLocation, location)
|
||||||
track.length + LocationHelper.calculateDistance(previousLocation, location)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location.altitude != 0.0) {
|
if (location.altitude != 0.0) {
|
||||||
|
@ -115,23 +105,12 @@ 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> =
|
val elevationDifferences: Pair<Double, Double> = LocationHelper.calculateElevationDifferences(previousLocation, location, track)
|
||||||
LocationHelper.calculateElevationDifferences(
|
|
||||||
previousLocation,
|
|
||||||
location,
|
|
||||||
track
|
|
||||||
)
|
|
||||||
// check if any differences were calculated
|
// check if any differences were calculated
|
||||||
if (elevationDifferences != Pair(
|
if (elevationDifferences != Pair(track.positiveElevation, track.negativeElevation)) {
|
||||||
track.positiveElevation,
|
|
||||||
track.negativeElevation
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// update altitude values
|
// update altitude values
|
||||||
if (location.altitude > track.maxAltitude) track.maxAltitude =
|
if (location.altitude > track.maxAltitude) track.maxAltitude = location.altitude
|
||||||
location.altitude
|
if (location.altitude < track.minAltitude) track.minAltitude = 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
|
||||||
|
@ -143,17 +122,16 @@ 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 =
|
track.wayPoints[track.wayPoints.size - 1].isStopOver = LocationHelper.isStopOver(previousLocation, location)
|
||||||
LocationHelper.isStopOver(previousLocation, location)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// save number of satellites
|
// save number of satellites
|
||||||
val numberOfSatellites: Int
|
val numberOfSatellites: Int
|
||||||
val extras = location.extras
|
val extras = location.extras
|
||||||
numberOfSatellites = if (extras != null && extras.containsKey("satellites")) {
|
if (extras != null && extras.containsKey("satellites")) {
|
||||||
extras.getInt("satellites", 0)
|
numberOfSatellites = extras.getInt("satellites", 0)
|
||||||
} else {
|
} else {
|
||||||
0
|
numberOfSatellites = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// add current location as point to center on for later display
|
// add current location as point to center on for later display
|
||||||
|
@ -161,18 +139,7 @@ object TrackHelper {
|
||||||
track.longitude = location.longitude
|
track.longitude = location.longitude
|
||||||
|
|
||||||
// add location as new waypoint
|
// add location as new waypoint
|
||||||
track.wayPoints.add(
|
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))
|
||||||
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)
|
||||||
|
@ -186,9 +153,10 @@ object TrackHelper {
|
||||||
|
|
||||||
/* Creates GPX string for given track */
|
/* Creates GPX string for given track */
|
||||||
fun createGpxString(track: Track): String {
|
fun createGpxString(track: Track): String {
|
||||||
|
var gpxString: String
|
||||||
|
|
||||||
// add header
|
// add header
|
||||||
var gpxString: String = "<?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"
|
||||||
|
@ -259,16 +227,8 @@ 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(
|
true -> Toast.makeText(context, R.string.toast_message_poi_added, Toast.LENGTH_LONG).show()
|
||||||
context,
|
false -> Toast.makeText(context, R.string.toast_message_poi_removed, Toast.LENGTH_LONG).show()
|
||||||
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,14 +41,7 @@ object UiHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Sets layout margins for given view in DP */
|
/* Sets layout margins for given view in DP */
|
||||||
private fun setViewMargins(
|
fun setViewMargins(context: Context, view: View, left: Int = 0, right: Int = 0, top: Int= 0, bottom: Int = 0) {
|
||||||
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()
|
||||||
|
@ -63,16 +56,7 @@ object UiHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Sets layout margins for given view in percent */
|
/* Sets layout margins for given view in percent */
|
||||||
fun setViewMarginsPercentage(
|
fun setViewMarginsPercentage(context: Context, view: View, height: Int, width: Int, left: Int = 0, right: Int = 0, top: Int= 0, bottom: Int = 0) {
|
||||||
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()
|
||||||
|
@ -85,58 +69,28 @@ 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) :
|
abstract class SwipeToDeleteCallback(context: Context): ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
|
||||||
ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
|
|
||||||
|
|
||||||
private val deleteIcon =
|
private val deleteIcon = ContextCompat.getDrawable(context, R.drawable.ic_remove_circle_24dp)
|
||||||
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 =
|
private val backgroundColor = context.resources.getColor(R.color.list_card_delete_background, null)
|
||||||
context.resources.getColor(R.color.list_card_delete_background, null)
|
private val clearPaint: Paint = Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
|
||||||
private val clearPaint: Paint =
|
|
||||||
Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
|
|
||||||
|
|
||||||
override fun onMove(
|
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||||
recyclerView: RecyclerView,
|
|
||||||
viewHolder: RecyclerView.ViewHolder,
|
|
||||||
target: RecyclerView.ViewHolder
|
|
||||||
): Boolean {
|
|
||||||
// do nothing
|
// do nothing
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onChildDraw(
|
override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
|
||||||
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(
|
clearCanvas(c, itemView.right + dX, itemView.top.toFloat(), itemView.right.toFloat(), itemView.bottom.toFloat())
|
||||||
c,
|
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
|
||||||
itemView.right + dX,
|
|
||||||
itemView.top.toFloat(),
|
|
||||||
itemView.right.toFloat(),
|
|
||||||
itemView.bottom.toFloat()
|
|
||||||
)
|
|
||||||
super.onChildDraw(
|
|
||||||
c,
|
|
||||||
recyclerView,
|
|
||||||
viewHolder,
|
|
||||||
dX,
|
|
||||||
dY,
|
|
||||||
actionState,
|
|
||||||
isCurrentlyActive
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,7 @@ import java.util.*
|
||||||
/*
|
/*
|
||||||
* TracklistAdapter class
|
* TracklistAdapter class
|
||||||
*/
|
*/
|
||||||
class TracklistAdapter(private val fragment: Fragment) :
|
class TracklistAdapter(private val fragment: Fragment) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
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)
|
||||||
|
@ -55,7 +54,7 @@ class TracklistAdapter(private val fragment: Fragment) :
|
||||||
|
|
||||||
/* 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +111,7 @@ class TracklistAdapter(private val fragment: Fragment) :
|
||||||
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> =
|
val deferred: Deferred<Tracklist> = async { FileHelper.deleteTrackSuspended(context, position, 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)
|
||||||
|
@ -124,7 +122,7 @@ class TracklistAdapter(private val fragment: Fragment) :
|
||||||
|
|
||||||
/* 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
|
||||||
|
@ -145,11 +143,7 @@ class TracklistAdapter(private val fragment: Fragment) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
FileHelper.saveTracklistSuspended(
|
FileHelper.saveTracklistSuspended(context, tracklist, GregorianCalendar.getInstance().time)
|
||||||
context,
|
|
||||||
tracklist,
|
|
||||||
GregorianCalendar.getInstance().time
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,17 +152,11 @@ class TracklistAdapter(private val fragment: Fragment) :
|
||||||
private fun createTrackDataString(position: Int): String {
|
private fun createTrackDataString(position: Int): String {
|
||||||
val tracklistElement: TracklistElement = tracklist.tracklistElements[position]
|
val tracklistElement: TracklistElement = tracklist.tracklistElements[position]
|
||||||
val trackDataString: String
|
val trackDataString: String
|
||||||
trackDataString = 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 -> "${LengthUnitHelper.convertDistanceToString(
|
true -> trackDataString = "${LengthUnitHelper.convertDistanceToString(tracklistElement.length, useImperial)} • ${tracklistElement.durationString}"
|
||||||
tracklistElement.length,
|
|
||||||
useImperial
|
|
||||||
)} • ${tracklistElement.durationString}"
|
|
||||||
// CASE: no individual name set - include date
|
// CASE: no individual name set - include date
|
||||||
false -> "${tracklistElement.dateString} • ${LengthUnitHelper.convertDistanceToString(
|
false -> trackDataString = "${tracklistElement.dateString} • ${LengthUnitHelper.convertDistanceToString(tracklistElement.length, useImperial)} • ${tracklistElement.durationString}"
|
||||||
tracklistElement.length,
|
|
||||||
useImperial
|
|
||||||
)} • ${tracklistElement.durationString}"
|
|
||||||
}
|
}
|
||||||
return trackDataString
|
return trackDataString
|
||||||
}
|
}
|
||||||
|
@ -177,8 +165,7 @@ class TracklistAdapter(private val fragment: Fragment) :
|
||||||
/*
|
/*
|
||||||
* 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) :
|
private inner class DiffCallback(val oldList: Tracklist, val newList: Tracklist): DiffUtil.Callback() {
|
||||||
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]
|
||||||
|
@ -208,8 +195,7 @@ class TracklistAdapter(private val fragment: Fragment) :
|
||||||
/*
|
/*
|
||||||
* Inner class: ViewHolder for a track element
|
* Inner class: ViewHolder for a track element
|
||||||
*/
|
*/
|
||||||
private inner class TrackElementViewHolder(trackElementLayout: View) :
|
private inner class TrackElementViewHolder (trackElementLayout: View): RecyclerView.ViewHolder(trackElementLayout) {
|
||||||
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)
|
||||||
|
|
|
@ -50,20 +50,14 @@ import kotlin.math.roundToInt
|
||||||
/*
|
/*
|
||||||
* TrackFragmentLayoutHolder class
|
* TrackFragmentLayoutHolder class
|
||||||
*/
|
*/
|
||||||
data class TrackFragmentLayoutHolder(
|
data class TrackFragmentLayoutHolder(private var context: Context, private var markerListener: MapOverlay.MarkerListener, private var inflater: LayoutInflater, private var container: ViewGroup?, var track: Track) {
|
||||||
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)
|
||||||
|
|
||||||
|
|
||||||
/* Main class variables */
|
/* Main class variables */
|
||||||
val rootView: View = inflater.inflate(R.layout.fragment_track, container, false)
|
val rootView: View
|
||||||
val shareButton: ImageButton
|
val shareButton: ImageButton
|
||||||
val deleteButton: ImageButton
|
val deleteButton: ImageButton
|
||||||
val editButton: ImageButton
|
val editButton: ImageButton
|
||||||
|
@ -96,6 +90,7 @@ data class TrackFragmentLayoutHolder(
|
||||||
/* Init block */
|
/* Init block */
|
||||||
init {
|
init {
|
||||||
// find views
|
// find views
|
||||||
|
rootView = inflater.inflate(R.layout.fragment_track, container, false)
|
||||||
mapView = rootView.findViewById(R.id.map)
|
mapView = rootView.findViewById(R.id.map)
|
||||||
shareButton = rootView.findViewById(R.id.save_button)
|
shareButton = rootView.findViewById(R.id.save_button)
|
||||||
deleteButton = rootView.findViewById(R.id.delete_button)
|
deleteButton = rootView.findViewById(R.id.delete_button)
|
||||||
|
@ -139,15 +134,13 @@ data class TrackFragmentLayoutHolder(
|
||||||
}
|
}
|
||||||
|
|
||||||
// add compass to map
|
// add compass to map
|
||||||
val compassOverlay =
|
val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), mapView)
|
||||||
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 =
|
trackOverlay = MapOverlay(markerListener).createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -175,11 +168,7 @@ data class TrackFragmentLayoutHolder(
|
||||||
mapView.overlays.remove(trackOverlay)
|
mapView.overlays.remove(trackOverlay)
|
||||||
}
|
}
|
||||||
if (track.wayPoints.isNotEmpty()) {
|
if (track.wayPoints.isNotEmpty()) {
|
||||||
trackOverlay = MapOverlay(markerListener).createTrackOverlay(
|
trackOverlay = MapOverlay(markerListener).createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT)
|
||||||
context,
|
|
||||||
track,
|
|
||||||
Keys.STATE_TRACKING_NOT
|
|
||||||
)
|
|
||||||
mapView.overlays.add(trackOverlay)
|
mapView.overlays.add(trackOverlay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,9 +190,9 @@ data class TrackFragmentLayoutHolder(
|
||||||
private fun setupStatisticsViews() {
|
private fun setupStatisticsViews() {
|
||||||
|
|
||||||
// get step count string
|
// get step count string
|
||||||
val steps: String =
|
val steps: String
|
||||||
if (track.stepCount == -1f) 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 track.stepCount.roundToInt().toString()
|
else steps = track.stepCount.roundToInt().toString()
|
||||||
|
|
||||||
// populate views
|
// populate views
|
||||||
trackNameView.text = track.name
|
trackNameView.text = track.name
|
||||||
|
@ -211,29 +200,19 @@ data class TrackFragmentLayoutHolder(
|
||||||
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(
|
velocityView.text = LengthUnitHelper.convertToVelocityString(track.duration, track.recordingPaused, track.length, useImperialUnits)
|
||||||
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 =
|
maxAltitudeView.text = LengthUnitHelper.convertDistanceToString(track.maxAltitude, useImperialUnits)
|
||||||
LengthUnitHelper.convertDistanceToString(track.maxAltitude, useImperialUnits)
|
minAltitudeView.text = LengthUnitHelper.convertDistanceToString(track.minAltitude, useImperialUnits)
|
||||||
minAltitudeView.text =
|
positiveElevationView.text = LengthUnitHelper.convertDistanceToString(track.positiveElevation, useImperialUnits)
|
||||||
LengthUnitHelper.convertDistanceToString(track.minAltitude, useImperialUnits)
|
negativeElevationView.text = LengthUnitHelper.convertDistanceToString(track.negativeElevation, 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 =
|
recordingPausedView.text = DateTimeHelper.convertToReadableTime(context, track.recordingPaused)
|
||||||
DateTimeHelper.convertToReadableTime(context, track.recordingPaused)
|
|
||||||
} else {
|
} else {
|
||||||
recordingPausedLabelView.visibility = View.GONE
|
recordingPausedLabelView.visibility = View.GONE
|
||||||
recordingPausedView.visibility = View.GONE
|
recordingPausedView.visibility = View.GONE
|
||||||
|
@ -241,9 +220,8 @@ data class TrackFragmentLayoutHolder(
|
||||||
|
|
||||||
// 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)
|
Toast.makeText(context, R.string.toast_message_elevation_info, Toast.LENGTH_LONG).show()
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// make track name on statistics sheet clickable
|
// make track name on statistics sheet clickable
|
||||||
|
@ -256,8 +234,7 @@ data class TrackFragmentLayoutHolder(
|
||||||
/* Shows/hides the statistics sheet */
|
/* Shows/hides the statistics sheet */
|
||||||
private fun toggleStatisticsSheetVisibility() {
|
private fun toggleStatisticsSheetVisibility() {
|
||||||
when (statisticsSheetBehavior.state) {
|
when (statisticsSheetBehavior.state) {
|
||||||
BottomSheetBehavior.STATE_EXPANDED -> statisticsSheetBehavior.state =
|
BottomSheetBehavior.STATE_EXPANDED -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||||
BottomSheetBehavior.STATE_COLLAPSED
|
|
||||||
else -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
else -> statisticsSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,31 +246,26 @@ data class TrackFragmentLayoutHolder(
|
||||||
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 =
|
statisticsSheet.background = context.getDrawable(R.drawable.shape_statistics_background_expanded)
|
||||||
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 =
|
statisticsSheet.background = context.getDrawable(R.drawable.shape_statistics_background_collapsed)
|
||||||
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 =
|
statisticsSheet.background = context.getDrawable(R.drawable.shape_statistics_background_collapsed)
|
||||||
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 =
|
statisticsSheet.background = context.getDrawable(R.drawable.shape_statistics_background_expanded)
|
||||||
context.getDrawable(R.drawable.shape_statistics_background_expanded)
|
|
||||||
trackManagementViews.visibility = View.VISIBLE
|
trackManagementViews.visibility = View.VISIBLE
|
||||||
shareButton.visibility = View.GONE
|
shareButton.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources />
|
|
Loading…
Reference in a new issue