Further cleanup and refactoring

This commit is contained in:
Arty Bishop committed 2020-04-25 17:03:14 +01:00
1 parent 499febefcb
commit 1824cd8af0
12 files changed
+184 -138

No files matched your search

@@ -0,0 +1,7 @@
package com.rtbishop.look4sat.data
sealed class Result<out T : Any> {
data class Success<out T : Any>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object InProgress : Result<Nothing>()
}
@@ -19,13 +19,11 @@
package com.rtbishop.look4sat.network
import android.util.Log
import com.rtbishop.look4sat.data.Transmitter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException
import java.io.InputStream
import java.io.SequenceInputStream
import java.util.*
@@ -38,16 +36,12 @@ class RemoteDataSource @Inject constructor(
override suspend fun fetchTleStream(urlList: List<String>): InputStream {
val streamTable = Hashtable<String, InputStream>()
try {
withContext(Dispatchers.IO) {
urlList.forEach {
val request = Request.Builder().url(it).build()
val stream = client.newCall(request).execute().body()?.byteStream()
streamTable[it] = stream
}
withContext(Dispatchers.IO) {
urlList.forEach {
val request = Request.Builder().url(it).build()
val stream = client.newCall(request).execute().body()?.byteStream()
streamTable[it] = stream
}
} catch (e: IOException) {
Log.w("remoteDataSource", e.toString())
}
return SequenceInputStream(streamTable.elements())
}
@@ -19,7 +19,6 @@
package com.rtbishop.look4sat.repo
import android.util.Log
import com.github.amsacode.predict4java.TLE
import com.rtbishop.look4sat.data.SatEntry
import com.rtbishop.look4sat.data.Transmitter
@@ -27,7 +26,6 @@ import com.rtbishop.look4sat.network.RemoteSource
import com.rtbishop.look4sat.persistence.LocalSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException
import javax.inject.Inject
class DefaultRepository @Inject constructor(
@@ -35,18 +33,12 @@ class DefaultRepository @Inject constructor(
private val remoteSource: RemoteSource
) : Repository {
private val tag = "defaultRepository"
override suspend fun updateEntriesFrom(urlList: List<String>) {
val stream = remoteSource.fetchTleStream(urlList)
try {
withContext(Dispatchers.IO) {
val tleList = TLE.importSat(stream)
val entries = tleList.map { SatEntry(it) }
localSource.insertEntries(entries)
}
} catch (e: Exception) {
Log.w(tag, e.toString())
withContext(Dispatchers.IO) {
val tleList = TLE.importSat(stream)
val entries = tleList.map { SatEntry(it) }
localSource.insertEntries(entries)
}
}
@@ -65,12 +57,7 @@ class DefaultRepository @Inject constructor(
////////////////////////////////////////////////////////////////////////////////////////////////
override suspend fun updateTransmitters() {
try {
val list = remoteSource.fetchTransmitters()
localSource.insertTransmitters(list)
} catch (e: IOException) {
Log.w(tag, e.toString())
}
localSource.insertTransmitters(remoteSource.fetchTransmitters())
}
override suspend fun getTransmittersByCatNum(catNum: Int): List<Transmitter> {
@@ -43,6 +43,7 @@ import androidx.navigation.ui.setupWithNavController
import com.rtbishop.look4sat.Look4SatApp
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.dagger.ViewModelFactory
import com.rtbishop.look4sat.data.Result
import com.rtbishop.look4sat.databinding.ActivityMainBinding
import com.rtbishop.look4sat.databinding.DrawerHeaderBinding
import kotlinx.coroutines.delay
@@ -54,7 +55,6 @@ class MainActivity : AppCompatActivity() {
private val permLocationCode = 101
private val permLocation = Manifest.permission.ACCESS_FINE_LOCATION
private val permGranted = PackageManager.PERMISSION_GRANTED
private val githubUrl = "https://github.com/rt-bishop/LookingSat"
@Inject
lateinit var viewModelFactory: ViewModelFactory
@@ -116,14 +116,32 @@ class MainActivity : AppCompatActivity() {
}
private fun setupObservers() {
viewModel.getGSP().observe(this, Observer { gsp ->
drawerBinding.drawerLatValue.text =
String.format(getString(R.string.pat_location), gsp.latitude)
drawerBinding.drawerLonValue.text =
String.format(getString(R.string.pat_location), gsp.longitude)
viewModel.getGSP().observe(this, Observer { result ->
when (result) {
is Result.Success -> {
drawerBinding.drawerLatValue.text =
String.format(getString(R.string.pat_location), result.data.latitude)
drawerBinding.drawerLonValue.text =
String.format(getString(R.string.pat_location), result.data.longitude)
}
is Result.Error -> {
when (result.exception) {
is SecurityException -> toast(getString(R.string.err_no_permissions))
is IllegalArgumentException -> toast(getString(R.string.update_loc_failure))
}
}
}
})
viewModel.getDebugMessage().observe(this, Observer { message ->
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
viewModel.getUpdateStatus().observe(this, Observer { result ->
when (result) {
is Result.Success -> {
when (result.data) {
0 -> toast(getString(R.string.update_tle_success))
1 -> toast(getString(R.string.update_trans_success))
}
}
is Result.Error -> toast(getString(R.string.update_failure))
}
})
}
@@ -141,12 +159,17 @@ class MainActivity : AppCompatActivity() {
it.lockButton()
}
drawerBinding.drawerBtnGithub.setOnClickListener {
val githubIntent = Intent(Intent.ACTION_VIEW, Uri.parse(githubUrl))
val gitHubUrl = "https://github.com/rt-bishop/LookingSat"
val githubIntent = Intent(Intent.ACTION_VIEW, Uri.parse(gitHubUrl))
startActivity(githubIntent)
}
drawerBinding.drawerBtnExit.setOnClickListener { finish() }
}
private fun toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
private fun View.lockButton() {
lifecycleScope.launch {
this@lockButton.isEnabled = false
@@ -158,8 +181,7 @@ class MainActivity : AppCompatActivity() {
private fun requestLocationUpdate() {
if (ContextCompat.checkSelfPermission(this, permLocation) != permGranted) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permLocation)) {
Toast.makeText(this, getString(R.string.err_no_permissions), Toast.LENGTH_LONG)
.show()
toast(getString(R.string.err_no_permissions))
} else {
ActivityCompat.requestPermissions(this, arrayOf(permLocation), permLocationCode)
}
@@ -19,8 +19,10 @@
package com.rtbishop.look4sat.ui
import android.location.LocationManager
import androidx.lifecycle.*
import com.github.amsacode.predict4java.GroundStationPosition
import com.rtbishop.look4sat.data.Result
import com.rtbishop.look4sat.data.SatEntry
import com.rtbishop.look4sat.data.SatPass
import com.rtbishop.look4sat.repo.Repository
@@ -39,55 +41,70 @@ class SharedViewModel @Inject constructor(
private val repository: Repository
) : ViewModel() {
private val _satPassList = MutableLiveData<Result<MutableList<SatPass>>>()
private val _updateStatus = MutableLiveData<Result<Int>>()
private val _gsp = MutableLiveData<Result<GroundStationPosition>>().apply {
value = Result.Success(prefsManager.getPosition())
}
private val urlList = listOf("https://celestrak.com/NORAD/elements/active.txt")
private val _gsp = MutableLiveData(prefsManager.getPosition())
private val _satPassList = MutableLiveData<MutableList<SatPass>>()
private val _isListRefreshing = MutableLiveData<Boolean>()
private val _debugMessage = MutableLiveData<String>()
private var calculationJob: Job? = null
var isFirstLaunch = true
fun getTransmittersForSat(id: Int) = liveData { emit(repository.getTransmittersByCatNum(id)) }
fun getGSP(): LiveData<GroundStationPosition> = _gsp
fun getSatPassList(): LiveData<MutableList<SatPass>> = _satPassList
fun getRefreshing(): LiveData<Boolean> = _isListRefreshing
fun getDebugMessage(): LiveData<String> = _debugMessage
fun getPassList(): LiveData<Result<MutableList<SatPass>>> = _satPassList
fun getUpdateStatus(): LiveData<Result<Int>> = _updateStatus
fun getGSP(): LiveData<Result<GroundStationPosition>> = _gsp
fun getRefreshRate() = prefsManager.getRefreshRate()
fun getHoursAhead() = prefsManager.getHoursAhead()
fun getMinElevation() = prefsManager.getMinElevation()
suspend fun getAllEntries(): List<SatEntry> {
return repository.getAllEntries()
}
fun setPositionFromPref() = _gsp.postValue(Result.Success(prefsManager.getPosition()))
suspend fun getAllEntries() = repository.getAllEntries()
fun setPassPrefs(hoursAhead: Int, minEl: Double) {
prefsManager.setHoursAhead(hoursAhead)
prefsManager.setMinElevation(minEl)
}
fun setPositionFromPref() = _gsp.postValue(prefsManager.getPosition())
fun updatePosition() {
prefsManager.getLastKnownPosition().let {
prefsManager.setPosition(it)
_gsp.postValue(it)
_debugMessage.postValue("Location was updated")
val provider = LocationManager.PASSIVE_PROVIDER
try {
val location = prefsManager.locationManager.getLastKnownLocation(provider)
location.let {
if (it == null) {
_gsp.postValue(Result.Error(IllegalArgumentException()))
} else {
val gsp = GroundStationPosition(it.latitude, it.longitude, it.altitude)
prefsManager.setPosition(gsp)
_gsp.postValue(Result.Success(gsp))
}
}
} catch (e: SecurityException) {
_gsp.postValue(Result.Error(e))
}
}
fun updateEntries() {
viewModelScope.launch(Dispatchers.IO) {
val selected = repository.getSelectedEntries().map { it.catNum }
repository.updateEntriesFrom(urlList)
repository.updateEntriesSelection(selected)
_debugMessage.postValue("TLE file was updated")
try {
val selected = repository.getSelectedEntries().map { it.catNum }
repository.updateEntriesFrom(urlList)
repository.updateEntriesSelection(selected)
_updateStatus.postValue(Result.Success(0))
} catch (e: Exception) {
_updateStatus.postValue(Result.Error(e))
}
}
}
fun updateTransmitters() {
viewModelScope.launch(Dispatchers.IO) {
repository.updateTransmitters()
_debugMessage.postValue("Transceivers database was updated")
try {
repository.updateTransmitters()
_updateStatus.postValue(Result.Success(1))
} catch (e: Exception) {
_updateStatus.postValue(Result.Error(e))
}
}
}
@@ -99,18 +116,17 @@ class SharedViewModel @Inject constructor(
}
fun calculatePasses() {
_isListRefreshing.postValue(true)
_satPassList.postValue(Result.InProgress)
calculationJob?.cancel()
var passList = mutableListOf<SatPass>()
val dateNow = Date()
val gsp = getGSP().value ?: GroundStationPosition(0.0, 0.0, 0.0)
val gsp = prefsManager.getPosition()
calculationJob = viewModelScope.launch(Dispatchers.Default) {
repository.getSelectedEntries().forEach {
passList.addAll(getPassesForEntries(it, dateNow, gsp))
}
passList = filterAndSortPasses(passList, dateNow, getHoursAhead())
_satPassList.postValue(passList)
_isListRefreshing.postValue(false)
_satPassList.postValue(Result.Success(passList))
}
}
@@ -29,16 +29,13 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.SatPass
import com.rtbishop.look4sat.databinding.ItemPassGeoBinding
import com.rtbishop.look4sat.databinding.ItemPassLeoBinding
import com.rtbishop.look4sat.ui.SharedViewModel
import com.rtbishop.look4sat.ui.fragments.PassListFragmentDirections
import java.text.SimpleDateFormat
import java.util.*
class SatPassAdapter(val viewModel: SharedViewModel) :
class SatPassAdapter(private var satPassList: MutableList<SatPass>) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var satPassList = mutableListOf<SatPass>()
fun setList(list: MutableList<SatPass>) {
satPassList = list
notifyDataSetChanged()
@@ -123,11 +120,9 @@ class SatPassAdapter(val viewModel: SharedViewModel) :
binding.passLeoProgress.progress = satPass.progress
itemView.setOnClickListener {
viewModel.getSatPassList().value?.let {
val satPassIndex = it.indexOf(satPass)
val action = PassListFragmentDirections.actionPassToPolar(satPassIndex)
itemView.findNavController().navigate(action)
}
val passIndex = satPassList.indexOf(satPass)
val action = PassListFragmentDirections.actionPassToPolar(passIndex)
itemView.findNavController().navigate(action)
}
}
}
@@ -148,11 +143,9 @@ class SatPassAdapter(val viewModel: SharedViewModel) :
String.format(context.getString(R.string.pat_elevation), satPass.pass.maxEl)
itemView.setOnClickListener {
viewModel.getSatPassList().value?.let {
val satPassIndex = it.indexOf(satPass)
val action = PassListFragmentDirections.actionPassToPolar(satPassIndex)
itemView.findNavController().navigate(action)
}
val passIndex = satPassList.indexOf(satPass)
val action = PassListFragmentDirections.actionPassToPolar(passIndex)
itemView.findNavController().navigate(action)
}
}
}
@@ -36,6 +36,7 @@ import com.github.amsacode.predict4java.TLE
import com.rtbishop.look4sat.Look4SatApp
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.dagger.ViewModelFactory
import com.rtbishop.look4sat.data.Result
import com.rtbishop.look4sat.data.SatPass
import com.rtbishop.look4sat.databinding.FragmentMapViewBinding
import com.rtbishop.look4sat.ui.MainActivity
@@ -56,8 +57,8 @@ class MapViewFragment : Fragment(R.layout.fragment_map_view) {
private lateinit var mapView: MapView
private lateinit var predictor: PassPredictor
private lateinit var selectedSat: TLE
private lateinit var gsp: GroundStationPosition
private lateinit var satPassList: List<SatPass>
private var gsp = GroundStationPosition(0.0, 0.0, 0.0)
private var satPassList = emptyList<SatPass>()
private var checkedItem = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -66,9 +67,29 @@ class MapViewFragment : Fragment(R.layout.fragment_map_view) {
mainActivity = activity as MainActivity
(mainActivity.application as Look4SatApp).appComponent.inject(this)
viewModel = ViewModelProvider(mainActivity, modelFactory).get(SharedViewModel::class.java)
gsp = viewModel.getGSP().value ?: GroundStationPosition(0.0, 0.0, 0.0)
satPassList = viewModel.getSatPassList().value ?: emptyList()
setupObservers(binding)
}
private fun setupObservers(binding: FragmentMapViewBinding) {
viewModel.getGSP().observe(viewLifecycleOwner, androidx.lifecycle.Observer { result ->
when (result) {
is Result.Success -> {
gsp = result.data
}
}
})
viewModel.getPassList().observe(viewLifecycleOwner, androidx.lifecycle.Observer { result ->
when (result) {
is Result.Success -> {
satPassList = result.data
setupMapView(binding)
}
}
})
}
private fun setupMapView(binding: FragmentMapViewBinding) {
if (satPassList.isNotEmpty()) {
satPassList = satPassList.distinctBy { it.tle }
satPassList = satPassList.sortedBy { it.tle.name }
@@ -36,6 +36,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import com.rtbishop.look4sat.Look4SatApp
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.dagger.ViewModelFactory
import com.rtbishop.look4sat.data.Result
import com.rtbishop.look4sat.data.SatEntry
import com.rtbishop.look4sat.data.SatPass
import com.rtbishop.look4sat.databinding.FragmentPassListBinding
@@ -66,7 +67,7 @@ class PassListFragment : Fragment(R.layout.fragment_pass_list) {
mainActivity = activity as MainActivity
(mainActivity.application as Look4SatApp).appComponent.inject(this)
viewModel = ViewModelProvider(mainActivity, modelFactory).get(SharedViewModel::class.java)
satPassAdapter = SatPassAdapter(viewModel)
satPassAdapter = SatPassAdapter(satPassList)
aosTimerText = mainActivity.findViewById(R.id.toolbar_timer)
btnPassPrefs = mainActivity.findViewById(R.id.toolbar_filter)
@@ -80,20 +81,25 @@ class PassListFragment : Fragment(R.layout.fragment_pass_list) {
}
private fun setupObservers(binding: FragmentPassListBinding) {
viewModel.getSatPassList().observe(viewLifecycleOwner, Observer {
satPassList = it
satPassAdapter.setList(satPassList)
if (satPassList.isNotEmpty()) {
binding.layoutInfo.visibility = View.INVISIBLE
binding.recPassList.visibility = View.VISIBLE
} else {
binding.layoutInfo.visibility = View.VISIBLE
binding.recPassList.visibility = View.INVISIBLE
viewModel.getPassList().observe(viewLifecycleOwner, Observer { result ->
when (result) {
is Result.Success -> {
satPassList = result.data
satPassAdapter.setList(satPassList)
if (satPassList.isNotEmpty()) {
binding.layoutInfo.visibility = View.INVISIBLE
binding.recPassList.visibility = View.VISIBLE
} else {
binding.layoutInfo.visibility = View.VISIBLE
binding.recPassList.visibility = View.INVISIBLE
}
setTimer()
binding.refLayoutPassList.isRefreshing = false
}
is Result.InProgress -> {
binding.refLayoutPassList.isRefreshing = true
}
}
setTimer()
})
viewModel.getRefreshing().observe(viewLifecycleOwner, Observer {
binding.refLayoutPassList.isRefreshing = it
})
}
@@ -35,6 +35,7 @@ import com.github.amsacode.predict4java.SatPos
import com.rtbishop.look4sat.Look4SatApp
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.dagger.ViewModelFactory
import com.rtbishop.look4sat.data.Result
import com.rtbishop.look4sat.data.SatPass
import com.rtbishop.look4sat.databinding.FragmentPolarViewBinding
import com.rtbishop.look4sat.ui.MainActivity
@@ -71,25 +72,40 @@ class PolarViewFragment : Fragment(R.layout.fragment_polar_view) {
adapter = transmitterAdapter
}
viewModel.getSatPassList()
.observe(viewLifecycleOwner, androidx.lifecycle.Observer { mutableList ->
satPass = mutableList[args.satPassIndex]
mainActivity.supportActionBar?.title = satPass.tle.name
polarView = PolarView(mainActivity, refreshRate, binding)
binding.framePolar.addView(polarView)
refreshView()
viewModel.getTransmittersForSat(satPass.tle.catnum)
.observe(viewLifecycleOwner, androidx.lifecycle.Observer {
if (it.isNotEmpty()) {
transmitterAdapter.setList(it)
transmitterAdapter.notifyDataSetChanged()
binding.recPolar.visibility = View.VISIBLE
binding.tvPolarNoTrans.visibility = View.INVISIBLE
} else {
binding.recPolar.visibility = View.INVISIBLE
binding.tvPolarNoTrans.visibility = View.VISIBLE
}
})
observePasses(refreshRate, binding)
}
private fun observePasses(
refreshRate: Long,
binding: FragmentPolarViewBinding
) {
viewModel.getPassList()
.observe(viewLifecycleOwner, androidx.lifecycle.Observer { result ->
when (result) {
is Result.Success -> {
satPass = result.data[args.satPassIndex]
mainActivity.supportActionBar?.title = satPass.tle.name
polarView = PolarView(mainActivity, refreshRate, binding)
binding.framePolar.addView(polarView)
observeTransmitters(binding)
refreshView()
}
}
})
}
private fun observeTransmitters(binding: FragmentPolarViewBinding) {
viewModel.getTransmittersForSat(satPass.tle.catnum)
.observe(viewLifecycleOwner, androidx.lifecycle.Observer {
if (it.isNotEmpty()) {
transmitterAdapter.setList(it)
transmitterAdapter.notifyDataSetChanged()
binding.recPolar.visibility = View.VISIBLE
binding.tvPolarNoTrans.visibility = View.INVISIBLE
} else {
binding.recPolar.visibility = View.INVISIBLE
binding.tvPolarNoTrans.visibility = View.VISIBLE
}
})
}
@@ -21,16 +21,14 @@ package com.rtbishop.look4sat.utility
import android.content.SharedPreferences
import android.location.LocationManager
import android.util.Log
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 locationManager: LocationManager
val locationManager: LocationManager
) {
private val tag = "prefsManager"
private val keyHoursAhead = "hoursAhead"
private val keyMinElevation = "minEl"
private val keyLatitude = "latitude"
@@ -91,22 +89,6 @@ class PrefsManager @Inject constructor(
}
}
fun getLastKnownPosition(): GroundStationPosition {
val provider = LocationManager.PASSIVE_PROVIDER
var gsp = GroundStationPosition(0.0, 0.0, 0.0)
return try {
val location = locationManager.getLastKnownLocation(provider)
location?.let {
gsp =
GroundStationPosition(it.latitude, it.longitude, it.altitude)
}
gsp
} catch (e: SecurityException) {
Log.w(tag, "No permissions")
gsp
}
}
private fun SharedPreferences.Editor.putDouble(key: String, double: Double) {
putLong(key, double.toRawBits())
}
+1
View File
@@ -145,4 +145,5 @@
<string name="btn_all_none">Все/Нет</string>
<string name="map_gsp">СТЦ</string>
<string name="update_loc_failure">Проверьте ваши настройки GPS</string>
</resources>
+1
View File
@@ -27,6 +27,7 @@
<string name="update_loc">Update location</string>
<string name="update_loc_success">Location was updated</string>
<string name="update_loc_failure">Check your Location settings</string>
<string name="update_tle">Update TLE file</string>
<string name="update_tle_success">TLE file was updated</string>
<string name="update_trans">Update transceivers</string>