Code separation and cleanup for future testing

This commit is contained in:
Arty Bishop committed 2020-03-01 21:42:50 +00:00
1 parent 38b9feb829
commit b5fa5c0673
10 files changed
+311 -152

No files matched your search

@@ -20,11 +20,6 @@
package com.rtbishop.look4sat package com.rtbishop.look4sat
import android.app.Application import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import android.location.LocationManager
import android.util.Log
import androidx.core.content.edit
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@@ -36,103 +31,58 @@ import com.rtbishop.look4sat.predict4kotlin.PassPredictor
import com.rtbishop.look4sat.repo.Repository import com.rtbishop.look4sat.repo.Repository
import com.rtbishop.look4sat.repo.SatPass import com.rtbishop.look4sat.repo.SatPass
import com.rtbishop.look4sat.repo.Transmitter import com.rtbishop.look4sat.repo.Transmitter
import com.rtbishop.look4sat.utility.DataManager
import com.rtbishop.look4sat.utility.PrefsManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.FileNotFoundException
import java.io.IOException import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
class MainViewModel(application: Application) : AndroidViewModel(application) { class MainViewModel(application: Application) : AndroidViewModel(application) {
private val tag = "MainViewModel"
private val app = application private val app = application
private val keyHours = application.getString(R.string.pref_hours_ahead_key)
private val keyMinEl = application.getString(R.string.pref_min_el_key)
private val keyLat = application.getString(R.string.pref_lat_key)
private val keyLon = application.getString(R.string.pref_lon_key)
private val keyAlt = application.getString(R.string.pref_alt_key)
private val keyDelay = application.getString(R.string.pref_refresh_rate_key)
private val tleMainListFileName = "tleFile.txt"
private val tleSelectionFileName = "tleSelection"
private val _debugMessage = MutableLiveData<String>()
val debugMessage: LiveData<String> = _debugMessage
private val _satPassList = MutableLiveData<MutableList<SatPass>>()
val satPassList: LiveData<MutableList<SatPass>> = _satPassList
private val _gsp = MutableLiveData<GroundStationPosition>()
val gsp: LiveData<GroundStationPosition> = _gsp
private val urlList = listOf("https://celestrak.com/NORAD/elements/active.txt") private val urlList = listOf("https://celestrak.com/NORAD/elements/active.txt")
private val _satPassList = MutableLiveData<MutableList<SatPass>>()
private val _debugMessage = MutableLiveData<String>()
private val _gsp = MutableLiveData<GroundStationPosition>()
private var calculationJob: Job? = null private var calculationJob: Job? = null
@Inject
lateinit var locationManager: LocationManager
@Inject
lateinit var preferences: SharedPreferences
@Inject @Inject
lateinit var repository: Repository lateinit var repository: Repository
@Inject
lateinit var tleMainList: List<TLE> lateinit var dataManager: DataManager
lateinit var tleSelection: MutableList<Int> @Inject
lateinit var prefsManager: PrefsManager
init { init {
(app as Look4SatApp).appComponent.inject(this) (app as Look4SatApp).appComponent.inject(this)
loadDataFromDisk() setGroundStationPosition()
} }
val delay: Long var tleMainList = dataManager.loadTleList()
get() = preferences.getString(keyDelay, "3000")!!.toLong() var tleSelection = dataManager.loadSelectionList()
val hoursAhead: Int fun getGSP(): LiveData<GroundStationPosition> = _gsp
get() = preferences.getInt(keyHours, 8) fun getDebugMessage(): LiveData<String> = _debugMessage
fun getSatPassList(): LiveData<MutableList<SatPass>> = _satPassList
val minEl: Double fun getRefreshRate() = prefsManager.getRefreshRate()
get() = preferences.getDouble(keyMinEl, 16.0) fun getHoursAhead() = prefsManager.getHoursAhead()
fun getMinElevation() = prefsManager.getMinElevation()
fun setGroundStationPosition() { fun setGroundStationPosition() = _gsp.postValue(prefsManager.getGroundStationPosition())
val lat = preferences.getString(keyLat, "0.0")!!.toDouble()
val lon = preferences.getString(keyLon, "0.0")!!.toDouble()
val alt = preferences.getString(keyAlt, "0.0")!!.toDouble()
_gsp.postValue(GroundStationPosition(lat, lon, alt))
}
fun updateLocation() { fun updateLocation() {
val passiveProvider = LocationManager.PASSIVE_PROVIDER dataManager.getLastKnownLocation()?.let {
try { prefsManager.setGroundStationPosition(it)
val location = locationManager.getLastKnownLocation(passiveProvider) _gsp.postValue(it)
val lat = location?.latitude ?: 0.0
val lon = location?.longitude ?: 0.0
val alt = location?.altitude ?: 0.0
preferences.edit {
putString(keyLat, lat.toString())
putString(keyLon, lon.toString())
putString(keyAlt, alt.toString())
apply()
}
_gsp.postValue(GroundStationPosition(lat, lon, alt))
_debugMessage.postValue(app.getString(R.string.update_loc_success))
} catch (e: SecurityException) {
_debugMessage.postValue(app.getString(R.string.err_no_permissions))
} }
} }
fun setPassPrefs(hoursAhead: Int, minEl: Double) { fun setPassPrefs(hoursAhead: Int, minEl: Double) {
preferences.edit { prefsManager.setHoursAhead(hoursAhead)
putInt(keyHours, hoursAhead) prefsManager.setMinElevation(minEl)
putDouble(keyMinEl, minEl)
apply()
}
} }
fun updateAndSaveTleFile() { fun updateAndSaveTleFile() {
@@ -140,12 +90,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
try { try {
val inputStream = repository.getStreamForUrl(urlList) val inputStream = repository.getStreamForUrl(urlList)
val tleList = TLE.importSat(inputStream).apply { sortBy { it.name } } val tleList = TLE.importSat(inputStream).apply { sortBy { it.name } }
val fileOutStream = app.openFileOutput(tleMainListFileName, Context.MODE_PRIVATE) dataManager.saveTleList(tleList)
ObjectOutputStream(fileOutStream).apply {
writeObject(tleList)
flush()
close()
}
tleMainList = tleList tleMainList = tleList
tleSelection = mutableListOf() tleSelection = mutableListOf()
_debugMessage.postValue(app.getString(R.string.update_tle_success)) _debugMessage.postValue(app.getString(R.string.update_tle_success))
@@ -168,35 +113,21 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
fun updateAndSaveSelectionList(list: MutableList<Int>) { fun updateAndSaveSelectionList(list: MutableList<Int>) {
tleSelection = list tleSelection = list
try { dataManager.saveSelectionList(list)
val fileOutputStream = app.openFileOutput(tleSelectionFileName, Context.MODE_PRIVATE)
ObjectOutputStream(fileOutputStream).apply {
writeObject(list)
flush()
close()
}
} catch (exception: IOException) {
_debugMessage.postValue(exception.toString())
}
} }
fun getPasses() { fun getPasses() {
calculationJob?.cancel() calculationJob?.cancel()
val passList = mutableListOf<SatPass>() var passList = mutableListOf<SatPass>()
val dateNow = Date()
val dateFuture = Calendar.getInstance().let {
it.time = dateNow
it.add(Calendar.HOUR, hoursAhead)
it.time
}
calculationJob = viewModelScope.launch { calculationJob = viewModelScope.launch {
if (tleMainList.isNotEmpty() && tleSelection.isNotEmpty()) { if (tleMainList.isNotEmpty() && tleSelection.isNotEmpty()) {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
val dateNow = Date()
tleSelection.forEach { indexOfSelection -> tleSelection.forEach { indexOfSelection ->
val tle = tleMainList[indexOfSelection] val tle = tleMainList[indexOfSelection]
try { try {
val predictor = PassPredictor(tle, gsp.value!!) val predictor = PassPredictor(tle, getGSP().value!!)
val passes = predictor.getPasses(dateNow, hoursAhead, true) val passes = predictor.getPasses(dateNow, getHoursAhead(), true)
passes.forEach { passes.forEach {
passList.add(SatPass(tle, predictor, it)) passList.add(SatPass(tle, predictor, it))
} }
@@ -206,10 +137,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
_debugMessage.postValue(app.getString(R.string.err_sat_wont_pass)) _debugMessage.postValue(app.getString(R.string.err_sat_wont_pass))
} }
} }
passList.removeAll { it.pass.startTime.after(dateFuture) } passList = filterAndSortList(passList, getHoursAhead())
passList.removeAll { it.pass.endTime.before(dateNow) }
passList.removeAll { it.pass.maxEl < minEl }
passList.sortBy { it.pass.startTime }
} }
_satPassList.postValue(passList) _satPassList.postValue(passList)
} else { } else {
@@ -219,42 +147,24 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
} }
} }
private fun filterAndSortList(
list: MutableList<SatPass>,
hoursAhead: Int
): MutableList<SatPass> {
val dateNow = Date()
val dateFuture = Calendar.getInstance().let {
it.time = dateNow
it.add(Calendar.HOUR, hoursAhead)
it.time
}
list.removeAll { it.pass.startTime.after(dateFuture) }
list.removeAll { it.pass.endTime.before(dateNow) }
list.removeAll { it.pass.maxEl < getMinElevation() }
list.sortBy { it.pass.startTime }
return list
}
suspend fun getTransmittersForSat(id: Int): List<Transmitter> { suspend fun getTransmittersForSat(id: Int): List<Transmitter> {
return repository.getTransmittersForSat(id) return repository.getTransmittersForSat(id)
} }
@Suppress("UNCHECKED_CAST")
private fun loadDataFromDisk() {
tleMainList = try {
val tleStream = app.openFileInput(tleMainListFileName)
val tleList = ObjectInputStream(tleStream).readObject()
tleList as List<TLE>
} catch (exception: FileNotFoundException) {
Log.w(tag, app.getString(R.string.err_no_tle_file))
emptyList()
} catch (exception: IOException) {
Log.w(tag, exception.toString())
emptyList()
}
tleSelection = try {
val selectionStream = app.openFileInput(tleSelectionFileName)
val selectionList = ObjectInputStream(selectionStream).readObject()
selectionList as MutableList<Int>
} catch (exception: FileNotFoundException) {
Log.w(tag, app.getString(R.string.err_no_selection_file))
mutableListOf()
} catch (exception: IOException) {
Log.w(tag, exception.toString())
mutableListOf()
}
setGroundStationPosition()
}
}
fun SharedPreferences.Editor.putDouble(key: String, double: Double) {
putLong(key, double.toRawBits())
}
fun SharedPreferences.getDouble(key: String, default: Double): Double {
return Double.fromBits(getLong(key, default.toRawBits()))
} }
@@ -0,0 +1,45 @@
/*
* Look4Sat. Amateur radio and weather satellite tracker and passes predictor for Android.
* Copyright (C) 2019, 2020 Arty Bishop (bishop.arty@gmail.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.rtbishop.look4sat.di
import android.content.Context
import android.content.SharedPreferences
import android.location.LocationManager
import com.rtbishop.look4sat.utility.DataManager
import com.rtbishop.look4sat.utility.PrefsManager
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@Module
class UtilityModule {
@Singleton
@Provides
fun providePrefsManager(preferences: SharedPreferences): PrefsManager {
return PrefsManager(preferences)
}
@Singleton
@Provides
fun provideDataManager(locationManager: LocationManager, context: Context): DataManager {
return DataManager(locationManager, context)
}
}
@@ -103,7 +103,7 @@ class MainActivity : AppCompatActivity() {
} }
private fun setupObservers() { private fun setupObservers() {
viewModel.debugMessage.observe(this, Observer { message -> viewModel.getDebugMessage().observe(this, Observer { message ->
Toast.makeText(this, message, Toast.LENGTH_SHORT).show() Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
when (message) { when (message) {
getString(R.string.update_loc_success) -> drawerBinding.drawerBtnLoc.isEnabled = getString(R.string.update_loc_success) -> drawerBinding.drawerBtnLoc.isEnabled =
@@ -119,7 +119,7 @@ class MainActivity : AppCompatActivity() {
} }
}) })
viewModel.gsp.observe(this, Observer { gsp -> viewModel.getGSP().observe(this, Observer { gsp ->
drawerBinding.drawerLatValue.text = drawerBinding.drawerLatValue.text =
String.format(getString(R.string.pat_location), gsp.latitude) String.format(getString(R.string.pat_location), gsp.latitude)
drawerBinding.drawerLonValue.text = drawerBinding.drawerLonValue.text =
@@ -82,9 +82,9 @@ class MapViewFragment : Fragment() {
} }
private fun setupComponents() { private fun setupComponents() {
val delay = viewModel.delay val refreshRate = viewModel.getRefreshRate()
gsp = viewModel.gsp.value ?: GroundStationPosition(0.0, 0.0, 0.0) gsp = viewModel.getGSP().value ?: GroundStationPosition(0.0, 0.0, 0.0)
satPassList = viewModel.satPassList.value ?: emptyList() satPassList = viewModel.getSatPassList().value ?: emptyList()
if (satPassList.isNotEmpty()) { if (satPassList.isNotEmpty()) {
satPassList = satPassList.distinctBy { it.tle } satPassList = satPassList.distinctBy { it.tle }
@@ -96,8 +96,8 @@ class MapViewFragment : Fragment() {
binding.frameMap.addView(mapView) binding.frameMap.addView(mapView)
service.scheduleAtFixedRate( service.scheduleAtFixedRate(
{ mapView.invalidate() }, { mapView.invalidate() },
delay, refreshRate,
delay, refreshRate,
TimeUnit.MILLISECONDS TimeUnit.MILLISECONDS
) )
} else { } else {
@@ -97,7 +97,7 @@ class PassListFragment : Fragment() {
binding.refLayoutPassList.setColorSchemeResources(R.color.backgroundDark) binding.refLayoutPassList.setColorSchemeResources(R.color.backgroundDark)
binding.refLayoutPassList.setOnRefreshListener { calculatePasses() } binding.refLayoutPassList.setOnRefreshListener { calculatePasses() }
btnPassPrefs.setOnClickListener { btnPassPrefs.setOnClickListener {
showSatPassPrefsDialog(viewModel.hoursAhead, viewModel.minEl) showSatPassPrefsDialog(viewModel.getHoursAhead(), viewModel.getMinElevation())
} }
binding.fabSatSelect.setOnClickListener { binding.fabSatSelect.setOnClickListener {
showSelectSatDialog(viewModel.tleMainList, viewModel.tleSelection) showSelectSatDialog(viewModel.tleMainList, viewModel.tleSelection)
@@ -105,7 +105,7 @@ class PassListFragment : Fragment() {
} }
private fun setupObservers() { private fun setupObservers() {
viewModel.satPassList.observe(viewLifecycleOwner, Observer { viewModel.getSatPassList().observe(viewLifecycleOwner, Observer {
satPassList = it satPassList = it
satPassAdapter.setList(satPassList) satPassAdapter.setList(satPassList)
if (satPassList.isNotEmpty()) { if (satPassList.isNotEmpty()) {
@@ -78,8 +78,8 @@ class PolarViewFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
viewModel.satPassList.value?.let { viewModel.getSatPassList().value?.let {
val refreshRate = viewModel.delay val refreshRate = viewModel.getRefreshRate()
satPass = it[args.satPassIndex] satPass = it[args.satPassIndex]
mainActivity.supportActionBar?.title = satPass.tle.name mainActivity.supportActionBar?.title = satPass.tle.name
@@ -123,7 +123,7 @@ class SatPassAdapter(val viewModel: MainViewModel) :
binding.passLeoProgress.progress = satPass.progress binding.passLeoProgress.progress = satPass.progress
itemView.setOnClickListener { itemView.setOnClickListener {
viewModel.satPassList.value?.let { viewModel.getSatPassList().value?.let {
val satPassIndex = it.indexOf(satPass) val satPassIndex = it.indexOf(satPass)
val action = PassListFragmentDirections.actionPassToPolar(satPassIndex) val action = PassListFragmentDirections.actionPassToPolar(satPassIndex)
itemView.findNavController().navigate(action) itemView.findNavController().navigate(action)
@@ -148,7 +148,7 @@ class SatPassAdapter(val viewModel: MainViewModel) :
String.format(context.getString(R.string.pat_elevation), satPass.pass.maxEl) String.format(context.getString(R.string.pat_elevation), satPass.pass.maxEl)
itemView.setOnClickListener { itemView.setOnClickListener {
viewModel.satPassList.value?.let { viewModel.getSatPassList().value?.let {
val satPassIndex = it.indexOf(satPass) val satPassIndex = it.indexOf(satPass)
val action = PassListFragmentDirections.actionPassToPolar(satPassIndex) val action = PassListFragmentDirections.actionPassToPolar(satPassIndex)
itemView.findNavController().navigate(action) itemView.findNavController().navigate(action)
@@ -0,0 +1,112 @@
/*
* Look4Sat. Amateur radio and weather satellite tracker and passes predictor for Android.
* Copyright (C) 2019, 2020 Arty Bishop (bishop.arty@gmail.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.rtbishop.look4sat.utility
import android.content.Context
import android.location.LocationManager
import android.util.Log
import com.github.amsacode.predict4java.GroundStationPosition
import com.github.amsacode.predict4java.TLE
import com.rtbishop.look4sat.R
import java.io.FileNotFoundException
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import javax.inject.Inject
class DataManager @Inject constructor(
private val locationManager: LocationManager,
private val context: Context
) {
private val tag = "DataManager"
private val tleMainListFileName = "tleFile.txt"
private val tleSelectionFileName = "tleSelection"
fun getLastKnownLocation(): GroundStationPosition? {
val passiveProvider = LocationManager.PASSIVE_PROVIDER
var groundStationPosition: GroundStationPosition? = null
return try {
val location = locationManager.getLastKnownLocation(passiveProvider)
location?.let {
groundStationPosition =
GroundStationPosition(it.latitude, it.longitude, it.altitude)
}
groundStationPosition
} catch (e: SecurityException) {
null
}
}
fun saveSelectionList(selectionList: MutableList<Int>) {
try {
val fileOutputStream =
context.openFileOutput(tleSelectionFileName, Context.MODE_PRIVATE)
ObjectOutputStream(fileOutputStream).apply {
writeObject(selectionList)
flush()
close()
}
} catch (exception: IOException) {
}
}
fun saveTleList(tleList: MutableList<TLE>) {
try {
val fileOutStream = context.openFileOutput(tleMainListFileName, Context.MODE_PRIVATE)
ObjectOutputStream(fileOutStream).apply {
writeObject(tleList)
flush()
close()
}
} catch (exception: IOException) {
}
}
@Suppress("UNCHECKED_CAST")
fun loadTleList(): List<TLE> {
return try {
val tleStream = context.openFileInput(tleMainListFileName)
val tleList = ObjectInputStream(tleStream).readObject()
tleList as List<TLE>
} catch (exception: FileNotFoundException) {
Log.w(tag, context.getString(R.string.err_no_tle_file))
emptyList()
} catch (exception: IOException) {
Log.w(tag, exception.toString())
emptyList()
}
}
@Suppress("UNCHECKED_CAST")
fun loadSelectionList(): MutableList<Int> {
return try {
val selectionStream = context.openFileInput(tleSelectionFileName)
val selectionList = ObjectInputStream(selectionStream).readObject()
selectionList as MutableList<Int>
} catch (exception: FileNotFoundException) {
Log.w(tag, context.getString(R.string.err_no_selection_file))
mutableListOf()
} catch (exception: IOException) {
Log.w(tag, exception.toString())
mutableListOf()
}
}
}
@@ -0,0 +1,92 @@
/*
* Look4Sat. Amateur radio and weather satellite tracker and passes predictor for Android.
* Copyright (C) 2019, 2020 Arty Bishop (bishop.arty@gmail.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.rtbishop.look4sat.utility
import android.content.SharedPreferences
import androidx.core.content.edit
import com.github.amsacode.predict4java.GroundStationPosition
import javax.inject.Inject
class PrefsManager @Inject constructor(private val preferences: SharedPreferences) {
private val keyHoursAhead = "hoursAhead"
private val keyMinElevation = "minEl"
private val keyLatitude = "latitude"
private val keyLongitude = "longitude"
private val keyAltitude = "altitude"
private val keyRefreshRate = "rate"
fun getHoursAhead(): Int {
return preferences.getInt(keyHoursAhead, 8)
}
fun getMinElevation(): Double {
return preferences.getDouble(keyMinElevation, 16.0)
}
fun getRefreshRate(): Long {
return preferences.getString(keyRefreshRate, "3000")!!.toLong()
}
fun getGroundStationPosition(): GroundStationPosition {
val lat = preferences.getString(keyLatitude, "0.0")!!.toDouble()
val lon = preferences.getString(keyLongitude, "0.0")!!.toDouble()
val alt = preferences.getString(keyAltitude, "0.0")!!.toDouble()
return GroundStationPosition(lat, lon, alt)
}
fun setHoursAhead(hours: Int) {
preferences.edit {
putInt(keyHoursAhead, hours)
apply()
}
}
fun setMinElevation(minEl: Double) {
preferences.edit {
putDouble(keyMinElevation, minEl)
apply()
}
}
fun setRefreshRate(rate: Long) {
preferences.edit {
putLong(keyRefreshRate, rate)
apply()
}
}
fun setGroundStationPosition(gsp: GroundStationPosition) {
preferences.edit {
putString(keyLatitude, gsp.latitude.toString())
putString(keyLongitude, gsp.longitude.toString())
putString(keyAltitude, gsp.heightAMSL.toString())
apply()
}
}
private fun SharedPreferences.Editor.putDouble(key: String, double: Double) {
putLong(key, double.toRawBits())
}
private fun SharedPreferences.getDouble(key: String, default: Double): Double {
return Double.fromBits(getLong(key, default.toRawBits()))
}
}
+1 -1
View File
@@ -26,7 +26,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.6.0' classpath 'com.android.tools.build:gradle:3.6.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.2.1" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.2.1"
} }