mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Major classes refactoring and code cleanup
This commit is contained in:
1 parent
6572dd4eab
commit
f7f317cf8b
67 files changed
+610
-754
No files matched your search
File renamed without changes.
+4
-4
@@ -118,10 +118,10 @@
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"catnum"
|
||||
],
|
||||
"autoGenerate": false
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
@@ -186,10 +186,10 @@
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"uuid"
|
||||
],
|
||||
"autoGenerate": false
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
@@ -12,7 +12,7 @@
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".presentation.MainApplication"
|
||||
android:name=".MainApplication"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
@@ -24,7 +24,7 @@
|
||||
android:theme="@style/Theme.Look4Sat.Main"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".presentation.MainActivity"
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Look4Sat.SplashScreen">
|
||||
<intent-filter>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import com.rtbishop.look4sat.presentation.MainScreen
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
installSplashScreen()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent { MainTheme { MainScreen() } }
|
||||
}
|
||||
|
||||
override fun attachBaseContext(newBase: Context?) {
|
||||
val newConfig = Configuration(newBase?.resources?.configuration)
|
||||
newConfig.fontScale = 1.0f
|
||||
applyOverrideConfiguration(newConfig)
|
||||
super.attachBaseContext(newBase)
|
||||
}
|
||||
}
|
||||
+7
-8
@@ -15,12 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation
|
||||
package com.rtbishop.look4sat
|
||||
|
||||
import android.app.Application
|
||||
import com.rtbishop.look4sat.BuildConfig
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.framework.SettingsManager
|
||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import timber.log.Timber
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -31,10 +30,10 @@ import javax.inject.Inject
|
||||
class MainApplication : Application() {
|
||||
|
||||
@Inject
|
||||
lateinit var repository: IDataRepository
|
||||
lateinit var dataRepository: IDataRepository
|
||||
|
||||
@Inject
|
||||
lateinit var settingsManager: SettingsManager
|
||||
lateinit var settingsSource: ISettingsSource
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
@@ -45,12 +44,12 @@ class MainApplication : Application() {
|
||||
}
|
||||
|
||||
private fun checkAutoUpdate() {
|
||||
if (settingsManager.isUpdateEnabled()) {
|
||||
val timeDelta = System.currentTimeMillis() - settingsManager.getLastUpdateTime()
|
||||
if (settingsSource.isUpdateEnabled()) {
|
||||
val timeDelta = System.currentTimeMillis() - settingsSource.getLastUpdateTime()
|
||||
if (timeDelta > 172800000) { // 48 hours in ms
|
||||
val sdf = SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault())
|
||||
Timber.d("Started periodic data update on ${sdf.format(Date())}")
|
||||
repository.updateFromWeb()
|
||||
dataRepository.updateFromWeb()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat
|
||||
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.content.Context
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorManager
|
||||
import android.location.LocationManager
|
||||
import androidx.room.Room
|
||||
import com.rtbishop.look4sat.data.DataParser
|
||||
import com.rtbishop.look4sat.data.DataRepository
|
||||
import com.rtbishop.look4sat.domain.*
|
||||
import com.rtbishop.look4sat.framework.*
|
||||
import com.rtbishop.look4sat.framework.data.*
|
||||
import com.rtbishop.look4sat.framework.data.local.EntriesStorage
|
||||
import com.rtbishop.look4sat.framework.data.local.RadiosStorage
|
||||
import com.rtbishop.look4sat.framework.data.remote.FileSource
|
||||
import com.rtbishop.look4sat.framework.data.remote.NetworkSource
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object MainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBluetoothReporter(@ApplicationContext context: Context): BluetoothReporter {
|
||||
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
return BluetoothReporter(manager, CoroutineScope(Dispatchers.IO))
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideNetworkReporter(): NetworkReporter = NetworkReporter(CoroutineScope(Dispatchers.IO))
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDataRepository(
|
||||
@ApplicationContext context: Context, settings: ISettingsSource
|
||||
): IDataRepository {
|
||||
val db = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb")
|
||||
.addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build()
|
||||
val parser = DataParser(Dispatchers.Default)
|
||||
val fileSource = FileSource(context.contentResolver, Dispatchers.IO)
|
||||
val entries = EntriesStorage(db.entriesDao())
|
||||
val radios = RadiosStorage(db.radiosDao())
|
||||
val remoteSource = NetworkSource(Dispatchers.IO)
|
||||
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
return DataRepository(parser, fileSource, entries, radios, remoteSource, scope, settings)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLocationSource(
|
||||
@ApplicationContext context: Context, settings: ISettingsSource
|
||||
): ILocationSource {
|
||||
val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
return LocationSource(manager, settings)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOrientationSource(@ApplicationContext context: Context): OrientationSource {
|
||||
val manager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||
val sensor = manager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||
return OrientationSource(manager, sensor)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSatelliteManager(): ISatelliteManager = SatelliteManager(Dispatchers.Default)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSettingsSource(@ApplicationContext context: Context): ISettingsSource {
|
||||
return SettingsSource(context.getSharedPreferences("default", Context.MODE_PRIVATE))
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -1,4 +1,4 @@
|
||||
package com.rtbishop.look4sat.presentation.radarScreen
|
||||
package com.rtbishop.look4sat.framework
|
||||
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.util.Log
|
||||
@@ -9,7 +9,10 @@ import java.io.OutputStream
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
|
||||
class BTReporter(private val manager: BluetoothManager, private val reporterScope: CoroutineScope) {
|
||||
class BluetoothReporter(
|
||||
private val manager: BluetoothManager,
|
||||
private val reporterScope: CoroutineScope
|
||||
) {
|
||||
|
||||
private val tag = "BTReporter"
|
||||
private val sppid: UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb")
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.framework
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import android.os.Bundle
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.ILocationManager
|
||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.utility.QthConverter
|
||||
import com.rtbishop.look4sat.utility.round
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class LocationManager @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val manager: LocationManager,
|
||||
private val settings: ISettingsManager,
|
||||
) : LocationListener, ILocationManager {
|
||||
|
||||
private val providerDef = LocationManager.PASSIVE_PROVIDER
|
||||
private val providerNet = LocationManager.NETWORK_PROVIDER
|
||||
private val providerGps = LocationManager.GPS_PROVIDER
|
||||
private val locationCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
private val locationFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||
private val _stationPosition = MutableStateFlow<DataState<GeoPos>>(DataState.Handled)
|
||||
private var currentLocator = settings.loadStationLocator()
|
||||
private var currentPosition = settings.loadStationPosition()
|
||||
|
||||
override val stationPosition: StateFlow<DataState<GeoPos>> = _stationPosition
|
||||
|
||||
override fun getStationLocator(): String = currentLocator
|
||||
|
||||
override fun getStationPosition(): GeoPos = currentPosition
|
||||
|
||||
override fun setStationPosition(latitude: Double, longitude: Double) {
|
||||
if (QthConverter.isValidPosition(latitude, longitude)) {
|
||||
val tempLon = if (longitude > 180.0) longitude - 180 else longitude
|
||||
val newLat = latitude.round(4)
|
||||
val newLon = tempLon.round(4)
|
||||
QthConverter.positionToQth(newLat, newLon)?.let { locator ->
|
||||
currentLocator = locator
|
||||
currentPosition = GeoPos(newLat, newLon)
|
||||
settings.saveStationLocator(locator)
|
||||
settings.saveStationPosition(currentPosition)
|
||||
_stationPosition.value = DataState.Success(currentPosition)
|
||||
}
|
||||
} else {
|
||||
_stationPosition.value =
|
||||
DataState.Error(context.getString(R.string.location_manual_error))
|
||||
}
|
||||
}
|
||||
|
||||
override fun setPositionFromGps() {
|
||||
val result = ContextCompat.checkSelfPermission(context, locationFine)
|
||||
if (manager.isProviderEnabled(providerGps) && result == PackageManager.PERMISSION_GRANTED) {
|
||||
val location = manager.getLastKnownLocation(providerDef)
|
||||
if (location != null) {
|
||||
setStationPosition(location.latitude, location.longitude)
|
||||
} else {
|
||||
_stationPosition.value = DataState.Loading
|
||||
manager.requestLocationUpdates(providerGps, 0L, 0f, this)
|
||||
}
|
||||
} else {
|
||||
_stationPosition.value = DataState.Error(context.getString(R.string.location_null))
|
||||
}
|
||||
}
|
||||
|
||||
override fun setPositionFromNet() {
|
||||
val result = ContextCompat.checkSelfPermission(context, locationCoarse)
|
||||
if (manager.isProviderEnabled(providerNet) && result == PackageManager.PERMISSION_GRANTED) {
|
||||
val location = manager.getLastKnownLocation(providerDef)
|
||||
if (location != null) {
|
||||
setStationPosition(location.latitude, location.longitude)
|
||||
} else {
|
||||
_stationPosition.value = DataState.Loading
|
||||
manager.requestLocationUpdates(providerNet, 0L, 0f, this)
|
||||
}
|
||||
} else {
|
||||
_stationPosition.value = DataState.Error(context.getString(R.string.location_null))
|
||||
}
|
||||
}
|
||||
|
||||
override fun setPositionFromQth(locator: String) {
|
||||
val position = QthConverter.qthToPosition(locator)
|
||||
if (position != null) {
|
||||
currentLocator = locator
|
||||
currentPosition = position
|
||||
settings.saveStationLocator(locator)
|
||||
settings.saveStationPosition(currentPosition)
|
||||
_stationPosition.value = DataState.Success(currentPosition)
|
||||
} else {
|
||||
_stationPosition.value = DataState.Error(context.getString(R.string.location_qth_error))
|
||||
}
|
||||
}
|
||||
|
||||
override fun setPositionHandled() {
|
||||
_stationPosition.value = DataState.Handled
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location: Location) {
|
||||
manager.removeUpdates(this)
|
||||
setStationPosition(location.latitude, location.longitude)
|
||||
}
|
||||
|
||||
override fun onProviderEnabled(provider: String) {}
|
||||
|
||||
override fun onProviderDisabled(provider: String) {}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.framework
|
||||
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import androidx.core.location.LocationListenerCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import com.rtbishop.look4sat.domain.ILocationSource
|
||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
||||
import com.rtbishop.look4sat.model.StationPos
|
||||
import com.rtbishop.look4sat.utility.QthConverter
|
||||
import com.rtbishop.look4sat.utility.round
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import timber.log.Timber
|
||||
|
||||
class LocationSource(
|
||||
private val manager: LocationManager,
|
||||
private val settings: ISettingsSource,
|
||||
) : ILocationSource, LocationListenerCompat {
|
||||
|
||||
private val _stationPosition = MutableStateFlow(settings.loadStationPosition())
|
||||
override val stationPosition: StateFlow<StationPos> = _stationPosition
|
||||
|
||||
override fun setGpsPosition(): Boolean {
|
||||
if (!LocationManagerCompat.isLocationEnabled(manager)) return false
|
||||
forceLocationUpdate(LocationManager.GPS_PROVIDER)
|
||||
forceLocationUpdate(LocationManager.NETWORK_PROVIDER)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun setGeoPosition(latitude: Double, longitude: Double): Boolean {
|
||||
manager.removeUpdates(this)
|
||||
val newLongitude = if (longitude > 180.0) longitude - 180 else longitude
|
||||
val locator = QthConverter.positionToQth(latitude, newLongitude) ?: return false
|
||||
saveStationPosition(latitude, newLongitude, locator)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun setQthPosition(locator: String): Boolean {
|
||||
val position = QthConverter.qthToPosition(locator) ?: return false
|
||||
saveStationPosition(position.lat, position.lon, locator)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location: Location) {
|
||||
setGeoPosition(location.latitude, location.longitude)
|
||||
}
|
||||
|
||||
private fun forceLocationUpdate(provider: String) {
|
||||
try {
|
||||
val location = manager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)
|
||||
if (location == null || System.currentTimeMillis() - location.time > 600000) {
|
||||
Timber.d("Requesting $provider location update")
|
||||
manager.requestLocationUpdates(provider, 0L, 0f, this)
|
||||
} else {
|
||||
setGeoPosition(location.latitude, location.longitude)
|
||||
}
|
||||
} catch (exception: SecurityException) {
|
||||
Timber.d("No permissions were given")
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveStationPosition(latitude: Double, longitude: Double, locator: String) {
|
||||
val newLat = latitude.round(4)
|
||||
val newLon = longitude.round(4)
|
||||
Timber.d("Received new Position($newLat, $newLon) & Locator $locator")
|
||||
_stationPosition.value = StationPos(newLat, newLon, locator, System.currentTimeMillis())
|
||||
settings.saveStationPosition(stationPosition.value)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.utility
|
||||
package com.rtbishop.look4sat.framework
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.nio.ByteBuffer
|
||||
@@ -25,7 +25,7 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class DataReporter(private val reporterScope: CoroutineScope) {
|
||||
class NetworkReporter(private val reporterScope: CoroutineScope) {
|
||||
|
||||
private var rotationSocketChannel: SocketChannel? = null
|
||||
private var rotationReporting: Job? = null
|
||||
+19
-30
@@ -21,47 +21,36 @@ import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import com.rtbishop.look4sat.domain.predict.RAD2DEG
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import com.rtbishop.look4sat.domain.RAD2DEG
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlin.math.round
|
||||
|
||||
@Singleton
|
||||
class OrientationManager @Inject constructor(private val sensorManager: SensorManager) :
|
||||
SensorEventListener {
|
||||
class OrientationSource(
|
||||
private val manager: SensorManager, private val sensor: Sensor?
|
||||
) : SensorEventListener {
|
||||
|
||||
interface OrientationListener {
|
||||
fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float)
|
||||
}
|
||||
|
||||
private val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||
private val rotationMatrix = FloatArray(9)
|
||||
private val orientationValues = FloatArray(3)
|
||||
private var sensorAccuracy: Int = SensorManager.SENSOR_STATUS_UNRELIABLE
|
||||
private var orientationListener: OrientationListener? = null
|
||||
|
||||
fun startListening(listener: OrientationListener) {
|
||||
if (sensor != null && orientationListener !== listener) {
|
||||
orientationListener = listener
|
||||
sensorManager.registerListener(this, sensor, 16000)
|
||||
} else return
|
||||
private val _orientation = MutableStateFlow(Pair(0f, 0f))
|
||||
val orientation: StateFlow<Pair<Float, Float>> = _orientation
|
||||
|
||||
fun startListening() = sensor?.let {
|
||||
manager.registerListener(this, it, 16000)
|
||||
}
|
||||
|
||||
fun stopListening() {
|
||||
sensorManager.unregisterListener(this)
|
||||
orientationListener = null
|
||||
}
|
||||
fun stopListening() = manager.unregisterListener(this)
|
||||
|
||||
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
|
||||
sensorAccuracy = accuracy
|
||||
}
|
||||
|
||||
override fun onSensorChanged(event: SensorEvent) {
|
||||
when {
|
||||
orientationListener == null -> return
|
||||
sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE -> return
|
||||
event.sensor == sensor -> updateOrientation(event.values)
|
||||
}
|
||||
override fun onSensorChanged(event: SensorEvent) = when {
|
||||
sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE -> {}
|
||||
event.sensor == sensor -> updateOrientation(event.values)
|
||||
else -> {}
|
||||
}
|
||||
|
||||
private fun updateOrientation(rotationVector: FloatArray) {
|
||||
@@ -69,11 +58,11 @@ class OrientationManager @Inject constructor(private val sensorManager: SensorMa
|
||||
SensorManager.getOrientation(rotationMatrix, orientationValues)
|
||||
val azimuth = (orientationValues[0] * RAD2DEG).toFloat()
|
||||
val pitch = (orientationValues[1] * RAD2DEG).toFloat()
|
||||
val roll = (orientationValues[2] * RAD2DEG).toFloat()
|
||||
// val roll = (orientationValues[2] * RAD2DEG).toFloat()
|
||||
val magneticAzimuth = (azimuth + 360f) % 360f
|
||||
val roundedAzimuth = round(magneticAzimuth * 10) / 10
|
||||
val roundedPitch = round(pitch * 10) / 10
|
||||
val roundedRoll = round(roll * 10) / 10
|
||||
orientationListener?.onOrientationChanged(roundedAzimuth, roundedPitch, roundedRoll)
|
||||
// val roundedRoll = round(roll * 10) / 10
|
||||
_orientation.value = Pair(roundedAzimuth, roundedPitch)
|
||||
}
|
||||
}
|
||||
+15
-24
@@ -19,13 +19,10 @@ package com.rtbishop.look4sat.framework
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
||||
import com.rtbishop.look4sat.model.StationPos
|
||||
|
||||
@Singleton
|
||||
class SettingsManager @Inject constructor(private val prefs: SharedPreferences) : ISettingsManager {
|
||||
class SettingsSource(private val prefs: SharedPreferences) : ISettingsSource {
|
||||
|
||||
companion object {
|
||||
const val keyModes = "satModes"
|
||||
@@ -46,29 +43,23 @@ class SettingsManager @Inject constructor(private val prefs: SharedPreferences)
|
||||
const val keyLatitude = "stationLat"
|
||||
const val keyLongitude = "stationLon"
|
||||
const val keyLocator = "stationQTH"
|
||||
const val keyLocTimestamp = "locTimestamp"
|
||||
const val keySelection = "selection"
|
||||
}
|
||||
|
||||
override fun loadStationLocator(): String {
|
||||
return prefs.getString(keyLocator, null) ?: ""
|
||||
override fun loadStationPosition(): StationPos {
|
||||
val latitude = prefs.getString(keyLatitude, null) ?: "0.0"
|
||||
val longitude = prefs.getString(keyLongitude, null) ?: "0.0"
|
||||
val qthLocator = prefs.getString(keyLocator, null) ?: ""
|
||||
val timestamp = prefs.getLong(keyLocTimestamp, 0L)
|
||||
return StationPos(latitude.toDouble(), longitude.toDouble(), qthLocator, timestamp)
|
||||
}
|
||||
|
||||
override fun saveStationLocator(locator: String) {
|
||||
prefs.edit { putString(keyLocator, locator) }
|
||||
}
|
||||
|
||||
override fun loadStationPosition(): GeoPos {
|
||||
val defaultSP = "0.0"
|
||||
val latitude = prefs.getString(keyLatitude, null) ?: defaultSP
|
||||
val longitude = prefs.getString(keyLongitude, null) ?: defaultSP
|
||||
return GeoPos(latitude.toDouble(), longitude.toDouble())
|
||||
}
|
||||
|
||||
override fun saveStationPosition(position: GeoPos) {
|
||||
prefs.edit {
|
||||
putString(keyLatitude, position.lat.toString())
|
||||
putString(keyLongitude, position.lon.toString())
|
||||
}
|
||||
override fun saveStationPosition(stationPos: StationPos) = prefs.edit {
|
||||
putString(keyLatitude, stationPos.latitude.toString())
|
||||
putString(keyLongitude, stationPos.longitude.toString())
|
||||
putString(keyLocator, stationPos.qthLocator)
|
||||
putLong(keyLocTimestamp, stationPos.timestamp)
|
||||
}
|
||||
|
||||
override fun saveEntriesSelection(catnums: List<Int>) {
|
||||
+1
-1
@@ -27,7 +27,7 @@ import com.rtbishop.look4sat.framework.model.SatEntry
|
||||
import com.rtbishop.look4sat.framework.model.SatRadio
|
||||
|
||||
@Database(entities = [SatEntry::class, SatRadio::class], version = 2, exportSchema = true)
|
||||
abstract class LocalDatabase : RoomDatabase() {
|
||||
abstract class MainDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun entriesDao(): EntriesDao
|
||||
|
||||
+6
-6
@@ -15,17 +15,17 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.framework.data
|
||||
package com.rtbishop.look4sat.framework.data.local
|
||||
|
||||
import com.rtbishop.look4sat.domain.data.ILocalEntrySource
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.data.IEntriesStorage
|
||||
import com.rtbishop.look4sat.domain.Satellite
|
||||
import com.rtbishop.look4sat.framework.data.dao.EntriesDao
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry as DomainEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatItem as DomainItem
|
||||
import com.rtbishop.look4sat.framework.model.SatEntry as FrameworkEntry
|
||||
import com.rtbishop.look4sat.framework.model.SatItem as FrameworkItem
|
||||
import com.rtbishop.look4sat.model.SatEntry as DomainEntry
|
||||
import com.rtbishop.look4sat.model.SatItem as DomainItem
|
||||
|
||||
class LocalEntrySource(private val entriesDao: EntriesDao) : ILocalEntrySource {
|
||||
class EntriesStorage(private val entriesDao: EntriesDao) : IEntriesStorage {
|
||||
|
||||
override fun getEntriesTotal() = entriesDao.getEntriesTotal()
|
||||
|
||||
+5
-6
@@ -15,14 +15,14 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.framework.data
|
||||
package com.rtbishop.look4sat.framework.data.local
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio as DomainRadio
|
||||
import com.rtbishop.look4sat.framework.model.SatRadio as FrameworkRadio
|
||||
import com.rtbishop.look4sat.domain.data.ILocalRadioSource
|
||||
import com.rtbishop.look4sat.data.IRadiosStorage
|
||||
import com.rtbishop.look4sat.framework.data.dao.RadiosDao
|
||||
import com.rtbishop.look4sat.framework.model.SatRadio as FrameworkRadio
|
||||
import com.rtbishop.look4sat.model.SatRadio as DomainRadio
|
||||
|
||||
class LocalRadioSource(private val radiosDao: RadiosDao) : ILocalRadioSource {
|
||||
class RadiosStorage(private val radiosDao: RadiosDao) : IRadiosStorage {
|
||||
|
||||
override fun getRadiosTotal() = radiosDao.getRadiosTotal()
|
||||
|
||||
@@ -31,7 +31,6 @@ class LocalRadioSource(private val radiosDao: RadiosDao) : ILocalRadioSource {
|
||||
}
|
||||
|
||||
override suspend fun insertRadios(radios: List<DomainRadio>) {
|
||||
radiosDao.deleteRadios()
|
||||
radiosDao.insertRadios(radios.toFrameworkRadios())
|
||||
}
|
||||
|
||||
+6
-7
@@ -15,22 +15,21 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.framework.data
|
||||
package com.rtbishop.look4sat.framework.data.remote
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import com.rtbishop.look4sat.domain.data.IFileDataSource
|
||||
import com.rtbishop.look4sat.data.IFileSource
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.InputStream
|
||||
|
||||
class FileDataSource(
|
||||
class FileSource(
|
||||
private val contentResolver: ContentResolver,
|
||||
private val ioDispatcher: CoroutineDispatcher
|
||||
) : IFileDataSource {
|
||||
private val coroutineDispatcher: CoroutineDispatcher
|
||||
) : IFileSource {
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
override suspend fun getDataStream(uri: String): InputStream? {
|
||||
return withContext(ioDispatcher) { contentResolver.openInputStream(Uri.parse(uri)) }
|
||||
return withContext(coroutineDispatcher) { contentResolver.openInputStream(Uri.parse(uri)) }
|
||||
}
|
||||
}
|
||||
+6
-14
@@ -15,25 +15,17 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.framework.data
|
||||
package com.rtbishop.look4sat.framework.data.remote
|
||||
|
||||
import com.rtbishop.look4sat.domain.data.IRemoteDataSource
|
||||
import com.rtbishop.look4sat.data.INetworkSource
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.io.InputStream
|
||||
import java.net.URL
|
||||
|
||||
class RemoteDataSource(
|
||||
private val httpClient: OkHttpClient, private val ioDispatcher: CoroutineDispatcher
|
||||
) : IRemoteDataSource {
|
||||
class NetworkSource(private val coroutineDispatcher: CoroutineDispatcher) : INetworkSource {
|
||||
|
||||
override suspend fun getDataStream(url: String): InputStream? = withContext(ioDispatcher) {
|
||||
try {
|
||||
val request = Request.Builder().url(url).build()
|
||||
httpClient.newCall(request).execute().body?.byteStream()
|
||||
} catch (exception: Exception) {
|
||||
null
|
||||
}
|
||||
override suspend fun getDataStream(url: String): InputStream? {
|
||||
return withContext(coroutineDispatcher) { URL(url).openStream() }
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.framework.model
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
import com.rtbishop.look4sat.model.OrbitalData
|
||||
|
||||
@Entity(tableName = "entries", primaryKeys = ["catnum"])
|
||||
data class SatEntry(@Embedded val data: OrbitalData, var comment: String? = null)
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.injection
|
||||
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.hardware.SensorManager
|
||||
import android.location.LocationManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object AppModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBluetoothManager(@ApplicationContext context: Context): BluetoothManager {
|
||||
return context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLocationManager(@ApplicationContext context: Context): LocationManager {
|
||||
return context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSensorManager(@ApplicationContext context: Context): SensorManager {
|
||||
return context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
|
||||
return context.getSharedPreferences("default", Context.MODE_PRIVATE)
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.injection
|
||||
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.domain.ILocationManager
|
||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||
import com.rtbishop.look4sat.domain.data.DataRepository
|
||||
import com.rtbishop.look4sat.domain.predict.SatelliteManager
|
||||
import com.rtbishop.look4sat.framework.LocationManager
|
||||
import com.rtbishop.look4sat.framework.SettingsManager
|
||||
import com.rtbishop.look4sat.framework.data.FileDataSource
|
||||
import com.rtbishop.look4sat.framework.data.LocalDatabase
|
||||
import com.rtbishop.look4sat.framework.data.LocalEntrySource
|
||||
import com.rtbishop.look4sat.framework.data.LocalRadioSource
|
||||
import com.rtbishop.look4sat.framework.data.MIGRATION_1_2
|
||||
import com.rtbishop.look4sat.framework.data.RemoteDataSource
|
||||
import com.rtbishop.look4sat.presentation.radarScreen.BTReporter
|
||||
import com.rtbishop.look4sat.utility.DataParser
|
||||
import com.rtbishop.look4sat.utility.DataReporter
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object BaseModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDataRepository(
|
||||
@ApplicationContext context: Context,
|
||||
settings: ISettingsManager
|
||||
): IDataRepository {
|
||||
val db = Room.databaseBuilder(context, LocalDatabase::class.java, "Look4SatDb")
|
||||
.addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build()
|
||||
val parser = DataParser(Dispatchers.Default)
|
||||
val fileSource = FileDataSource(context.contentResolver, Dispatchers.IO)
|
||||
val entries = LocalEntrySource(db.entriesDao())
|
||||
val radios = LocalRadioSource(db.radiosDao())
|
||||
val remoteSource = RemoteDataSource(OkHttpClient.Builder().build(), Dispatchers.IO)
|
||||
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
return DataRepository(parser, fileSource, entries, radios, remoteSource, scope, settings)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDataReporter(): DataReporter = DataReporter(CoroutineScope(Dispatchers.IO))
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBTReporter(manager: BluetoothManager): BTReporter {
|
||||
return BTReporter(manager, CoroutineScope(Dispatchers.IO))
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLocationManager(manager: LocationManager): ILocationManager = manager
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSatelliteManager(): ISatelliteManager = SatelliteManager(Dispatchers.Default)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSettingsManager(manager: SettingsManager): ISettingsManager = manager
|
||||
}
|
||||
+6
-51
@@ -1,29 +1,5 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -32,7 +8,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
@@ -41,31 +16,11 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.presentation.entriesScreen.EntriesScreen
|
||||
import com.rtbishop.look4sat.presentation.mapScreen.MapScreen
|
||||
import com.rtbishop.look4sat.presentation.passesScreen.PassesScreen
|
||||
import com.rtbishop.look4sat.presentation.radarScreen.RadarScreen
|
||||
import com.rtbishop.look4sat.presentation.settingsScreen.SettingsScreen
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
installSplashScreen()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent { MainTheme { MainScreen() } }
|
||||
}
|
||||
|
||||
override fun attachBaseContext(newBase: Context?) {
|
||||
val newConfig = Configuration(newBase?.resources?.configuration)
|
||||
newConfig.fontScale = 1.0f
|
||||
applyOverrideConfiguration(newConfig)
|
||||
super.attachBaseContext(newBase)
|
||||
}
|
||||
}
|
||||
import com.rtbishop.look4sat.presentation.entries.EntriesScreen
|
||||
import com.rtbishop.look4sat.presentation.map.MapScreen
|
||||
import com.rtbishop.look4sat.presentation.passes.PassesScreen
|
||||
import com.rtbishop.look4sat.presentation.radar.RadarScreen
|
||||
import com.rtbishop.look4sat.presentation.settings.SettingsScreen
|
||||
|
||||
sealed class Screen(var title: String, var icon: Int, var route: String) {
|
||||
object Entries : Screen("Entries", R.drawable.ic_entries, "entries")
|
||||
@@ -77,7 +32,7 @@ sealed class Screen(var title: String, var icon: Int, var route: String) {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun MainScreen(navController: NavHostController = rememberNavController()) {
|
||||
fun MainScreen(navController: NavHostController = rememberNavController()) {
|
||||
Scaffold(bottomBar = { MainNavBar(navController = navController) }) { innerPadding ->
|
||||
Box(modifier = Modifier.padding(innerPadding)) { MainNavGraph(navController) }
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.passesScreen
|
||||
package com.rtbishop.look4sat.presentation.dialogs
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
||||
package com.rtbishop.look4sat.presentation.dialogs
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.rtbishop.look4sat.presentation.passesScreen
|
||||
package com.rtbishop.look4sat.presentation.dialogs
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
||||
package com.rtbishop.look4sat.presentation.dialogs
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.rtbishop.look4sat.presentation.entriesScreen
|
||||
package com.rtbishop.look4sat.presentation.dialogs
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
+5
-4
@@ -1,4 +1,4 @@
|
||||
package com.rtbishop.look4sat.presentation.entriesScreen
|
||||
package com.rtbishop.look4sat.presentation.entries
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
@@ -28,11 +28,12 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.presentation.Screen
|
||||
import com.rtbishop.look4sat.model.DataState
|
||||
import com.rtbishop.look4sat.model.SatItem
|
||||
import com.rtbishop.look4sat.presentation.CardLoadingIndicator
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.presentation.Screen
|
||||
import com.rtbishop.look4sat.presentation.dialogs.TypesDialog
|
||||
import com.rtbishop.look4sat.presentation.onClick
|
||||
|
||||
@Composable
|
||||
+11
-7
@@ -15,15 +15,17 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.entriesScreen
|
||||
package com.rtbishop.look4sat.presentation.entries
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.domain.ILocationSource
|
||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
||||
import com.rtbishop.look4sat.model.DataState
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.SatItem
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -37,7 +39,8 @@ import javax.inject.Inject
|
||||
class EntriesViewModel @Inject constructor(
|
||||
private val satelliteManager: ISatelliteManager,
|
||||
private val repository: IDataRepository,
|
||||
private val settings: ISettingsManager
|
||||
private val settings: ISettingsSource,
|
||||
private val locationSource: ILocationSource
|
||||
) : ViewModel() {
|
||||
|
||||
private val satType = MutableStateFlow("All")
|
||||
@@ -78,11 +81,12 @@ class EntriesViewModel @Inject constructor(
|
||||
settings.saveEntriesSelection(newSelection)
|
||||
val selectedIds = settings.loadEntriesSelection()
|
||||
val satellites = repository.getEntriesWithIds(selectedIds)
|
||||
val stationPos = settings.loadStationPosition()
|
||||
val stationPos = locationSource.stationPosition.value
|
||||
val geoPos = GeoPos(stationPos.latitude, stationPos.longitude)
|
||||
val timeRef = System.currentTimeMillis()
|
||||
val hoursAhead = settings.getHoursAhead()
|
||||
val minElev = settings.getMinElevation()
|
||||
satelliteManager.calculatePasses(satellites, stationPos, timeRef, hoursAhead, minElev)
|
||||
satelliteManager.calculatePasses(satellites, geoPos, timeRef, hoursAhead, minElev)
|
||||
}
|
||||
|
||||
fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
|
||||
+2
-2
@@ -15,9 +15,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.mapScreen
|
||||
package com.rtbishop.look4sat.presentation.map
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
|
||||
data class MapData(
|
||||
val catNum: Int,
|
||||
+7
-5
@@ -1,5 +1,6 @@
|
||||
package com.rtbishop.look4sat.presentation.mapScreen
|
||||
package com.rtbishop.look4sat.presentation.map
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
@@ -22,9 +23,9 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.Satellite
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.SatPos
|
||||
import org.osmdroid.config.Configuration
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
@@ -60,7 +61,8 @@ private val labelRect = Rect()
|
||||
@Composable
|
||||
fun MapScreen(viewModel: MapViewModel = hiltViewModel()) {
|
||||
val context = LocalContext.current
|
||||
Configuration.getInstance().load(context, viewModel.getPreferences())
|
||||
val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE)
|
||||
Configuration.getInstance().load(context, preferences)
|
||||
viewModel.selectDefaultSatellite(-1)
|
||||
val stationPos = viewModel.stationPos.collectAsState(initial = null)
|
||||
val positions = viewModel.positions.collectAsState(initial = null)
|
||||
+8
-13
@@ -15,17 +15,13 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.mapScreen
|
||||
package com.rtbishop.look4sat.presentation.map
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.*
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.SatPos
|
||||
import com.rtbishop.look4sat.utility.QthConverter
|
||||
import com.rtbishop.look4sat.utility.toDegrees
|
||||
import com.rtbishop.look4sat.utility.toTimerString
|
||||
@@ -42,13 +38,14 @@ import kotlin.math.min
|
||||
|
||||
@HiltViewModel
|
||||
class MapViewModel @Inject constructor(
|
||||
private val preferences: SharedPreferences,
|
||||
private val repository: IDataRepository,
|
||||
private val satelliteManager: ISatelliteManager,
|
||||
private val settings: ISettingsManager
|
||||
private val settings: ISettingsSource,
|
||||
private val locationSource: ILocationSource
|
||||
) : ViewModel() {
|
||||
|
||||
private val stationPosition = settings.loadStationPosition()
|
||||
private val stationPosNew = locationSource.stationPosition.value
|
||||
private val stationPosition = GeoPos(stationPosNew.latitude, stationPosNew.longitude)
|
||||
private var allPasses = satelliteManager.getPasses()
|
||||
private var allSatellites = listOf<Satellite>()
|
||||
private var dataUpdateJob: Job? = null
|
||||
@@ -73,8 +70,6 @@ class MapViewModel @Inject constructor(
|
||||
private val _positions = MutableSharedFlow<Map<Satellite, GeoPos>>()
|
||||
val positions: SharedFlow<Map<Satellite, GeoPos>> = _positions
|
||||
|
||||
fun getPreferences() = preferences
|
||||
|
||||
fun scrollSelection(decrement: Boolean) {
|
||||
if (allSatellites.isNotEmpty()) {
|
||||
val index = allSatellites.indexOf(selectedSatellite)
|
||||
+7
-7
@@ -1,4 +1,4 @@
|
||||
package com.rtbishop.look4sat.presentation.passesScreen
|
||||
package com.rtbishop.look4sat.presentation.passes
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
@@ -23,12 +23,12 @@ import androidx.compose.ui.unit.*
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.NearEarthSat
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.presentation.Screen
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.domain.NearEarthSat
|
||||
import com.rtbishop.look4sat.model.DataState
|
||||
import com.rtbishop.look4sat.model.OrbitalData
|
||||
import com.rtbishop.look4sat.model.SatPass
|
||||
import com.rtbishop.look4sat.presentation.*
|
||||
import com.rtbishop.look4sat.presentation.dialogs.FilterDialog
|
||||
import com.rtbishop.look4sat.presentation.pullRefresh.PullRefreshIndicator
|
||||
import com.rtbishop.look4sat.presentation.pullRefresh.PullRefreshState
|
||||
import com.rtbishop.look4sat.presentation.pullRefresh.pullRefresh
|
||||
+11
-7
@@ -15,15 +15,17 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.passesScreen
|
||||
package com.rtbishop.look4sat.presentation.passes
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.domain.ILocationSource
|
||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
||||
import com.rtbishop.look4sat.model.DataState
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.SatPass
|
||||
import com.rtbishop.look4sat.utility.toTimerString
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.*
|
||||
@@ -35,7 +37,8 @@ import javax.inject.Inject
|
||||
class PassesViewModel @Inject constructor(
|
||||
private val satelliteManager: ISatelliteManager,
|
||||
private val repository: IDataRepository,
|
||||
private val settings: ISettingsManager
|
||||
private val settings: ISettingsSource,
|
||||
private val locationSource: ILocationSource
|
||||
) : ViewModel() {
|
||||
|
||||
private val _passes = MutableStateFlow<DataState<List<SatPass>>>(DataState.Loading)
|
||||
@@ -105,11 +108,12 @@ class PassesViewModel @Inject constructor(
|
||||
selection?.let { items -> settings.saveEntriesSelection(items) }
|
||||
settings.setHoursAhead(hoursAhead)
|
||||
settings.setMinElevation(minElevation)
|
||||
val stationPos = settings.loadStationPosition()
|
||||
val stationPos = locationSource.stationPosition.value
|
||||
val geoPos = GeoPos(stationPos.latitude, stationPos.longitude)
|
||||
val selectedIds = settings.loadEntriesSelection()
|
||||
val satellites = repository.getEntriesWithIds(selectedIds)
|
||||
satelliteManager.calculatePasses(
|
||||
satellites, stationPos, timeRef, hoursAhead, minElevation
|
||||
satellites, geoPos, timeRef, hoursAhead, minElevation
|
||||
)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -15,8 +15,8 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.radarScreen
|
||||
package com.rtbishop.look4sat.presentation.radar
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.model.SatPos
|
||||
|
||||
data class RadarData(val satPos: SatPos, val satTrack: List<SatPos> = emptyList())
|
||||
+9
-20
@@ -1,4 +1,4 @@
|
||||
package com.rtbishop.look4sat.presentation.radarScreen
|
||||
package com.rtbishop.look4sat.presentation.radar
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
@@ -18,15 +18,12 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.model.SatRadio
|
||||
|
||||
@Composable
|
||||
fun RadarScreen(navController: NavController) {
|
||||
val viewModel: RadarViewModel = hiltViewModel()
|
||||
val currentPass = viewModel.getPass().collectAsState(null)
|
||||
val radarData = viewModel.radarData.collectAsState()
|
||||
val transmitters = viewModel.transmitters.collectAsState()
|
||||
val orientation = viewModel.orientation.collectAsState()
|
||||
|
||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
ElevatedCard(modifier = Modifier.height(52.dp)) {
|
||||
@@ -62,20 +59,18 @@ fun RadarScreen(navController: NavController) {
|
||||
ElevatedCard(modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.weight(1f)) {
|
||||
radarData.value?.let { data ->
|
||||
orientation.value?.let { triple ->
|
||||
RadarViewCompose(
|
||||
item = data.satPos,
|
||||
items = data.satTrack,
|
||||
orientation = triple
|
||||
)
|
||||
}
|
||||
viewModel.radarData.value?.let { data ->
|
||||
RadarViewCompose(
|
||||
item = data.satPos,
|
||||
items = data.satTrack,
|
||||
azimElev = viewModel.orientation.value
|
||||
)
|
||||
}
|
||||
}
|
||||
ElevatedCard(modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.weight(1f)) {
|
||||
TransmittersList(transmitters = transmitters.value)
|
||||
TransmittersList(transmitters = viewModel.transmitters.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,15 +156,9 @@ private fun TransmitterItem(radio: SatRadio) {
|
||||
// setShowAim(viewModel.getUseCompass())
|
||||
// setScanning(viewModel.getShowSweep())
|
||||
// }
|
||||
// radarCard.addView(radarView)
|
||||
// viewModel.radarData.observe(viewLifecycleOwner) { passData ->
|
||||
// radarView?.setPosition(passData.satPos)
|
||||
// radarView?.setPositions(passData.satTrack)
|
||||
// setPassText(pass, passData.satPos)
|
||||
// }
|
||||
// viewModel.orientation.observe(viewLifecycleOwner) { value ->
|
||||
// radarView?.setOrientation(value.first, value.second, value.third)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
@file:OptIn(ExperimentalTextApi::class)
|
||||
|
||||
package com.rtbishop.look4sat.presentation.radarScreen
|
||||
package com.rtbishop.look4sat.presentation.radar
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.Canvas
|
||||
@@ -14,14 +14,14 @@ import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.*
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.rtbishop.look4sat.domain.predict.PI_2
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.PI_2
|
||||
import com.rtbishop.look4sat.model.SatPos
|
||||
import com.rtbishop.look4sat.utility.toRadians
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
@Composable
|
||||
fun RadarViewCompose(item: SatPos, items: List<SatPos>, orientation: Triple<Float, Float, Float>) {
|
||||
fun RadarViewCompose(item: SatPos, items: List<SatPos>, azimElev: Pair<Float, Float>) {
|
||||
val radarColor = Color(0xFFDCDCDC)
|
||||
val primaryColor = Color(0xFFFFE082)
|
||||
val secondaryColor = Color(0xFFDC0000)
|
||||
@@ -41,14 +41,14 @@ fun RadarViewCompose(item: SatPos, items: List<SatPos>, orientation: Triple<Floa
|
||||
trackEffect.value = createTrackEffect(trackPath.value)
|
||||
trackCreated.value = true
|
||||
}
|
||||
rotate(-orientation.first) {
|
||||
rotate(-azimElev.first) {
|
||||
drawSweep(center, sweepDegrees.value, radius, primaryColor)
|
||||
drawRadar(radius, radarColor, strokeWidth, 3)
|
||||
drawInfo(radius, primaryColor, measurer, 3)
|
||||
translate(center.x, center.y) {
|
||||
drawTrack(trackPath.value, trackEffect.value, secondaryColor, primaryColor)
|
||||
if (item.elevation > 0) drawPosition(item, radius, animScale.value, primaryColor)
|
||||
drawAim(orientation.first, orientation.second, radius, strokeWidth, secondaryColor)
|
||||
drawAim(azimElev.first, azimElev.second, radius, strokeWidth, secondaryColor)
|
||||
}
|
||||
sweepDegrees.value = (sweepDegrees.value + 360 / 12.0f / 60) % 360
|
||||
}
|
||||
+36
-45
@@ -15,29 +15,26 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.radarScreen
|
||||
package com.rtbishop.look4sat.presentation.radar
|
||||
|
||||
import android.hardware.GeomagneticField
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.framework.OrientationManager
|
||||
import com.rtbishop.look4sat.utility.DataReporter
|
||||
import com.rtbishop.look4sat.domain.*
|
||||
import com.rtbishop.look4sat.framework.BluetoothReporter
|
||||
import com.rtbishop.look4sat.framework.NetworkReporter
|
||||
import com.rtbishop.look4sat.framework.OrientationSource
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.SatPass
|
||||
import com.rtbishop.look4sat.model.SatPos
|
||||
import com.rtbishop.look4sat.model.SatRadio
|
||||
import com.rtbishop.look4sat.utility.round
|
||||
import com.rtbishop.look4sat.utility.toDegrees
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -46,28 +43,34 @@ import javax.inject.Inject
|
||||
@HiltViewModel
|
||||
class RadarViewModel @Inject constructor(
|
||||
private val savedStateHandle: SavedStateHandle,
|
||||
private val orientationManager: OrientationManager,
|
||||
private val reporter: DataReporter,
|
||||
private val btReporter: BTReporter,
|
||||
private val orientationSource: OrientationSource,
|
||||
private val reporter: NetworkReporter,
|
||||
private val bluetoothReporter: BluetoothReporter,
|
||||
private val satManager: ISatelliteManager,
|
||||
private val repository: IDataRepository,
|
||||
private val settings: ISettingsManager
|
||||
) : ViewModel(), OrientationManager.OrientationListener {
|
||||
private val settings: ISettingsSource,
|
||||
private val locationSource: ILocationSource
|
||||
) : ViewModel() {
|
||||
|
||||
private val stationPos = settings.loadStationPosition()
|
||||
private val _passData = MutableStateFlow<RadarData?>(null)
|
||||
private val _transmitters = MutableStateFlow<List<SatRadio>>(emptyList())
|
||||
private val _orientation = MutableStateFlow<Triple<Float, Float, Float>?>(null)
|
||||
val radarData: StateFlow<RadarData?> = _passData
|
||||
val transmitters: StateFlow<List<SatRadio>> = _transmitters
|
||||
val orientation: StateFlow<Triple<Float, Float, Float>?> = _orientation
|
||||
private val stationPos = locationSource.stationPosition.value.toGeoPos()
|
||||
private val magDeclination = getMagDeclination(stationPos)
|
||||
val transmitters = mutableStateOf<List<SatRadio>>(emptyList())
|
||||
val radarData = mutableStateOf<RadarData?>(null)
|
||||
val orientation = mutableStateOf(orientationSource.orientation.value)
|
||||
|
||||
init {
|
||||
enableSensor()
|
||||
if (settings.isSensorEnabled()) {
|
||||
viewModelScope.launch {
|
||||
orientationSource.startListening()
|
||||
orientationSource.orientation.collect { data ->
|
||||
orientation.value = Pair(data.first + magDeclination, data.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
disableSensor()
|
||||
orientationSource.stopListening()
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
@@ -94,22 +97,10 @@ class RadarViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun enableSensor() {
|
||||
if (settings.isSensorEnabled()) orientationManager.startListening(this)
|
||||
}
|
||||
|
||||
fun disableSensor() {
|
||||
if (settings.isSensorEnabled()) orientationManager.stopListening()
|
||||
}
|
||||
|
||||
fun getUseCompass(): Boolean = settings.isSensorEnabled()
|
||||
|
||||
fun getShowSweep(): Boolean = settings.isSweepEnabled()
|
||||
|
||||
override fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) {
|
||||
_orientation.value = Triple(azimuth + getMagDeclination(stationPos), pitch, roll)
|
||||
}
|
||||
|
||||
private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
|
||||
val latitude = geoPos.lat.toFloat()
|
||||
val longitude = geoPos.lon.toFloat()
|
||||
@@ -129,7 +120,7 @@ class RadarViewModel @Inject constructor(
|
||||
val elevation = satPos.elevation.toDegrees().round(1)
|
||||
reporter.reportRotation(server, port, azimuth, elevation)
|
||||
}
|
||||
_passData.value = RadarData(satPos, track)
|
||||
radarData.value = RadarData(satPos, track)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,14 +128,14 @@ class RadarViewModel @Inject constructor(
|
||||
viewModelScope.launch {
|
||||
if (settings.getBTEnabled()) {
|
||||
val btDevice = settings.getBTDeviceAddr()
|
||||
if (btReporter.isConnected()) {
|
||||
if (bluetoothReporter.isConnected()) {
|
||||
val format = settings.getBTFormat()
|
||||
val azimuth = satPos.azimuth.toDegrees().round(0).toInt()
|
||||
val elevation = satPos.elevation.toDegrees().round(0).toInt()
|
||||
btReporter.reportRotation(format, azimuth, elevation)
|
||||
} else if (!btReporter.isConnecting()) {
|
||||
bluetoothReporter.reportRotation(format, azimuth, elevation)
|
||||
} else if (!bluetoothReporter.isConnecting()) {
|
||||
Log.i("BTReporter", "BTReporter: Attempting to connect...")
|
||||
btReporter.connectBTDevice(btDevice)
|
||||
bluetoothReporter.connectBTDevice(btDevice)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,7 +145,7 @@ class RadarViewModel @Inject constructor(
|
||||
viewModelScope.launch {
|
||||
delay(125)
|
||||
val list = satManager.processRadios(satellite, stationPos, radios, time)
|
||||
_transmitters.value = list
|
||||
transmitters.value = list
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -1,4 +1,4 @@
|
||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
||||
package com.rtbishop.look4sat.presentation.settings
|
||||
|
||||
import android.Manifest
|
||||
import android.os.Build
|
||||
@@ -26,6 +26,8 @@ import com.rtbishop.look4sat.BuildConfig
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.presentation.CardButton
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.presentation.dialogs.LocatorDialog
|
||||
import com.rtbishop.look4sat.presentation.dialogs.PositionDialog
|
||||
import com.rtbishop.look4sat.presentation.gotoUrl
|
||||
import com.rtbishop.look4sat.presentation.showToast
|
||||
|
||||
@@ -57,8 +59,8 @@ fun SettingsScreen(navController: NavController) {
|
||||
val locationPermFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||
val locationRequest = rememberLauncherForActivityResult(locationContract) { permissions ->
|
||||
when {
|
||||
permissions[locationPermFine] == true -> viewModel.locationSettings.value.setGpsLoc
|
||||
permissions[locationPermCoarse] == true -> viewModel.locationSettings.value.setGpsLoc
|
||||
permissions[locationPermFine] == true -> viewModel.locationSettings.value.setGpsLoc()
|
||||
permissions[locationPermCoarse] == true -> viewModel.locationSettings.value.setGpsLoc()
|
||||
else -> showToast(context, locationError)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
||||
package com.rtbishop.look4sat.presentation.settings
|
||||
|
||||
data class LocationSettings(
|
||||
val getUpdating: Boolean,
|
||||
+17
-22
@@ -15,15 +15,15 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
||||
package com.rtbishop.look4sat.presentation.settings
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.domain.ILocationManager
|
||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.ILocationSource
|
||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
||||
import com.rtbishop.look4sat.model.DataState
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -31,21 +31,20 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SettingsViewModel @Inject constructor(
|
||||
private val locationManager: ILocationManager,
|
||||
private val locationSource: ILocationSource,
|
||||
private val repository: IDataRepository,
|
||||
private val settings: ISettingsManager
|
||||
private val settings: ISettingsSource
|
||||
) : ViewModel() {
|
||||
|
||||
private val defaultLocationSettings = LocationSettings(false,
|
||||
settings.getLastUpdateTime(),
|
||||
locationManager.getStationPosition().lat,
|
||||
locationManager.getStationPosition().lon,
|
||||
settings.loadStationLocator(),
|
||||
{ locationManager.setPositionFromGps() },
|
||||
{ lat, lon -> locationManager.setStationPosition(lat, lon) },
|
||||
{ locationManager.setPositionFromQth(it) })
|
||||
var locationSettings = mutableStateOf(defaultLocationSettings)
|
||||
private set
|
||||
locationSource.stationPosition.value.timestamp,
|
||||
locationSource.stationPosition.value.latitude,
|
||||
locationSource.stationPosition.value.longitude,
|
||||
locationSource.stationPosition.value.qthLocator,
|
||||
{ locationSource.setGpsPosition() },
|
||||
{ lat, lon -> locationSource.setGeoPosition(lat, lon) },
|
||||
{ locationSource.setQthPosition(it) })
|
||||
val locationSettings = mutableStateOf(defaultLocationSettings)
|
||||
|
||||
private val defaultDataSettings = DataSettings(false,
|
||||
settings.getLastUpdateTime(),
|
||||
@@ -54,8 +53,7 @@ class SettingsViewModel @Inject constructor(
|
||||
{ repository.updateFromWeb() },
|
||||
{ repository.updateFromFile(it) },
|
||||
{ repository.clearAllData() })
|
||||
var dataSettings = mutableStateOf(defaultDataSettings)
|
||||
private set
|
||||
val dataSettings = mutableStateOf(defaultDataSettings)
|
||||
|
||||
private val defaultOtherSettings = OtherSettings(settings.isUtcEnabled(),
|
||||
settings.isUpdateEnabled(),
|
||||
@@ -65,8 +63,7 @@ class SettingsViewModel @Inject constructor(
|
||||
{ setUpdate(it) },
|
||||
{ setSweep(it) },
|
||||
{ setSensor(it) })
|
||||
var otherSettings = mutableStateOf(defaultOtherSettings)
|
||||
private set
|
||||
val otherSettings = mutableStateOf(defaultOtherSettings)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
@@ -81,9 +78,7 @@ class SettingsViewModel @Inject constructor(
|
||||
combine(repository.getEntriesTotal(), repository.getRadiosTotal()) { sats, radios ->
|
||||
dataSettings.value =
|
||||
dataSettings.value.copy(getSatellites = sats, getRadios = radios)
|
||||
}.collect {
|
||||
|
||||
}
|
||||
}.collect {}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-40
@@ -15,50 +15,52 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.utility
|
||||
package com.rtbishop.look4sat.data
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
import java.io.InputStream
|
||||
import kotlin.math.pow
|
||||
import com.rtbishop.look4sat.model.OrbitalData
|
||||
import com.rtbishop.look4sat.model.SatRadio
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.InputStream
|
||||
import kotlin.math.pow
|
||||
|
||||
class DataParser(private val defaultDispatcher: CoroutineDispatcher) {
|
||||
|
||||
suspend fun parseCSVStream(csvStream: InputStream): List<OrbitalData> = withContext(defaultDispatcher) {
|
||||
val parsedItems = mutableListOf<OrbitalData>()
|
||||
csvStream.bufferedReader().useLines { lines ->
|
||||
lines.forEachIndexed { index, line ->
|
||||
if (index != 0) {
|
||||
val values = line.split(",")
|
||||
parseCSV(values)?.let { tle -> parsedItems.add(tle) }
|
||||
suspend fun parseCSVStream(csvStream: InputStream): List<OrbitalData> =
|
||||
withContext(defaultDispatcher) {
|
||||
val parsedItems = mutableListOf<OrbitalData>()
|
||||
csvStream.bufferedReader().useLines { lines ->
|
||||
lines.forEachIndexed { index, line ->
|
||||
if (index != 0) {
|
||||
val values = line.split(",")
|
||||
parseCSV(values)?.let { tle -> parsedItems.add(tle) }
|
||||
}
|
||||
}
|
||||
}
|
||||
return@withContext parsedItems
|
||||
}
|
||||
return@withContext parsedItems
|
||||
}
|
||||
|
||||
suspend fun parseTLEStream(tleStream: InputStream): List<OrbitalData> = withContext(defaultDispatcher) {
|
||||
val tleStrings = mutableListOf(String(), String(), String())
|
||||
val parsedItems = mutableListOf<OrbitalData>()
|
||||
var lineIndex = 0
|
||||
tleStream.bufferedReader().forEachLine { line ->
|
||||
tleStrings[lineIndex] = line
|
||||
if (lineIndex < 2) {
|
||||
lineIndex++
|
||||
} else {
|
||||
val isLineOneValid = tleStrings[1].substring(0, 1) == "1"
|
||||
val isLineTwoValid = tleStrings[2].substring(0, 1) == "2"
|
||||
if (!isLineOneValid && !isLineTwoValid) return@forEachLine
|
||||
parseTLE(tleStrings)?.let { tle -> parsedItems.add(tle) }
|
||||
lineIndex = 0
|
||||
suspend fun parseTLEStream(tleStream: InputStream): List<OrbitalData> =
|
||||
withContext(defaultDispatcher) {
|
||||
val tleStrings = mutableListOf(String(), String(), String())
|
||||
val parsedItems = mutableListOf<OrbitalData>()
|
||||
var lineIndex = 0
|
||||
tleStream.bufferedReader().forEachLine { line ->
|
||||
tleStrings[lineIndex] = line
|
||||
if (lineIndex < 2) {
|
||||
lineIndex++
|
||||
} else {
|
||||
val isLineOneValid = tleStrings[1].substring(0, 1) == "1"
|
||||
val isLineTwoValid = tleStrings[2].substring(0, 1) == "2"
|
||||
if (!isLineOneValid && !isLineTwoValid) return@forEachLine
|
||||
parseTLE(tleStrings)?.let { tle -> parsedItems.add(tle) }
|
||||
lineIndex = 0
|
||||
}
|
||||
}
|
||||
return@withContext parsedItems
|
||||
}
|
||||
return@withContext parsedItems
|
||||
}
|
||||
|
||||
suspend fun parseJSONStream(jsonStream: InputStream): List<SatRadio> {
|
||||
return withContext(defaultDispatcher) {
|
||||
@@ -76,10 +78,6 @@ class DataParser(private val defaultDispatcher: CoroutineDispatcher) {
|
||||
}
|
||||
}
|
||||
|
||||
fun isLeapYear(year: Int): Boolean {
|
||||
return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)
|
||||
}
|
||||
|
||||
private fun parseCSV(values: List<String>): OrbitalData? {
|
||||
try {
|
||||
val name = values[0]
|
||||
@@ -122,8 +120,8 @@ class DataParser(private val defaultDispatcher: CoroutineDispatcher) {
|
||||
val argper: Double = tle[2].substring(34, 42).toDouble()
|
||||
val meanan: Double = tle[2].substring(43, 51).toDouble()
|
||||
val catnum: Int = tle[1].substring(2, 7).trim().toInt()
|
||||
val bstar: Double =
|
||||
1.0e-5 * tle[1].substring(53, 59).toDouble() / 10.0.pow(tle[1].substring(60, 61).toDouble())
|
||||
val bstar: Double = 1.0e-5 * tle[1].substring(53, 59).toDouble() /
|
||||
10.0.pow(tle[1].substring(60, 61).toDouble())
|
||||
return OrbitalData(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar)
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
@@ -155,10 +153,8 @@ class DataParser(private val defaultDispatcher: CoroutineDispatcher) {
|
||||
val daysArray = arrayOf(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||
var dayOfYear = dayOfMonth
|
||||
// If leap year increment Feb days
|
||||
if (isLeapYear(year)) daysArray[1]++
|
||||
for (i in 0 until month - 1) {
|
||||
dayOfYear += daysArray[i]
|
||||
}
|
||||
if (((year / 4 == 0) && (year / 100 != 0)) || (year / 400 == 0)) daysArray[1]++
|
||||
for (i in 0 until month - 1) { dayOfYear += daysArray[i] }
|
||||
return dayOfYear
|
||||
}
|
||||
}
|
||||
+10
-11
@@ -15,13 +15,12 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.data
|
||||
package com.rtbishop.look4sat.data
|
||||
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||
import com.rtbishop.look4sat.utility.DataParser
|
||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
||||
import com.rtbishop.look4sat.model.DataState
|
||||
import com.rtbishop.look4sat.model.SatEntry
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -30,16 +29,16 @@ import java.util.zip.ZipInputStream
|
||||
|
||||
class DataRepository(
|
||||
private val dataParser: DataParser,
|
||||
private val fileSource: IFileDataSource,
|
||||
private val entrySource: ILocalEntrySource,
|
||||
private val radioSource: ILocalRadioSource,
|
||||
private val remoteSource: IRemoteDataSource,
|
||||
private val fileSource: IFileSource,
|
||||
private val entrySource: IEntriesStorage,
|
||||
private val radioSource: IRadiosStorage,
|
||||
private val remoteSource: INetworkSource,
|
||||
private val repositoryScope: CoroutineScope,
|
||||
private val settingsManager: ISettingsManager
|
||||
private val settingsManager: ISettingsSource
|
||||
) : IDataRepository {
|
||||
|
||||
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
|
||||
_updateState.value = DataState.Error(exception.message)
|
||||
_updateState.value = DataState.Exception(exception.message)
|
||||
}
|
||||
private val updateStateDelay = 875L
|
||||
private val _updateState = MutableStateFlow<DataState<Long>>(DataState.Handled)
|
||||
+5
-5
@@ -15,14 +15,14 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.data
|
||||
package com.rtbishop.look4sat.data
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.Satellite
|
||||
import com.rtbishop.look4sat.model.SatEntry
|
||||
import com.rtbishop.look4sat.model.SatItem
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ILocalEntrySource {
|
||||
interface IEntriesStorage {
|
||||
|
||||
fun getEntriesTotal(): Flow<Int>
|
||||
|
||||
+2
-2
@@ -15,11 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.data
|
||||
package com.rtbishop.look4sat.data
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
interface IFileDataSource {
|
||||
interface IFileSource {
|
||||
|
||||
suspend fun getDataStream(uri: String): InputStream?
|
||||
}
|
||||
+2
-2
@@ -15,11 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.data
|
||||
package com.rtbishop.look4sat.data
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
interface IRemoteDataSource {
|
||||
interface INetworkSource {
|
||||
|
||||
val radioApi: String get() = "https://db.satnogs.org/api/transmitters/?format=json&status=active"
|
||||
|
||||
+3
-3
@@ -15,12 +15,12 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.data
|
||||
package com.rtbishop.look4sat.data
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.model.SatRadio
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ILocalRadioSource {
|
||||
interface IRadiosStorage {
|
||||
|
||||
fun getRadiosTotal(): Flow<Int>
|
||||
|
||||
+2
-1
@@ -15,8 +15,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.model.OrbitalData
|
||||
import kotlin.math.*
|
||||
|
||||
class DeepSpaceSat(data: OrbitalData) : Satellite(data) {
|
||||
@@ -17,10 +17,9 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.model.DataState
|
||||
import com.rtbishop.look4sat.model.SatItem
|
||||
import com.rtbishop.look4sat.model.SatRadio
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
interface ILocationManager {
|
||||
|
||||
val stationPosition: SharedFlow<DataState<GeoPos>>
|
||||
|
||||
fun getStationLocator(): String
|
||||
|
||||
fun getStationPosition(): GeoPos
|
||||
|
||||
fun setStationPosition(latitude: Double, longitude: Double)
|
||||
|
||||
fun setPositionFromGps()
|
||||
|
||||
fun setPositionFromNet()
|
||||
|
||||
fun setPositionFromQth(locator: String)
|
||||
|
||||
fun setPositionHandled()
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.model.StationPos
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface ILocationSource {
|
||||
|
||||
val stationPosition: StateFlow<StationPos>
|
||||
|
||||
fun setGpsPosition(): Boolean
|
||||
|
||||
fun setGeoPosition(latitude: Double, longitude: Double): Boolean
|
||||
|
||||
fun setQthPosition(locator: String): Boolean
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.SatPass
|
||||
import com.rtbishop.look4sat.model.SatPos
|
||||
import com.rtbishop.look4sat.model.SatRadio
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
interface ISatelliteManager {
|
||||
|
||||
+4
-8
@@ -17,9 +17,9 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.model.StationPos
|
||||
|
||||
interface ISettingsManager {
|
||||
interface ISettingsSource {
|
||||
|
||||
val sourcesMap: Map<String, String>
|
||||
get() = mapOf(
|
||||
@@ -51,13 +51,9 @@ interface ISettingsManager {
|
||||
"Amsat" to "https://amsat.org/tle/current/nasabare.txt"
|
||||
)
|
||||
|
||||
fun loadStationLocator(): String
|
||||
fun loadStationPosition(): StationPos
|
||||
|
||||
fun saveStationLocator(locator: String)
|
||||
|
||||
fun loadStationPosition(): GeoPos
|
||||
|
||||
fun saveStationPosition(position: GeoPos)
|
||||
fun saveStationPosition(stationPos: StationPos)
|
||||
|
||||
fun getHoursAhead(): Int
|
||||
|
||||
+2
-1
@@ -15,8 +15,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.model.OrbitalData
|
||||
import kotlin.math.*
|
||||
|
||||
class NearEarthSat(data: OrbitalData) : Satellite(data) {
|
||||
+4
-1
@@ -15,8 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.OrbitalData
|
||||
import com.rtbishop.look4sat.model.SatPos
|
||||
import kotlin.math.*
|
||||
|
||||
const val ASTRONOMICAL_UNIT = 1.49597870691E8
|
||||
+5
-3
@@ -15,10 +15,12 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.SatPass
|
||||
import com.rtbishop.look4sat.model.SatPos
|
||||
import com.rtbishop.look4sat.model.SatRadio
|
||||
import com.rtbishop.look4sat.utility.round
|
||||
import com.rtbishop.look4sat.utility.toDegrees
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
+3
-2
@@ -15,11 +15,12 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.model
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
sealed class DataState<out T> {
|
||||
data class Success<out T>(val data: T) : DataState<T>()
|
||||
data class Error(val message: String?) : DataState<Nothing>()
|
||||
data class Error(val resId: Int?) : DataState<Nothing>()
|
||||
data class Exception(val message: String?) : DataState<Nothing>()
|
||||
object Handled : DataState<Nothing>()
|
||||
object Loading : DataState<Nothing>()
|
||||
}
|
||||
+1
-1
@@ -15,6 +15,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
data class GeoPos(val lat: Double, val lon: Double, val alt: Double = 0.0, val name: String? = null)
|
||||
+3
-1
@@ -15,7 +15,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
import com.rtbishop.look4sat.domain.*
|
||||
|
||||
data class OrbitalData(
|
||||
val name: String,
|
||||
+1
-3
@@ -15,8 +15,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.model
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
data class SatEntry(val data: OrbitalData, var comment: String? = null)
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.model
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
data class SatItem(
|
||||
val catnum: Int,
|
||||
+3
-1
@@ -15,7 +15,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
import com.rtbishop.look4sat.domain.Satellite
|
||||
|
||||
data class SatPass(
|
||||
val aosTime: Long = 0L,
|
||||
+5
-1
@@ -15,8 +15,12 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
import com.rtbishop.look4sat.domain.DEG2RAD
|
||||
import com.rtbishop.look4sat.domain.EARTH_RADIUS
|
||||
import com.rtbishop.look4sat.domain.RAD2DEG
|
||||
import com.rtbishop.look4sat.domain.SPEED_OF_LIGHT
|
||||
import kotlin.math.*
|
||||
|
||||
data class SatPos(
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.model
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
data class SatRadio(
|
||||
val uuid: String,
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
data class StationPos(
|
||||
val latitude: Double, val longitude: Double, val qthLocator: String, val timestamp: Long
|
||||
) {
|
||||
fun toGeoPos(): GeoPos = GeoPos(latitude, longitude)
|
||||
}
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.utility
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.DEG2RAD
|
||||
import com.rtbishop.look4sat.domain.predict.RAD2DEG
|
||||
import com.rtbishop.look4sat.domain.DEG2RAD
|
||||
import com.rtbishop.look4sat.domain.RAD2DEG
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
fun Long.toTimerString(): String {
|
||||
@@ -64,7 +64,7 @@ fun Double.toRadians(): Double = this * DEG2RAD
|
||||
//}
|
||||
|
||||
fun String.isValidIPv4(): Boolean {
|
||||
val ip4 = "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!\$)|\$)){4}\$"
|
||||
val ip4 = "^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(\\.(?!\$)|\$)){4}\$"
|
||||
return this.matches(ip4.toRegex())
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.utility
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
|
||||
object QthConverter {
|
||||
|
||||
@@ -35,27 +35,27 @@ object QthConverter {
|
||||
return GeoPos(latitude, longitude)
|
||||
}
|
||||
|
||||
fun positionToQth(lat: Double, lon: Double): String? {
|
||||
if (!isValidPosition(lat, lon)) return null
|
||||
val tempLon = if (lon > 180.0) lon - 180 else lon
|
||||
fun positionToQth(latitude: Double, longitude: Double): String? {
|
||||
if (!isValidPosition(latitude, longitude)) return null
|
||||
val tempLon = if (longitude > 180.0) longitude - 180 else longitude
|
||||
val upper = "ABCDEFGHIJKLMNOPQRSTUVWX"
|
||||
val lower = "abcdefghijklmnopqrstuvwx"
|
||||
val longitude = tempLon + 180
|
||||
val latitude = lat + 90
|
||||
val lonFirst = upper[(longitude / 20).toInt()]
|
||||
val latFirst = upper[(latitude / 10).toInt()]
|
||||
val lonSecond = ((longitude / 2) % 10).toInt().toString()
|
||||
val latSecond = (latitude % 10).toInt().toString()
|
||||
val lonThird = lower[((longitude % 2) * 12).toInt()]
|
||||
val latThird = lower[((latitude % 1) * 24).toInt()]
|
||||
val newLongitude = tempLon + 180
|
||||
val newLatitude = latitude + 90
|
||||
val lonFirst = upper[(newLongitude / 20).toInt()]
|
||||
val latFirst = upper[(newLatitude / 10).toInt()]
|
||||
val lonSecond = ((newLongitude / 2) % 10).toInt().toString()
|
||||
val latSecond = (newLatitude % 10).toInt().toString()
|
||||
val lonThird = lower[((newLongitude % 2) * 12).toInt()]
|
||||
val latThird = lower[((newLatitude % 1) * 24).toInt()]
|
||||
return "$lonFirst$latFirst$lonSecond$latSecond$lonThird$latThird"
|
||||
}
|
||||
|
||||
fun isValidPosition(lat: Double, lon: Double): Boolean {
|
||||
private fun isValidPosition(lat: Double, lon: Double): Boolean {
|
||||
return (lat >= -90.0 && lat <= 90.0) && (lon >= -180.0 && lon <= 360.0)
|
||||
}
|
||||
|
||||
private fun isValidLocator(locator: String): Boolean {
|
||||
return locator.matches("[a-xA-X][a-xA-X][0-9][0-9][a-xA-X][a-xA-X]".toRegex())
|
||||
return locator.matches("[a-xA-X][a-xA-X]\\d\\d[a-xA-X][a-xA-X]".toRegex())
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat
|
||||
|
||||
import com.rtbishop.look4sat.utility.DataParser
|
||||
import com.rtbishop.look4sat.data.DataParser
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
||||
Reference in new issue
Block a user