mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Added various minor fixes, cleaned up classes
This commit is contained in:
1 parent
5a8108384f
commit
fc0b995bac
35 files changed
+295
-310
No files matched your search
@@ -1,11 +1,13 @@
|
||||
@file: Suppress("UnstableApiUsage")
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("com.google.devtools.ksp")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.rtbishop.look4sat"
|
||||
compileSdk = 33
|
||||
@@ -29,12 +31,7 @@ android {
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
composeOptions { kotlinCompilerExtensionVersion = "1.5.1" }
|
||||
packaging { resources { excludes += listOf("META-INF/{AL2.0,LGPL2.1}") } }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -40,7 +40,7 @@ class MainApplication : Application() {
|
||||
if (timeDelta > 172800000) { // 48 hours in ms
|
||||
val sdf = SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault())
|
||||
println("Started periodic data update on ${sdf.format(Date())}")
|
||||
container.databaseRepo.updateFromWeb()
|
||||
container.databaseRepo.updateFromRemote()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,18 +10,21 @@ import com.rtbishop.look4sat.data.database.MIGRATION_1_2
|
||||
import com.rtbishop.look4sat.data.database.MainDatabase
|
||||
import com.rtbishop.look4sat.data.framework.BluetoothReporter
|
||||
import com.rtbishop.look4sat.data.framework.NetworkReporter
|
||||
import com.rtbishop.look4sat.data.framework.SettingsRepo
|
||||
import com.rtbishop.look4sat.data.repository.DatabaseRepo
|
||||
import com.rtbishop.look4sat.data.repository.SatelliteRepo
|
||||
import com.rtbishop.look4sat.data.repository.SelectionRepo
|
||||
import com.rtbishop.look4sat.data.source.DataSource
|
||||
import com.rtbishop.look4sat.data.source.LocalStorage
|
||||
import com.rtbishop.look4sat.data.repository.SettingsRepo
|
||||
import com.rtbishop.look4sat.data.source.FileSource
|
||||
import com.rtbishop.look4sat.data.source.LocalSource
|
||||
import com.rtbishop.look4sat.data.source.RemoteSource
|
||||
import com.rtbishop.look4sat.data.source.SensorSource
|
||||
import com.rtbishop.look4sat.domain.repository.IDatabaseRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISelectionRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.source.IDataSource
|
||||
import com.rtbishop.look4sat.domain.source.IFileSource
|
||||
import com.rtbishop.look4sat.domain.source.ILocalSource
|
||||
import com.rtbishop.look4sat.domain.source.IRemoteSource
|
||||
import com.rtbishop.look4sat.domain.source.ISensorSource
|
||||
import com.rtbishop.look4sat.domain.utility.DataParser
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
@@ -34,21 +37,16 @@ import org.osmdroid.config.Configuration
|
||||
|
||||
class MainContainer(private val context: Context) {
|
||||
|
||||
private val database =
|
||||
Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb").addMigrations(MIGRATION_1_2)
|
||||
.fallbackToDestructiveMigration().build()
|
||||
private val localStorage = LocalStorage(database.storageDao())
|
||||
private val mainHandler = CoroutineExceptionHandler { _, error -> println("Look4Sat: $error") }
|
||||
private val remoteSource = provideRemoteSource()
|
||||
val mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + mainHandler)
|
||||
private val localSource = provideLocalSource()
|
||||
val mainScope = provideMainScope()
|
||||
val settingsRepo = provideSettingsRepo()
|
||||
val databaseRepo = provideDatabaseRepo()
|
||||
val satelliteRepo = provideSatelliteRepo()
|
||||
val selectionRepo = provideSelectionRepo()
|
||||
val satelliteRepo = provideSatelliteRepo()
|
||||
val databaseRepo = provideDatabaseRepo()
|
||||
|
||||
fun provideBluetoothReporter(): BluetoothReporter {
|
||||
val btManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
return BluetoothReporter(btManager, CoroutineScope(Dispatchers.IO))
|
||||
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
return BluetoothReporter(manager, CoroutineScope(Dispatchers.IO))
|
||||
}
|
||||
|
||||
fun provideNetworkReporter(): NetworkReporter {
|
||||
@@ -56,34 +54,53 @@ class MainContainer(private val context: Context) {
|
||||
}
|
||||
|
||||
fun provideSensorSource(): ISensorSource {
|
||||
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||
return SensorSource(sensorManager, sensor)
|
||||
}
|
||||
|
||||
private fun provideRemoteSource(): IDataSource {
|
||||
val cache = Cache(context.cacheDir, 1000 * 1000 * 10L)
|
||||
val httpClient = OkHttpClient.Builder().cache(cache).build()
|
||||
return DataSource(context.contentResolver, httpClient, Dispatchers.IO)
|
||||
val manager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||
val sensor = manager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||
return SensorSource(manager, sensor)
|
||||
}
|
||||
|
||||
private fun provideDatabaseRepo(): IDatabaseRepo {
|
||||
val dataParser = DataParser(Dispatchers.Default)
|
||||
return DatabaseRepo(Dispatchers.Default, dataParser, remoteSource, localStorage, settingsRepo)
|
||||
val fileSource = provideFileSource()
|
||||
val remoteSource = provideRemoteSource()
|
||||
return DatabaseRepo(Dispatchers.Default, dataParser, fileSource, localSource, remoteSource, settingsRepo)
|
||||
}
|
||||
|
||||
private fun provideFileSource(): IFileSource {
|
||||
return FileSource(context.contentResolver, Dispatchers.IO)
|
||||
}
|
||||
|
||||
private fun provideLocalSource(): ILocalSource {
|
||||
val database = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb").apply {
|
||||
addMigrations(MIGRATION_1_2)
|
||||
fallbackToDestructiveMigration()
|
||||
}.build()
|
||||
return LocalSource(database.storageDao())
|
||||
}
|
||||
|
||||
private fun provideMainScope(): CoroutineScope {
|
||||
val mainHandler = CoroutineExceptionHandler { _, error -> println("Look4Sat: $error") }
|
||||
return CoroutineScope(SupervisorJob() + Dispatchers.Default + mainHandler)
|
||||
}
|
||||
|
||||
private fun provideRemoteSource(): IRemoteSource {
|
||||
val cache = Cache(context.cacheDir, 1000 * 1000 * 10L)
|
||||
val httpClient = OkHttpClient.Builder().cache(cache).build()
|
||||
return RemoteSource(httpClient, Dispatchers.IO)
|
||||
}
|
||||
|
||||
private fun provideSatelliteRepo(): ISatelliteRepo {
|
||||
return SatelliteRepo(Dispatchers.Default, localStorage, settingsRepo)
|
||||
return SatelliteRepo(Dispatchers.Default, localSource, settingsRepo)
|
||||
}
|
||||
|
||||
private fun provideSelectionRepo(): ISelectionRepo {
|
||||
return SelectionRepo(Dispatchers.Default, localStorage, settingsRepo)
|
||||
return SelectionRepo(Dispatchers.Default, localSource, settingsRepo)
|
||||
}
|
||||
|
||||
private fun provideSettingsRepo(): ISettingsRepo {
|
||||
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE)
|
||||
Configuration.getInstance().load(context, preferences)
|
||||
return SettingsRepo(locationManager, preferences)
|
||||
return SettingsRepo(manager, preferences)
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,6 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -34,18 +32,11 @@ private val allModes = listOf(
|
||||
"LRPT", "LSB", "MFSK", "MSK", "MSK AX.100 Mode 5", "MSK AX.100 Mode 6", "OFDM", "OQPSK",
|
||||
"PSK", "PSK31", "PSK63", "QPSK", "QPSK31", "QPSK63", "SSTV", "USB", "WSJT"
|
||||
)
|
||||
private val selectedModes = mutableListOf("AFSK", "AFSK S-Net")
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun ModesDialogPreview() {
|
||||
val modesList = remember { mutableStateOf(selectedModes) }
|
||||
MainTheme {
|
||||
ModesDialog(allModes, modesList.value, {}) {
|
||||
if (modesList.value.contains(it)) modesList.value.remove(it)
|
||||
else modesList.value.add(it)
|
||||
}
|
||||
}
|
||||
MainTheme { ModesDialog(allModes, listOf("AFSK", "AFSK S-Net"), {}) {} }
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -97,9 +97,7 @@ private fun setStationPosition(stationPos: GeoPos, mapView: MapView) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setPositions(
|
||||
posMap: Map<Satellite, GeoPos>, mapView: MapView, action: (Satellite) -> Unit
|
||||
) {
|
||||
private fun setPositions(posMap: Map<Satellite, GeoPos>, mapView: MapView, action: (Satellite) -> Unit) {
|
||||
val markers = FolderOverlay()
|
||||
try {
|
||||
posMap.entries.forEach {
|
||||
@@ -247,9 +245,7 @@ private val tonerLiteTileSource = XYTileSource(
|
||||
|
||||
private fun getColorFilter(): ColorMatrixColorFilter {
|
||||
val negativeMatrix = ColorMatrix(
|
||||
floatArrayOf(
|
||||
-1f, 0f, 0f, 0f, 260f, 0f, -1f, 0f, 0f, 260f, 0f, 0f, -1f, 0f, 260f, 0f, 0f, 0f, 1f, 0f
|
||||
)
|
||||
floatArrayOf(-1f, 0f, 0f, 0f, 260f, 0f, -1f, 0f, 0f, 260f, 0f, 0f, -1f, 0f, 260f, 0f, 0f, 0f, 1f, 0f)
|
||||
)
|
||||
return ColorMatrixColorFilter(negativeMatrix)
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.utility.QthConverter
|
||||
import com.rtbishop.look4sat.domain.utility.clipLat
|
||||
import com.rtbishop.look4sat.domain.utility.clipLon
|
||||
import com.rtbishop.look4sat.domain.utility.positionToQth
|
||||
import com.rtbishop.look4sat.domain.utility.toDegrees
|
||||
import com.rtbishop.look4sat.domain.utility.toTimerString
|
||||
import java.util.Date
|
||||
@@ -66,23 +66,23 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
||||
val footprint: SharedFlow<SatPos> = _footprint
|
||||
|
||||
private val _mapData = MutableSharedFlow<MapData>()
|
||||
val mapData: SharedFlow<MapData> = _mapData
|
||||
// val mapData: SharedFlow<MapData> = _mapData
|
||||
|
||||
private val _positions = MutableSharedFlow<Map<Satellite, GeoPos>>()
|
||||
val positions: SharedFlow<Map<Satellite, GeoPos>> = _positions
|
||||
|
||||
fun scrollSelection(decrement: Boolean) {
|
||||
if (allSatellites.isNotEmpty()) {
|
||||
val index = allSatellites.indexOf(selectedSatellite)
|
||||
if (decrement) {
|
||||
if (index > 0) selectSatellite(allSatellites[index - 1])
|
||||
else selectSatellite(allSatellites[allSatellites.size - 1])
|
||||
} else {
|
||||
if (index < allSatellites.size - 1) selectSatellite(allSatellites[index + 1])
|
||||
else selectSatellite(allSatellites[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
// fun scrollSelection(decrement: Boolean) {
|
||||
// if (allSatellites.isNotEmpty()) {
|
||||
// val index = allSatellites.indexOf(selectedSatellite)
|
||||
// if (decrement) {
|
||||
// if (index > 0) selectSatellite(allSatellites[index - 1])
|
||||
// else selectSatellite(allSatellites[allSatellites.size - 1])
|
||||
// } else {
|
||||
// if (index < allSatellites.size - 1) selectSatellite(allSatellites[index + 1])
|
||||
// else selectSatellite(allSatellites[0])
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fun selectDefaultSatellite(catnum: Int) {
|
||||
if (allSatellites.isNotEmpty()) {
|
||||
@@ -178,7 +178,7 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
||||
val osmLat = clipLat(satPos.latitude.toDegrees())
|
||||
val osmLon = clipLon(satPos.longitude.toDegrees())
|
||||
val osmPos = GeoPos(osmLat, osmLon)
|
||||
val qthLoc = QthConverter.positionToQth(osmPos.latitude, osmPos.longitude) ?: "-- --"
|
||||
val qthLoc = positionToQth(osmPos.latitude, osmPos.longitude) ?: "-- --"
|
||||
val velocity = satPos.getOrbitalVelocity()
|
||||
val phase = satPos.phase.toDegrees()
|
||||
val visibility = satPos.eclipsed
|
||||
|
||||
@@ -88,9 +88,7 @@ fun PassesScreen(navToRadar: (Int, Long) -> Unit) {
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
Column(
|
||||
verticalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.weight(1f)) {
|
||||
Text(text = timerText.value?.first ?: "Null")
|
||||
Text(text = timerText.value?.second ?: "Null")
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class PassesViewModel(
|
||||
|
||||
fun getFilterSettings() = settingsRepo.passesSettings.value
|
||||
|
||||
fun shouldUseUTC() = settingsRepo.otherSettings.value.utcState
|
||||
// fun shouldUseUTC() = settingsRepo.otherSettings.value.utcState
|
||||
|
||||
init {
|
||||
viewModelScope.launch { satelliteRepo.initRepository() }
|
||||
|
||||
+27
-48
@@ -73,27 +73,25 @@ fun PullRefreshIndicator(
|
||||
val showElevation by remember(refreshing, state) {
|
||||
derivedStateOf { refreshing || state.position > 0.5f }
|
||||
}
|
||||
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.size(IndicatorSize)
|
||||
.size(indicatorSize)
|
||||
.pullRefreshIndicatorTransform(state, scale),
|
||||
shape = SpinnerShape,
|
||||
shape = spinnerShape,
|
||||
color = backgroundColor,
|
||||
shadowElevation = if (showElevation) Elevation else 0.dp,
|
||||
shadowElevation = if (showElevation) elevation else 0.dp,
|
||||
) {
|
||||
Crossfade(
|
||||
targetState = refreshing, animationSpec = tween(durationMillis = CrossfadeDurationMs)
|
||||
targetState = refreshing,
|
||||
animationSpec = tween(durationMillis = CROSSFADE_DURATION_MS),
|
||||
label = "pullToRefresh"
|
||||
) { refreshing ->
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center
|
||||
) {
|
||||
val spinnerSize = (ArcRadius + StrokeWidth).times(2)
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
val spinnerSize = (arcRadius + strokeWidth).times(2)
|
||||
if (refreshing) {
|
||||
CircularProgressIndicator(
|
||||
color = contentColor,
|
||||
strokeWidth = StrokeWidth,
|
||||
strokeWidth = strokeWidth,
|
||||
modifier = Modifier.size(spinnerSize),
|
||||
)
|
||||
} else {
|
||||
@@ -108,18 +106,12 @@ fun PullRefreshIndicator(
|
||||
* Modifier.size MUST be specified.
|
||||
*/
|
||||
@Composable
|
||||
private fun CircularArrowIndicator(
|
||||
state: PullRefreshState,
|
||||
color: Color,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
private fun CircularArrowIndicator(state: PullRefreshState, color: Color, modifier: Modifier) {
|
||||
val path = remember { Path().apply { fillType = PathFillType.EvenOdd } }
|
||||
|
||||
Canvas(modifier.semantics { contentDescription = "Refreshing" }) {
|
||||
val values = ArrowValues(state.progress)
|
||||
|
||||
rotate(degrees = values.rotation) {
|
||||
val arcRadius = ArcRadius.toPx() + StrokeWidth.toPx() / 2f
|
||||
val arcRadius = arcRadius.toPx() + strokeWidth.toPx() / 2f
|
||||
val arcBounds = Rect(
|
||||
size.center.x - arcRadius,
|
||||
size.center.y - arcRadius,
|
||||
@@ -134,9 +126,7 @@ private fun CircularArrowIndicator(
|
||||
useCenter = false,
|
||||
topLeft = arcBounds.topLeft,
|
||||
size = arcBounds.size,
|
||||
style = Stroke(
|
||||
width = StrokeWidth.toPx(), cap = StrokeCap.Square
|
||||
)
|
||||
style = Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Square)
|
||||
)
|
||||
drawArrow(path, arcBounds, color, values)
|
||||
}
|
||||
@@ -145,11 +135,7 @@ private fun CircularArrowIndicator(
|
||||
|
||||
@Immutable
|
||||
private class ArrowValues(
|
||||
val alpha: Float,
|
||||
val rotation: Float,
|
||||
val startAngle: Float,
|
||||
val endAngle: Float,
|
||||
val scale: Float
|
||||
val alpha: Float, val rotation: Float, val startAngle: Float, val endAngle: Float, val scale: Float
|
||||
)
|
||||
|
||||
private fun ArrowValues(progress: Float): ArrowValues {
|
||||
@@ -164,7 +150,7 @@ private fun ArrowValues(progress: Float): ArrowValues {
|
||||
|
||||
// Calculations based on SwipeRefreshLayout specification.
|
||||
val alpha = progress.coerceIn(0f, 1f)
|
||||
val endTrim = adjustedPercent * MaxProgressArc
|
||||
val endTrim = adjustedPercent * MAX_PROGRESS_ARC
|
||||
val rotation = (-0.25f + 0.4f * adjustedPercent + tensionPercent) * 0.5f
|
||||
val startAngle = rotation * 360
|
||||
val endAngle = (rotation + endTrim) * 360
|
||||
@@ -176,33 +162,26 @@ private fun ArrowValues(progress: Float): ArrowValues {
|
||||
private fun DrawScope.drawArrow(arrow: Path, bounds: Rect, color: Color, values: ArrowValues) {
|
||||
arrow.reset()
|
||||
arrow.moveTo(0f, 0f) // Move to left corner
|
||||
arrow.lineTo(x = ArrowWidth.toPx() * values.scale, y = 0f) // Line to right corner
|
||||
arrow.lineTo(x = arrowWidth.toPx() * values.scale, y = 0f) // Line to right corner
|
||||
|
||||
// Line to tip of arrow
|
||||
arrow.lineTo(
|
||||
x = ArrowWidth.toPx() * values.scale / 2, y = ArrowHeight.toPx() * values.scale
|
||||
)
|
||||
arrow.lineTo(x = arrowWidth.toPx() * values.scale / 2, y = arrowHeight.toPx() * values.scale)
|
||||
|
||||
val radius = min(bounds.width, bounds.height) / 2f
|
||||
val inset = ArrowWidth.toPx() * values.scale / 2f
|
||||
arrow.translate(
|
||||
Offset(
|
||||
x = radius + bounds.center.x - inset, y = bounds.center.y + StrokeWidth.toPx() / 2f
|
||||
)
|
||||
)
|
||||
val inset = arrowWidth.toPx() * values.scale / 2f
|
||||
arrow.translate(Offset(x = radius + bounds.center.x - inset, y = bounds.center.y + strokeWidth.toPx() / 2f))
|
||||
arrow.close()
|
||||
rotate(degrees = values.endAngle) {
|
||||
drawPath(path = arrow, color = color, alpha = values.alpha)
|
||||
}
|
||||
}
|
||||
|
||||
private const val CrossfadeDurationMs = 100
|
||||
private const val MaxProgressArc = 0.8f
|
||||
|
||||
private val IndicatorSize = 40.dp
|
||||
private val SpinnerShape = CircleShape
|
||||
private val ArcRadius = 7.5.dp
|
||||
private val StrokeWidth = 2.5.dp
|
||||
private val ArrowWidth = 10.dp
|
||||
private val ArrowHeight = 5.dp
|
||||
private val Elevation = 6.dp
|
||||
private const val CROSSFADE_DURATION_MS = 100
|
||||
private const val MAX_PROGRESS_ARC = 0.8f
|
||||
private val indicatorSize = 40.dp
|
||||
private val spinnerShape = CircleShape
|
||||
private val arcRadius = 7.5.dp
|
||||
private val strokeWidth = 2.5.dp
|
||||
private val arrowWidth = 10.dp
|
||||
private val arrowHeight = 5.dp
|
||||
private val elevation = 6.dp
|
||||
+3
-11
@@ -49,12 +49,10 @@ fun Modifier.pullRefreshIndicatorTransform(
|
||||
properties["scale"] = scale
|
||||
}) {
|
||||
var height by remember { mutableStateOf(0) }
|
||||
|
||||
Modifier
|
||||
.onSizeChanged { height = it.height }
|
||||
.graphicsLayer {
|
||||
translationY = state.position - height
|
||||
|
||||
if (scale && !state.refreshing) {
|
||||
val scaleFraction = LinearOutSlowInEasing
|
||||
.transform(state.position / state.threshold)
|
||||
@@ -95,9 +93,7 @@ fun Modifier.pullRefresh(
|
||||
* [onPull] nor [onRelease] will be invoked.
|
||||
*/
|
||||
fun Modifier.pullRefresh(
|
||||
onPull: (pullDelta: Float) -> Float,
|
||||
onRelease: suspend (flingVelocity: Float) -> Unit,
|
||||
enabled: Boolean = true
|
||||
onPull: (pullDelta: Float) -> Float, onRelease: suspend (flingVelocity: Float) -> Unit, enabled: Boolean = true
|
||||
) = inspectable(inspectorInfo = debugInspectorInfo {
|
||||
name = "pullRefresh"
|
||||
properties["onPull"] = onPull
|
||||
@@ -113,17 +109,13 @@ private class PullRefreshNestedScrollConnection(
|
||||
private val enabled: Boolean
|
||||
) : NestedScrollConnection {
|
||||
|
||||
override fun onPreScroll(
|
||||
available: Offset, source: NestedScrollSource
|
||||
): Offset = when {
|
||||
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset = when {
|
||||
!enabled -> Offset.Zero
|
||||
source == Drag && available.y < 0 -> Offset(0f, onPull(available.y)) // Swiping up
|
||||
else -> Offset.Zero
|
||||
}
|
||||
|
||||
override fun onPostScroll(
|
||||
consumed: Offset, available: Offset, source: NestedScrollSource
|
||||
): Offset = when {
|
||||
override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset = when {
|
||||
!enabled -> Offset.Zero
|
||||
source == Drag && available.y > 0 -> Offset(0f, onPull(available.y)) // Pulling down
|
||||
else -> Offset.Zero
|
||||
|
||||
+4
-13
@@ -53,27 +53,18 @@ fun rememberPullRefreshState(
|
||||
refreshingOffset: Dp = PullRefreshDefaults.RefreshingOffset,
|
||||
): PullRefreshState {
|
||||
require(refreshThreshold > 0.dp) { "The refresh trigger must be greater than zero!" }
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
val onRefreshState = rememberUpdatedState(onRefresh)
|
||||
val thresholdPx: Float
|
||||
val refreshingOffsetPx: Float
|
||||
|
||||
with(LocalDensity.current) {
|
||||
thresholdPx = refreshThreshold.toPx()
|
||||
refreshingOffsetPx = refreshingOffset.toPx()
|
||||
}
|
||||
|
||||
// refreshThreshold and refreshingOffset should not be changed after instantiation, so any
|
||||
// changes to these values are ignored.
|
||||
val state = remember(scope) {
|
||||
PullRefreshState(scope, onRefreshState, refreshingOffsetPx, thresholdPx)
|
||||
}
|
||||
|
||||
SideEffect {
|
||||
state.setRefreshing(refreshing)
|
||||
}
|
||||
|
||||
val state = remember(scope) { PullRefreshState(scope, onRefreshState, refreshingOffsetPx, thresholdPx) }
|
||||
SideEffect { state.setRefreshing(refreshing) }
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -109,7 +100,7 @@ class PullRefreshState internal constructor(
|
||||
internal val refreshing get() = _refreshing
|
||||
internal val position get() = _position
|
||||
|
||||
private val adjustedDistancePulled by derivedStateOf { distancePulled * DragMultiplier }
|
||||
private val adjustedDistancePulled by derivedStateOf { distancePulled * DRAG_MULTIPLIER }
|
||||
|
||||
private var _refreshing by mutableStateOf(false)
|
||||
private var _position by mutableStateOf(0f)
|
||||
@@ -189,4 +180,4 @@ object PullRefreshDefaults {
|
||||
* the refresh threshold, it is the indicator position, otherwise the indicator position is
|
||||
* derived from the progress).
|
||||
*/
|
||||
private const val DragMultiplier = 0.5f
|
||||
private const val DRAG_MULTIPLIER = 0.5f
|
||||
@@ -44,14 +44,9 @@ fun RadarScreen() {
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
IconButton(onClick = {}) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_filter),
|
||||
contentDescription = null
|
||||
)
|
||||
Icon(painter = painterResource(id = R.drawable.ic_filter), contentDescription = null)
|
||||
}
|
||||
Column(
|
||||
verticalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.weight(1f)) {
|
||||
Text(text = "Null")
|
||||
Text(text = "Null")
|
||||
}
|
||||
@@ -109,10 +104,7 @@ private fun TransmitterItem(radio: SatRadio) {
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.padding(6.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_arrow),
|
||||
contentDescription = null, modifier = Modifier.rotate(180f)
|
||||
@@ -127,10 +119,7 @@ private fun TransmitterItem(radio: SatRadio) {
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.radio_downlink, radio.downlink ?: 0L),
|
||||
textAlign = TextAlign.Center,
|
||||
|
||||
@@ -43,9 +43,9 @@ fun RadarViewCompose(item: SatPos, items: List<SatPos>, azimElev: Pair<Float, Fl
|
||||
val primaryColor = Color(0xFFFFE082)
|
||||
val secondaryColor = Color(0xFFDC0000)
|
||||
val strokeWidth = 4f
|
||||
val animTransition = rememberInfiniteTransition()
|
||||
val animTransition = rememberInfiniteTransition(label = "animScale")
|
||||
val animSpec = infiniteRepeatable<Float>(tween(1000))
|
||||
val animScale = animTransition.animateFloat(16f, 64f, animSpec)
|
||||
val animScale = animTransition.animateFloat(16f, 64f, animSpec, label = "animScale")
|
||||
val measurer = rememberTextMeasurer()
|
||||
val sweepDegrees = remember { mutableStateOf(0f) }
|
||||
val trackCreated = remember { mutableStateOf(false) }
|
||||
|
||||
@@ -99,9 +99,9 @@ class RadarViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun getUseCompass(): Boolean = settingsRepo.otherSettings.value.sensorState
|
||||
|
||||
fun getShowSweep(): Boolean = settingsRepo.otherSettings.value.sweepState
|
||||
// fun getUseCompass(): Boolean = settingsRepo.otherSettings.value.sensorState
|
||||
//
|
||||
// fun getShowSweep(): Boolean = settingsRepo.otherSettings.value.sweepState
|
||||
|
||||
private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
|
||||
val latitude = geoPos.latitude.toFloat()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.rtbishop.look4sat.presentation.settings
|
||||
|
||||
import android.Manifest
|
||||
import android.os.Build
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -61,16 +60,16 @@ fun SettingsScreen() {
|
||||
val context = LocalContext.current
|
||||
|
||||
// Permissions setup
|
||||
val bluetoothContract = ActivityResultContracts.RequestPermission()
|
||||
val bluetoothError = stringResource(R.string.BTremote_perm_error)
|
||||
val bluetoothPerm = when {
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.S -> Manifest.permission.BLUETOOTH
|
||||
else -> Manifest.permission.BLUETOOTH_CONNECT
|
||||
}
|
||||
val bluetoothRequest = rememberLauncherForActivityResult(bluetoothContract) { isGranted ->
|
||||
// val bluetoothContract = ActivityResultContracts.RequestPermission()
|
||||
// val bluetoothError = stringResource(R.string.BTremote_perm_error)
|
||||
// val bluetoothPerm = when {
|
||||
// Build.VERSION.SDK_INT < Build.VERSION_CODES.S -> Manifest.permission.BLUETOOTH
|
||||
// else -> Manifest.permission.BLUETOOTH_CONNECT
|
||||
// }
|
||||
// val bluetoothRequest = rememberLauncherForActivityResult(bluetoothContract) { isGranted ->
|
||||
// viewModel.setBTEnabled(isGranted)
|
||||
if (!isGranted) showToast(context, bluetoothError)
|
||||
}
|
||||
// if (!isGranted) showToast(context, bluetoothError)
|
||||
// }
|
||||
val locationContract = ActivityResultContracts.RequestMultiplePermissions()
|
||||
val locationError = stringResource(R.string.location_gps_error)
|
||||
val locationPermCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
@@ -119,9 +118,7 @@ fun SettingsScreen() {
|
||||
val toggleSensor = { value: Boolean -> viewModel.toggleSensor(value) }
|
||||
|
||||
// Screen setup
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
item { CardAbout(BuildConfig.VERSION_NAME) }
|
||||
item { LocationCard(positionSettings, setGpsPos, showPosDialog, showLocDialog, dismissPos) }
|
||||
item { DataCard(dataSettings, updateFromWeb, updateFromFile, clearAllData) }
|
||||
@@ -376,8 +373,7 @@ private fun DataCard(
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun OtherCardPreview() = MainTheme {
|
||||
val values =
|
||||
OtherSettings(utcState = false, updateState = true, sweepState = true, sensorState = true)
|
||||
val values = OtherSettings(utcState = false, updateState = true, sweepState = true, sensorState = true)
|
||||
OtherCard(settings = values, {}, {}, {}, {})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,4 @@ import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
|
||||
data class PositionSettings(val isUpdating: Boolean, val stationPos: GeoPos, val messageResId: Int)
|
||||
|
||||
data class DataSettings(
|
||||
val isUpdating: Boolean, val entriesTotal: Int, val radiosTotal: Int, val timestamp: Long
|
||||
)
|
||||
data class DataSettings(val isUpdating: Boolean, val entriesTotal: Int, val radiosTotal: Int, val timestamp: Long)
|
||||
@@ -106,7 +106,7 @@ class SettingsViewModel(
|
||||
fun updateFromWeb() = viewModelScope.launch {
|
||||
try {
|
||||
_dataSettings.value = _dataSettings.value.copy(isUpdating = true)
|
||||
databaseRepo.updateFromWeb()
|
||||
databaseRepo.updateFromRemote()
|
||||
} catch (exception: Exception) {
|
||||
_dataSettings.value = _dataSettings.value.copy(isUpdating = false)
|
||||
println(exception.printStackTrace())
|
||||
|
||||
@@ -147,8 +147,6 @@
|
||||
<string name="data_success">යාවත්කාලීනය සම්පූර්ණ කිරීම සාර්ථකයි</string>
|
||||
<string name="data_clear_success">දත්ත ඉවත් කිරීම සාර්ථකයි</string>
|
||||
|
||||
<string name="last_updated_pattern" translatable="false">d MMM yyyy - HH:mm:ss</string>
|
||||
|
||||
<string name="remote_title">භ්රමක මනාප</string>
|
||||
<string name="remote_switch">දුරස්ථ පාලනය සබල ක°</string>
|
||||
<string name="remote_ip_hint">IP ලිපිනය</string>
|
||||
|
||||
Reference in new issue
Block a user