kopia lustrzana https://github.com/rt-bishop/Look4Sat
Minor fixes to various classes, version code bumped
rodzic
8c67ec4808
commit
39c3a69a11
app
src/main/java/com/rtbishop/look4sat
data
database
ui
entriesScreen
mapScreen
prefsScreen
utility
fastlane/metadata/android/en-US
changelogs
whatsnew
|
@ -5,19 +5,7 @@ plugins {
|
|||
id "dagger.hilt.android.plugin"
|
||||
}
|
||||
|
||||
def keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
def keystoreProperties = new Properties()
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
|
||||
android {
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file(keystoreProperties['storeFile'])
|
||||
storePassword keystoreProperties['storePassword']
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
}
|
||||
}
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.3"
|
||||
|
||||
|
@ -25,8 +13,8 @@ android {
|
|||
applicationId "com.rtbishop.look4sat"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 222
|
||||
versionName "2.2.2"
|
||||
versionCode 230
|
||||
versionName "2.3.0"
|
||||
kapt {
|
||||
arguments {
|
||||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
|
@ -36,7 +24,6 @@ android {
|
|||
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
postprocessing {
|
||||
removeUnusedCode true
|
||||
removeUnusedResources true
|
||||
|
|
|
@ -58,17 +58,17 @@ interface SatelliteDao {
|
|||
@Transaction
|
||||
suspend fun restoreEntriesSelection(catNums: List<Int>, isSelected: Boolean) {
|
||||
clearEntriesSelection()
|
||||
catNums.forEach { catNum -> updateItemSelection(catNum, isSelected) }
|
||||
catNums.forEach { catNum -> updateEntrySelection(catNum, isSelected) }
|
||||
}
|
||||
|
||||
@Transaction
|
||||
suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean) {
|
||||
catNums.forEach { catNum -> updateItemSelection(catNum, isSelected) }
|
||||
catNums.forEach { catNum -> updateEntrySelection(catNum, isSelected) }
|
||||
}
|
||||
|
||||
@Query("UPDATE entries SET isSelected = 0")
|
||||
suspend fun clearEntriesSelection()
|
||||
|
||||
@Query("UPDATE entries SET isSelected = :isSelected WHERE catNum = :catNum")
|
||||
suspend fun updateItemSelection(catNum: Int, isSelected: Boolean)
|
||||
suspend fun updateEntrySelection(catNum: Int, isSelected: Boolean)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import androidx.room.PrimaryKey
|
|||
import com.github.amsacode.predict4java.TLE
|
||||
|
||||
@Entity(tableName = "entries")
|
||||
class SatEntry(
|
||||
data class SatEntry(
|
||||
val tle: TLE,
|
||||
var isSelected: Boolean = false,
|
||||
@PrimaryKey val catNum: Int = tle.catnum,
|
||||
|
|
|
@ -22,7 +22,7 @@ import androidx.room.PrimaryKey
|
|||
import com.squareup.moshi.Json
|
||||
|
||||
@Entity(tableName = "transmitters")
|
||||
class SatTrans(
|
||||
data class SatTrans(
|
||||
@PrimaryKey @field:Json(name = "uuid") val uuid: String,
|
||||
@field:Json(name = "description") val info: String,
|
||||
@field:Json(name = "alive") val isAlive: Boolean,
|
||||
|
|
|
@ -21,7 +21,7 @@ import com.github.amsacode.predict4java.Position
|
|||
import com.github.amsacode.predict4java.Satellite
|
||||
import org.osmdroid.views.overlay.Overlay
|
||||
|
||||
class SelectedSat(
|
||||
data class SelectedSat(
|
||||
val pass: Satellite,
|
||||
val catNum: Int,
|
||||
val name: String,
|
||||
|
|
|
@ -27,8 +27,6 @@ class Look4SatApplication : Application() {
|
|||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.plant(Timber.DebugTree())
|
||||
}
|
||||
if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import androidx.fragment.app.viewModels
|
|||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.data.model.Result
|
||||
import com.rtbishop.look4sat.data.model.SatItem
|
||||
|
@ -33,6 +32,7 @@ import com.rtbishop.look4sat.data.model.TleSource
|
|||
import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
|
||||
import com.rtbishop.look4sat.utility.RecyclerDivider
|
||||
import com.rtbishop.look4sat.utility.getNavResult
|
||||
import com.rtbishop.look4sat.utility.showSnack
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -63,7 +63,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
|
|||
}
|
||||
importWeb.setOnClickListener { findNavController().navigate(R.id.nav_dialog_sources) }
|
||||
importFile.setOnClickListener { filePicker.launch("*/*") }
|
||||
selectMode.setOnClickListener { showModesDialog() }
|
||||
selectMode.setOnClickListener { viewModel.createModesDialog(requireContext()).show() }
|
||||
selectAll.setOnClickListener { viewModel.selectCurrentItems() }
|
||||
searchBar.setOnQueryTextListener(viewModel)
|
||||
}
|
||||
|
@ -75,10 +75,6 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun showModesDialog() {
|
||||
viewModel.createModesDialog(requireContext()).show()
|
||||
}
|
||||
|
||||
private fun handleSatData(
|
||||
result: Result<List<SatItem>>,
|
||||
binding: FragmentEntriesBinding,
|
||||
|
@ -98,8 +94,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
|
|||
is Result.Error -> {
|
||||
binding.entriesProgress.visibility = View.INVISIBLE
|
||||
binding.entriesRecycler.visibility = View.VISIBLE
|
||||
val errorMsg = getString(R.string.entries_update_error)
|
||||
Snackbar.make(requireView(), errorMsg, Snackbar.LENGTH_SHORT).show()
|
||||
requireView().showSnack(getString(R.string.entries_update_error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,6 +142,7 @@ class EntriesViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun filterByQuery(items: List<SatItem>, query: String): List<SatItem> {
|
||||
shouldSelectAll = true
|
||||
if (query.isBlank()) return items
|
||||
return try {
|
||||
items.filter { it.catNum == query.toInt() }
|
||||
|
|
|
@ -126,7 +126,11 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||
ContextCompat.getColor(requireContext(), R.color.themeLight)
|
||||
setTextIcon(it.key.tle.name)
|
||||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
|
||||
position = GeoPoint(it.value.lat, it.value.lon)
|
||||
try {
|
||||
position = GeoPoint(it.value.lat, it.value.lon)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.d("Position: $position")
|
||||
}
|
||||
setOnMarkerClickListener { _, _ ->
|
||||
mapViewModel.selectSatellite(it.key)
|
||||
return@setOnMarkerClickListener true
|
||||
|
@ -138,7 +142,11 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||
setInfoWindow(null)
|
||||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
|
||||
icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_map_sat)
|
||||
position = GeoPoint(it.value.lat, it.value.lon)
|
||||
try {
|
||||
position = GeoPoint(it.value.lat, it.value.lon)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.d("Position: $position")
|
||||
}
|
||||
setOnMarkerClickListener { _, _ ->
|
||||
mapViewModel.selectSatellite(it.key)
|
||||
return@setOnMarkerClickListener true
|
||||
|
|
|
@ -40,17 +40,13 @@ class Orientation @Inject constructor(
|
|||
private val mRotationMatrix = FloatArray(9)
|
||||
private val mOrientationValues = FloatArray(3)
|
||||
private val mOneRadDegrees = 57.295779513f
|
||||
private var mLastAccuracy: Int = SensorManager.SENSOR_STATUS_UNRELIABLE
|
||||
private var mListener: OrientationListener? = null
|
||||
|
||||
fun startListening(listener: OrientationListener) {
|
||||
when {
|
||||
mListener === listener -> return
|
||||
else -> {
|
||||
mListener = listener
|
||||
mSensorManager.registerListener(this, mRotationSensor, 16000)
|
||||
}
|
||||
}
|
||||
if (mListener !== listener) {
|
||||
mListener = listener
|
||||
mSensorManager.registerListener(this, mRotationSensor, 16000)
|
||||
} else return
|
||||
}
|
||||
|
||||
fun stopListening() {
|
||||
|
@ -58,16 +54,11 @@ class Orientation @Inject constructor(
|
|||
mListener = null
|
||||
}
|
||||
|
||||
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
|
||||
mLastAccuracy = accuracy
|
||||
}
|
||||
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
|
||||
|
||||
override fun onSensorChanged(event: SensorEvent) {
|
||||
when {
|
||||
mListener == null -> return
|
||||
mLastAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE -> return
|
||||
event.sensor == mRotationSensor -> updateOrientation(event.values)
|
||||
}
|
||||
if (mListener == null) return
|
||||
else if (event.sensor == mRotationSensor) updateOrientation(event.values)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
|
|
|
@ -31,43 +31,36 @@ import com.rtbishop.look4sat.utility.RecyclerDivider
|
|||
import com.rtbishop.look4sat.utility.formatForTimer
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PolarFragment : Fragment(R.layout.fragment_polar), Orientation.OrientationListener {
|
||||
|
||||
@Inject
|
||||
lateinit var orientation: Orientation
|
||||
class PolarFragment : Fragment(R.layout.fragment_polar) {
|
||||
|
||||
private val viewModel: PolarViewModel by viewModels()
|
||||
private var polarView: PolarView? = null
|
||||
private var magneticDeclination = 0f
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val binding = FragmentPolarBinding.bind(view)
|
||||
val catNum = requireArguments().getInt("catNum")
|
||||
val aosTime = requireArguments().getLong("aosTime")
|
||||
magneticDeclination = viewModel.getMagDeclination()
|
||||
viewModel.getPass(catNum, aosTime).observe(viewLifecycleOwner) { pass ->
|
||||
polarView = PolarView(requireContext()).apply { setPass(pass) }
|
||||
binding.frame.addView(polarView)
|
||||
observeTransmitters(pass, binding)
|
||||
}
|
||||
viewModel.azimuth.observe(viewLifecycleOwner, { trueNorthAzimuth ->
|
||||
polarView?.rotation = -trueNorthAzimuth
|
||||
})
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (viewModel.shouldUseCompass()) orientation.startListening(this)
|
||||
viewModel.enableSensor()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (viewModel.shouldUseCompass()) orientation.stopListening()
|
||||
}
|
||||
|
||||
override fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) {
|
||||
polarView?.rotation = -(azimuth + magneticDeclination)
|
||||
viewModel.disableSensor()
|
||||
}
|
||||
|
||||
private fun observeTransmitters(satPass: SatPass, binding: FragmentPolarBinding) {
|
||||
|
|
|
@ -17,10 +17,7 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.ui.polarScreen
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.liveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.*
|
||||
import com.rtbishop.look4sat.data.model.Result
|
||||
import com.rtbishop.look4sat.data.repository.PassesRepo
|
||||
import com.rtbishop.look4sat.data.repository.SatelliteRepo
|
||||
|
@ -32,10 +29,15 @@ import javax.inject.Inject
|
|||
|
||||
@HiltViewModel
|
||||
class PolarViewModel @Inject constructor(
|
||||
private val orientation: Orientation,
|
||||
private val prefsManager: PrefsManager,
|
||||
private val passesRepo: PassesRepo,
|
||||
private val satelliteRepo: SatelliteRepo
|
||||
) : ViewModel() {
|
||||
) : ViewModel(), Orientation.OrientationListener {
|
||||
|
||||
private val magDeclination = prefsManager.getMagDeclination()
|
||||
private val _azimuth = MutableLiveData<Float>()
|
||||
val azimuth: LiveData<Float> = _azimuth
|
||||
|
||||
fun getAppTimer() = liveData {
|
||||
while (true) {
|
||||
|
@ -53,14 +55,18 @@ class PolarViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun shouldUseCompass(): Boolean {
|
||||
return prefsManager.shouldUseCompass()
|
||||
fun enableSensor() {
|
||||
if (prefsManager.shouldUseCompass()) orientation.startListening(this)
|
||||
}
|
||||
|
||||
fun getMagDeclination(): Float {
|
||||
return prefsManager.getMagDeclination()
|
||||
fun disableSensor() {
|
||||
if (prefsManager.shouldUseCompass()) orientation.stopListening()
|
||||
}
|
||||
|
||||
fun getSatTransmitters(satId: Int) =
|
||||
satelliteRepo.getSatTransmitters(satId).asLiveData(viewModelScope.coroutineContext)
|
||||
|
||||
override fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) {
|
||||
_azimuth.value = azimuth + magDeclination
|
||||
}
|
||||
}
|
|
@ -26,11 +26,11 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.utility.PrefsManager
|
||||
import com.rtbishop.look4sat.utility.QthConverter
|
||||
import com.rtbishop.look4sat.utility.round
|
||||
import com.rtbishop.look4sat.utility.showSnack
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -39,20 +39,15 @@ class PrefsFragment : PreferenceFragmentCompat() {
|
|||
|
||||
@Inject
|
||||
lateinit var locationManager: LocationManager
|
||||
|
||||
@Inject
|
||||
lateinit var prefsManager: PrefsManager
|
||||
|
||||
@Inject
|
||||
lateinit var qthConverter: QthConverter
|
||||
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted) {
|
||||
setPositionFromGPS()
|
||||
} else {
|
||||
showSnack(getString(R.string.pref_pos_gps_error))
|
||||
}
|
||||
if (isGranted) setPositionFromGPS()
|
||||
else requireView().showSnack(getString(R.string.pref_pos_gps_error))
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
|
@ -79,10 +74,10 @@ class PrefsFragment : PreferenceFragmentCompat() {
|
|||
private fun setPositionFromQth(qthString: String): Boolean {
|
||||
qthConverter.qthToLocation(qthString)?.let { gsp ->
|
||||
prefsManager.setStationPosition(gsp.latitude, gsp.longitude, gsp.heightAMSL)
|
||||
showSnack(getString(R.string.pref_pos_success))
|
||||
requireView().showSnack(getString(R.string.pref_pos_success))
|
||||
return true
|
||||
}
|
||||
showSnack(getString(R.string.pref_pos_qth_error))
|
||||
requireView().showSnack(getString(R.string.pref_pos_qth_error))
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -96,14 +91,8 @@ class PrefsFragment : PreferenceFragmentCompat() {
|
|||
val longitude = location.longitude.round(4)
|
||||
val altitude = location.altitude.round(1)
|
||||
prefsManager.setStationPosition(latitude, longitude, altitude)
|
||||
showSnack(getString(R.string.pref_pos_success))
|
||||
} else showSnack(getString(R.string.pref_pos_gps_null))
|
||||
requireView().showSnack(getString(R.string.pref_pos_success))
|
||||
} else requireView().showSnack(getString(R.string.pref_pos_gps_null))
|
||||
} else requestPermissionLauncher.launch(locPermString)
|
||||
}
|
||||
|
||||
private fun showSnack(message: String) {
|
||||
Snackbar.make(requireView(), message, Snackbar.LENGTH_SHORT)
|
||||
.setAnchorView(R.id.nav_bottom)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.net.Network
|
|||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
|
@ -33,6 +34,8 @@ import androidx.navigation.Navigator
|
|||
import androidx.navigation.fragment.findNavController
|
||||
import com.github.amsacode.predict4java.GroundStationPosition
|
||||
import com.github.amsacode.predict4java.Satellite
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.rtbishop.look4sat.R
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -77,6 +80,12 @@ fun Satellite.getPredictor(stationPosition: GroundStationPosition): PassPredicto
|
|||
return PassPredictor(this, stationPosition)
|
||||
}
|
||||
|
||||
fun View.showSnack(message: String) {
|
||||
Snackbar.make(this, message, Snackbar.LENGTH_SHORT)
|
||||
.setAnchorView(R.id.nav_bottom)
|
||||
.show()
|
||||
}
|
||||
|
||||
fun NavController.navigateSafe(
|
||||
@IdRes resId: Int,
|
||||
args: Bundle? = null,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Added satellite items filtering by transmitter mode
|
||||
Now updating recyclers views asyncronously
|
||||
Switched to concurrent satellite data update
|
||||
Minor visual changes to map info
|
||||
Separated application screens logic
|
||||
Bug fixes, huge code cleanup
|
|
@ -1,8 +1,6 @@
|
|||
Fixed NullPointerException in EntriesFragment's update from file
|
||||
Fixed IllegalArgumentException on passes navigate action
|
||||
Fixed select all logic in Entries adapter
|
||||
Now correctly popping back stack on PolarView pass end
|
||||
Now using passes screen as a start destination
|
||||
Added zipped TLE import from Mike McCants' web site
|
||||
Added McCants' classified and integrated TLEs to default sources
|
||||
Added privacy policy to info screen, switched to GPL v3 licence
|
||||
Added satellite items filtering by transmitter mode
|
||||
Now updating recyclers views asyncronously
|
||||
Switched to concurrent satellite data update
|
||||
Minor visual changes to map info
|
||||
Separated application screens logic
|
||||
Bug fixes, huge code cleanup
|
Ładowanie…
Reference in New Issue