diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 95921e4a..a3488121 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ + android:screenOrientation="portrait" + android:theme="@style/Theme.Look4Sat.SplashScreen" + tools:ignore="LockedOrientationActivity"> - diff --git a/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt b/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt index 11c16578..c543c343 100644 --- a/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt +++ b/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt @@ -18,7 +18,6 @@ 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 @@ -32,15 +31,13 @@ import dagger.hilt.android.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 + val newConfig = Configuration(newBase?.resources?.configuration).apply { fontScale = 1.0f } applyOverrideConfiguration(newConfig) super.attachBaseContext(newBase) } diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/LocationSource.kt b/app/src/main/java/com/rtbishop/look4sat/framework/LocationSource.kt index bbb0f854..4d80f303 100644 --- a/app/src/main/java/com/rtbishop/look4sat/framework/LocationSource.kt +++ b/app/src/main/java/com/rtbishop/look4sat/framework/LocationSource.kt @@ -17,6 +17,7 @@ */ package com.rtbishop.look4sat.framework +import android.location.Criteria import android.location.Location import android.location.LocationManager import androidx.core.location.LocationListenerCompat @@ -35,13 +36,15 @@ class LocationSource( private val settings: ISettingsSource, ) : ILocationSource, LocationListenerCompat { + private val defaultProvider = LocationManager.PASSIVE_PROVIDER private val _stationPosition = MutableStateFlow(settings.loadStationPosition()) override val stationPosition: StateFlow = _stationPosition override fun setGpsPosition(): Boolean { if (!LocationManagerCompat.isLocationEnabled(manager)) return false - forceLocationUpdate(LocationManager.GPS_PROVIDER) - forceLocationUpdate(LocationManager.NETWORK_PROVIDER) + val criteria = Criteria().apply { isCostAllowed = true } + val bestProvider = manager.getBestProvider(criteria, true) ?: defaultProvider + forceLocationUpdate(bestProvider) return true } @@ -65,7 +68,7 @@ class LocationSource( private fun forceLocationUpdate(provider: String) { try { - val location = manager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER) + val location = manager.getLastKnownLocation(defaultProvider) if (location == null || System.currentTimeMillis() - location.time > 600000) { Timber.d("Requesting $provider location update") manager.requestLocationUpdates(provider, 0L, 0f, this) diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/SettingsSource.kt b/app/src/main/java/com/rtbishop/look4sat/framework/SettingsSource.kt index 725be84c..3d7dc8be 100644 --- a/app/src/main/java/com/rtbishop/look4sat/framework/SettingsSource.kt +++ b/app/src/main/java/com/rtbishop/look4sat/framework/SettingsSource.kt @@ -50,7 +50,7 @@ class SettingsSource(private val prefs: SharedPreferences) : ISettingsSource { 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 qthLocator = prefs.getString(keyLocator, null) ?: "null" val timestamp = prefs.getLong(keyLocTimestamp, 0L) return StationPos(latitude.toDouble(), longitude.toDouble(), qthLocator, timestamp) } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/MainComponents.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/Components.kt similarity index 96% rename from app/src/main/java/com/rtbishop/look4sat/presentation/MainComponents.kt rename to app/src/main/java/com/rtbishop/look4sat/presentation/Components.kt index 3881b1d5..6e45b6e5 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/MainComponents.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/Components.kt @@ -9,7 +9,6 @@ import androidx.compose.foundation.interaction.Interaction import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.* import androidx.compose.runtime.Composable @@ -28,7 +27,7 @@ fun CardButton(onClick: () -> Unit, text: String, modifier: Modifier = Modifier) onClick = { onClick() }, colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.tertiary, contentColor = MaterialTheme.colorScheme.onTertiary - ), shape = MaterialTheme.shapes.small, modifier = modifier.padding(start = 4.dp, end = 4.dp) + ), shape = MaterialTheme.shapes.small, modifier = modifier ) { Text(text = text, fontSize = 17.sp) } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt index 20e69fdb..721f36fe 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt @@ -60,14 +60,18 @@ private fun MainNavBar(navController: NavController) { @Composable private fun MainNavGraph(navController: NavHostController) { + val navToPasses = { navController.navigate(Screen.Passes.route) } + val navToRadar = { catNum: Int, aosTime: Long -> + navController.navigate("${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}") + } val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}" val radarArgs = listOf(navArgument("catNum") { defaultValue = 0 }, navArgument("aosTime") { defaultValue = 0L }) NavHost(navController, startDestination = Screen.Passes.route) { - composable(Screen.Entries.route) { EntriesScreen(navController) } - composable(Screen.Passes.route) { PassesScreen(navController) } - composable(radarRoute, radarArgs) { RadarScreen(navController) } + composable(Screen.Entries.route) { EntriesScreen(navToPasses) } + composable(Screen.Passes.route) { PassesScreen(navToRadar) } + composable(radarRoute, radarArgs) { RadarScreen() } composable(Screen.Map.route) { MapScreen() } - composable(Screen.Settings.route) { SettingsScreen(navController) } + composable(Screen.Settings.route) { SettingsScreen() } } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt index 9ebd186c..5f4e934b 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt @@ -26,21 +26,17 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp 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.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 -fun EntriesScreen(navController: NavController) { - val viewModel: EntriesViewModel = hiltViewModel() +fun EntriesScreen(navToPasses: () -> Unit, viewModel: EntriesViewModel = hiltViewModel()) { val state = viewModel.satData.collectAsState(initial = DataState.Loading) - val navToPasses = { navController.navigate(Screen.Passes.route) } val showDialog = rememberSaveable { mutableStateOf(false) } val toggleDialog = { showDialog.value = showDialog.value.not() } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt index 2c82c362..8ed372d9 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt @@ -102,8 +102,8 @@ private fun setPositions( posMap: Map, mapView: MapView, action: (Satellite) -> Unit ) { val markers = FolderOverlay() - posMap.entries.forEach { - try { + try { + posMap.entries.forEach { Marker(mapView).apply { setInfoWindow(null) setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER) @@ -119,12 +119,12 @@ private fun setPositions( } markers.add(this) } - } catch (exception: Exception) { - Timber.d(exception) } + mapView.overlays[3] = markers + mapView.invalidate() + } catch (exception: Exception) { + Timber.d(exception) } - mapView.overlays[3] = markers - mapView.invalidate() } private fun getCustomTextIcon(textLabel: String, mapView: MapView): Drawable { @@ -143,33 +143,33 @@ private fun getCustomTextIcon(textLabel: String, mapView: MapView): Drawable { private fun setSatelliteTrack(satTrack: List>, mapView: MapView) { // val center = GeoPoint(satTrack[0][0].lat, satTrack[0][0].lon) val trackOverlay = FolderOverlay() - satTrack.forEach { track -> - val trackPoints = track.map { GeoPoint(it.lat, it.lon) } - Polyline().apply { - try { + try { + satTrack.forEach { track -> + val trackPoints = track.map { GeoPoint(it.lat, it.lon) } + Polyline().apply { setPoints(trackPoints) outlinePaint.set(trackPaint) trackOverlay.add(this) - } catch (exception: IllegalArgumentException) { - println(exception.stackTraceToString()) } } + mapView.overlays[1] = trackOverlay + } catch (exception: Exception) { + Timber.d(exception) } - mapView.overlays[1] = trackOverlay // mapView.controller.animateTo(center) } private fun setFootprint(satPos: SatPos, mapView: MapView) { val footprintPoints = satPos.getRangeCircle().map { GeoPoint(it.lat, it.lon) } - val footprintOverlay = Polyline().apply { - outlinePaint.set(footprintPaint) - try { + try { + val footprintOverlay = Polyline().apply { + outlinePaint.set(footprintPaint) setPoints(footprintPoints) - } catch (exception: IllegalArgumentException) { - println(exception.stackTraceToString()) } + mapView.overlays[2] = footprintOverlay + } catch (exception: Exception) { + Timber.d(exception) } - mapView.overlays[2] = footprintOverlay } //private fun handleMapData(mapData: MapData) { diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt index 011ae921..2de6b221 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt @@ -21,7 +21,6 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview 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.NearEarthSat import com.rtbishop.look4sat.model.DataState @@ -39,11 +38,7 @@ import java.util.* private val sdf = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH) @Composable -fun PassesScreen(navController: NavController) { - val viewModel: PassesViewModel = hiltViewModel() - val navToRadar = { catNum: Int, aosTime: Long -> - navController.navigate("${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}") - } +fun PassesScreen(navToRadar: (Int, Long) -> Unit, viewModel: PassesViewModel = hiltViewModel()) { val state = viewModel.passes.collectAsState(initial = null) val timerText = viewModel.timerText.collectAsState(initial = null) val isRefreshing = state.value is DataState.Loading @@ -185,7 +180,6 @@ private fun Pass(pass: SatPass, navToRadar: (Int, Long) -> Unit, modifier: Modif modifier = modifier .fillMaxWidth(0.75f) .padding(top = 3.dp), - color = MaterialTheme.colorScheme.primary, trackColor = MaterialTheme.colorScheme.inverseSurface ) Text( diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt index aa8e3e6b..6c6ce37a 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt @@ -16,13 +16,11 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp 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.model.SatRadio @Composable -fun RadarScreen(navController: NavController) { - val viewModel: RadarViewModel = hiltViewModel() +fun RadarScreen(viewModel: RadarViewModel = hiltViewModel()) { val currentPass = viewModel.getPass().collectAsState(null) Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt index 6c00cc1c..f33de6a9 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt @@ -21,15 +21,17 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavController import com.rtbishop.look4sat.BuildConfig import com.rtbishop.look4sat.R +import com.rtbishop.look4sat.model.StationPos 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 +import java.text.SimpleDateFormat +import java.util.* private const val POLICY_URL = "https://sites.google.com/view/look4sat-privacy-policy/home" private const val LICENSE_URL = "https://www.gnu.org/licenses/gpl-3.0.html" @@ -38,9 +40,8 @@ private const val DONATE_URL = "https://ko-fi.com/rt_bishop" private const val FDROID_URL = "https://f-droid.org/en/packages/com.rtbishop.look4sat/" @Composable -fun SettingsScreen(navController: NavController) { +fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) { val context = LocalContext.current - val viewModel: SettingsViewModel = hiltViewModel() // Permissions setup val bluetoothContract = ActivityResultContracts.RequestPermission() @@ -76,8 +77,8 @@ fun SettingsScreen(navController: NavController) { val togglePosDialog = { showPosDialog.value = showPosDialog.value.not() } if (showPosDialog.value) { PositionDialog( - locSettings.getLatitude, - locSettings.getLongitude, + locSettings.stationPos.latitude, + locSettings.stationPos.longitude, togglePosDialog, locSettings.setManualLoc ) @@ -85,7 +86,7 @@ fun SettingsScreen(navController: NavController) { val showLocDialog = rememberSaveable { mutableStateOf(false) } val toggleLocDialog = { showLocDialog.value = showLocDialog.value.not() } if (showLocDialog.value) { - LocatorDialog(locSettings.getLocator, toggleLocDialog, locSettings.setQthLoc) + LocatorDialog(locSettings.stationPos.qthLocator, toggleLocDialog, locSettings.setQthLoc) } // Screen setup @@ -100,11 +101,18 @@ fun SettingsScreen(navController: NavController) { } } +@Preview(showBackground = true) +@Composable +private fun CardAboutPreview() = MainTheme { CardAbout(version = "4.0.0") } + @Composable private fun CardAbout(version: String, modifier: Modifier = Modifier) { val context = LocalContext.current ElevatedCard(modifier = modifier.fillMaxWidth()) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp) + ) { Row(horizontalArrangement = Arrangement.Center) { Icon( painter = painterResource(id = R.drawable.ic_entries), @@ -128,21 +136,21 @@ private fun CardAbout(version: String, modifier: Modifier = Modifier) { Text( text = stringResource(id = R.string.app_subtitle), fontSize = 20.sp, - modifier = modifier.padding(top = 4.dp) + modifier = modifier.padding(top = 4.dp, bottom = 2.dp) ) - Row( - horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.padding(4.dp) - ) { + Row(horizontalArrangement = Arrangement.SpaceEvenly) { CardButton( onClick = { gotoUrl(context, GITHUB_URL) }, text = stringResource(id = R.string.btn_github), modifier = Modifier.weight(1f) ) + Spacer(modifier = Modifier.width(6.dp)) CardButton( onClick = { gotoUrl(context, DONATE_URL) }, text = stringResource(id = R.string.btn_donate), modifier = Modifier.weight(1f) ) + Spacer(modifier = Modifier.width(6.dp)) CardButton( onClick = { gotoUrl(context, FDROID_URL) }, text = stringResource(id = R.string.btn_fdroid), @@ -155,8 +163,11 @@ private fun CardAbout(version: String, modifier: Modifier = Modifier) { @Preview(showBackground = true) @Composable -private fun LocationCardPreview() { - MainTheme { } +private fun LocationCardPreview() = MainTheme { + val stationPos = StationPos(0.0, 0.0, "IO91vl", 0L) + LocationCard( + settings = LocationSettings(true, stationPos, {}, { _, _ -> }, { }), + setGpsLoc = {}, togglePosDialog = {}) {} } @Composable @@ -167,36 +178,56 @@ private fun LocationCard( toggleLocDialog: () -> Unit ) { ElevatedCard(modifier = Modifier.fillMaxWidth()) { - Column(modifier = Modifier.padding(4.dp)) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp) + ) { Row(verticalAlignment = Alignment.CenterVertically) { - Text(text = "Station position") - UpdateIndicator(isUpdating = settings.getUpdating, Modifier.weight(1f)) + Text( + text = stringResource(id = R.string.location_title), + color = MaterialTheme.colorScheme.primary + ) + UpdateIndicator(isUpdating = settings.isUpdating, Modifier.weight(1f)) } - Row( - horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() - ) { - Text(text = "Updated: ${settings.getLastUpdated}") - Text(text = "QTH: ${settings.getLocator}") - } - Row( - horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() - ) { - Text(text = "Latitude: ${settings.getLatitude}") - Text(text = "Longitude: ${settings.getLongitude}") + Spacer(modifier = Modifier.height(2.dp)) + Text(text = setUpdateTime(updateTime = settings.stationPos.timestamp)) + Spacer(modifier = Modifier.height(2.dp)) + Row(horizontalArrangement = Arrangement.SpaceBetween) { + Text( + text = "Lat: ${settings.stationPos.latitude}°", + modifier = Modifier.weight(1f), + textAlign = TextAlign.Start + ) + Text( + text = "Lon: ${settings.stationPos.longitude}°", + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center + ) + Text( + text = "Qth: ${settings.stationPos.qthLocator}", + modifier = Modifier.weight(1f), + textAlign = TextAlign.End + ) } + Spacer(modifier = Modifier.height(1.dp)) Row(horizontalArrangement = Arrangement.SpaceEvenly) { CardButton( - onClick = setGpsLoc, // locationRequest.launch(arrayOf(locationFine, locationCoarse)) - text = stringResource(id = R.string.btn_gps), modifier = Modifier.weight(1f) + onClick = setGpsLoc, + text = stringResource(id = R.string.btn_gps), + modifier = Modifier.weight(1f) ) + Spacer(modifier = Modifier.width(6.dp)) CardButton( - onClick = togglePosDialog, // open Position dialog - text = stringResource(id = R.string.btn_manual), modifier = Modifier.weight(1f) - ) // viewModel.setStationPosition(position.first, position.second) + onClick = togglePosDialog, + text = stringResource(id = R.string.btn_manual), + modifier = Modifier.weight(1f) + ) + Spacer(modifier = Modifier.width(6.dp)) CardButton( - onClick = toggleLocDialog, // open Locator dialog - text = stringResource(id = R.string.btn_qth), modifier = Modifier.weight(1f) - ) // viewModel.setPositionFromQth(locator) + onClick = toggleLocDialog, + text = stringResource(id = R.string.btn_qth), + modifier = Modifier.weight(1f) + ) } } } @@ -204,42 +235,52 @@ private fun LocationCard( @Preview(showBackground = true) @Composable -private fun DataCardPreview() { - MainTheme { } +private fun DataCardPreview() = MainTheme { + DataCard(settings = DataSettings( + true, 0L, 5000, 2500, {}, { }, {}) + ) {} } @Composable private fun DataCard(settings: DataSettings, updateFromFile: () -> Unit) { ElevatedCard(modifier = Modifier.fillMaxWidth()) { - Column(modifier = Modifier.padding(4.dp)) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp) + ) { Row(verticalAlignment = Alignment.CenterVertically) { - Text(text = "Satellite data") - UpdateIndicator(isUpdating = settings.getUpdating, Modifier.weight(1f)) + Text( + text = stringResource(id = R.string.data_title), + color = MaterialTheme.colorScheme.primary + ) + UpdateIndicator(isUpdating = settings.isUpdating, Modifier.weight(1f)) } - Row( - horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() - ) { - Text(text = "Updated: ${settings.getLastUpdated}") - Text(text = "") - } - Row( - horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() - ) { - Text(text = "Satellites: ${settings.getSatellites}") - Text(text = "Transceivers: ${settings.getRadios}") + Spacer(modifier = Modifier.height(2.dp)) + Text(text = setUpdateTime(updateTime = settings.lastUpdated)) + Spacer(modifier = Modifier.height(2.dp)) + Row(horizontalArrangement = Arrangement.SpaceBetween) { + Text(text = "Satellites: ${settings.satsTotal}") + Spacer(modifier = Modifier.weight(1f)) + Text(text = "Transceivers: ${settings.radiosTotal}") } + Spacer(modifier = Modifier.height(1.dp)) Row(horizontalArrangement = Arrangement.SpaceEvenly) { CardButton( - onClick = settings.updateFromWeb, // viewModel.updateFromWeb() - text = stringResource(id = R.string.btn_web), modifier = Modifier.weight(1f) + onClick = settings.updateFromWeb, + text = stringResource(id = R.string.btn_web), + modifier = Modifier.weight(1f) ) + Spacer(modifier = Modifier.width(6.dp)) CardButton( - onClick = updateFromFile, // contentRequest.launch("*/*") - text = stringResource(id = R.string.btn_file), modifier = Modifier.weight(1f) + onClick = updateFromFile, + text = stringResource(id = R.string.btn_file), + modifier = Modifier.weight(1f) ) + Spacer(modifier = Modifier.width(6.dp)) CardButton( - onClick = settings.clearAllData, // viewModel.clearAllData() - text = stringResource(id = R.string.btn_clear), modifier = Modifier.weight(1f) + onClick = settings.clearAllData, + text = stringResource(id = R.string.btn_clear), + modifier = Modifier.weight(1f) ) } } @@ -293,15 +334,17 @@ private fun DataCard(settings: DataSettings, updateFromFile: () -> Unit) { // @Preview(showBackground = true) @Composable -private fun OtherCardPreview() { - MainTheme { } +private fun OtherCardPreview() = MainTheme { + OtherCard(settings = OtherSettings( + getUtc = false, getUpdate = true, getSweep = true, getSensor = true, + setUtc = {}, setUpdate = {}, setSweep = {}, setSensor = {}) + ) } @Composable private fun OtherCard(settings: OtherSettings) { ElevatedCard(modifier = Modifier.fillMaxWidth()) { - Column(modifier = Modifier.padding(4.dp)) { - Text(text = stringResource(id = R.string.other_title)) + Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp)) { Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, @@ -309,7 +352,7 @@ private fun OtherCard(settings: OtherSettings) { ) { Text(text = stringResource(id = R.string.other_switch_utc)) Switch(checked = settings.getUtc, onCheckedChange = { settings.setUtc(it) }) - } // viewModel.setUseUTC(isChecked) + } Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, @@ -317,7 +360,7 @@ private fun OtherCard(settings: OtherSettings) { ) { Text(text = stringResource(id = R.string.other_switch_update)) Switch(checked = settings.getUpdate, onCheckedChange = { settings.setUpdate(it) }) - } // AutoUpdateEnabled(isChecked) + } Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, @@ -325,7 +368,7 @@ private fun OtherCard(settings: OtherSettings) { ) { Text(text = stringResource(id = R.string.other_switch_sweep)) Switch(checked = settings.getSweep, onCheckedChange = { settings.setSweep(it) }) - } // viewModel.setShowSweep(isChecked) + } Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, @@ -333,58 +376,49 @@ private fun OtherCard(settings: OtherSettings) { ) { Text(text = stringResource(id = R.string.other_switch_sensors)) Switch(checked = settings.getSensor, onCheckedChange = { settings.setSensor(it) }) - } // viewModel.setUseCompass(isChecked) + } } } } -// -// showToast(getString(R.string.data_clear_success)) -// showToast(getString(R.string.data_success)) -// -//private fun setUpdateTime(updateTime: Long) { -// val updatePattern = getString(R.string.data_update) -// val updateDate = if (updateTime == 0L) { -// getString(R.string.pass_placeholder) -// } else { -// SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault()).format(Date(updateTime)) -// } -// binding.settingsData.dataUpdate.text = String.format(updatePattern, updateDate) -//} -// +@Preview(showBackground = true) +@Composable +private fun CardCreditsPreview() = MainTheme { CardCredits() } + @Composable private fun CardCredits(modifier: Modifier = Modifier) { val context = LocalContext.current ElevatedCard(modifier = modifier.fillMaxWidth()) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp) + ) { Text( text = stringResource(id = R.string.outro_title), fontSize = 18.sp, fontWeight = FontWeight.Medium, color = MaterialTheme.colorScheme.secondary, - modifier = modifier.padding(8.dp) + modifier = modifier.padding(6.dp) ) Text( text = stringResource(id = R.string.outro_thanks), fontSize = 16.sp, - textAlign = TextAlign.Center, - modifier = modifier.padding(horizontal = 6.dp, vertical = 4.dp) + textAlign = TextAlign.Center ) Text( text = stringResource(id = R.string.outro_license), fontSize = 18.sp, fontWeight = FontWeight.Medium, color = MaterialTheme.colorScheme.secondary, - modifier = modifier.padding(8.dp) + modifier = modifier.padding(6.dp) ) - Row( - horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.padding(4.dp) - ) { + Row(horizontalArrangement = Arrangement.SpaceEvenly) { CardButton( onClick = { gotoUrl(context, LICENSE_URL) }, text = stringResource(id = R.string.btn_license), modifier = Modifier.weight(1f) ) + Spacer(modifier = Modifier.width(6.dp)) CardButton( onClick = { gotoUrl(context, POLICY_URL) }, text = stringResource(id = R.string.btn_privacy), @@ -396,8 +430,26 @@ private fun CardCredits(modifier: Modifier = Modifier) { } @Composable -private fun UpdateIndicator(isUpdating: Boolean, modifier: Modifier = Modifier) = if (isUpdating) { - LinearProgressIndicator(modifier = modifier.padding(start = 6.dp, end = 6.dp)) -} else { - LinearProgressIndicator(modifier = modifier.padding(start = 6.dp, end = 6.dp), progress = 0f) +private fun setUpdateTime(updateTime: Long): String { + val updateDate = if (updateTime != 0L) { + val timePattern = stringResource(id = R.string.last_updated_pattern) + SimpleDateFormat(timePattern, Locale.getDefault()).format(Date(updateTime)) + } else { + stringResource(id = R.string.pass_placeholder) + } + return stringResource(id = R.string.data_update, updateDate) +} + +@Composable +private fun UpdateIndicator(isUpdating: Boolean, modifier: Modifier = Modifier) = if (isUpdating) { + LinearProgressIndicator( + modifier = modifier.padding(start = 6.dp), + trackColor = MaterialTheme.colorScheme.inverseSurface + ) +} else { + LinearProgressIndicator( + progress = 0f, + modifier = modifier.padding(start = 6.dp), + trackColor = MaterialTheme.colorScheme.inverseSurface + ) } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreenState.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreenState.kt index c80ca3e1..5434d4e7 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreenState.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreenState.kt @@ -1,21 +1,20 @@ package com.rtbishop.look4sat.presentation.settings +import com.rtbishop.look4sat.model.StationPos + data class LocationSettings( - val getUpdating: Boolean, - val getLastUpdated: Long, - val getLatitude: Double, - val getLongitude: Double, - val getLocator: String, + val isUpdating: Boolean, + val stationPos: StationPos, val setGpsLoc: () -> Unit, val setManualLoc: (Double, Double) -> Unit, val setQthLoc: (String) -> Unit ) data class DataSettings( - val getUpdating: Boolean, - val getLastUpdated: Long, - val getSatellites: Int, - val getRadios: Int, + val isUpdating: Boolean, + val lastUpdated: Long, + val satsTotal: Int, + val radiosTotal: Int, val updateFromWeb: () -> Unit, val updateFromFile: (String) -> Unit, val clearAllData: () -> Unit diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt index e6c1875f..6c6d168c 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt @@ -25,6 +25,7 @@ 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.delay import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch import javax.inject.Inject @@ -37,11 +38,8 @@ class SettingsViewModel @Inject constructor( ) : ViewModel() { private val defaultLocationSettings = LocationSettings(false, - locationSource.stationPosition.value.timestamp, - locationSource.stationPosition.value.latitude, - locationSource.stationPosition.value.longitude, - locationSource.stationPosition.value.qthLocator, - { locationSource.setGpsPosition() }, + locationSource.stationPosition.value, + { updateStationPosition() }, { lat, lon -> locationSource.setGeoPosition(lat, lon) }, { locationSource.setQthPosition(it) }) val locationSettings = mutableStateOf(defaultLocationSettings) @@ -66,22 +64,34 @@ class SettingsViewModel @Inject constructor( val otherSettings = mutableStateOf(defaultOtherSettings) init { + viewModelScope.launch { + locationSource.stationPosition.collect { stationPos -> + delay(125) + locationSettings.value = locationSettings.value.copy( + isUpdating = false, stationPos = stationPos + ) + } + } viewModelScope.launch { repository.updateState.collect { val isUpdating = it is DataState.Loading dataSettings.value = dataSettings.value.copy( - getUpdating = isUpdating, getLastUpdated = settings.getLastUpdateTime() + isUpdating = isUpdating, lastUpdated = settings.getLastUpdateTime() ) } } viewModelScope.launch { combine(repository.getEntriesTotal(), repository.getRadiosTotal()) { sats, radios -> - dataSettings.value = - dataSettings.value.copy(getSatellites = sats, getRadios = radios) + dataSettings.value = dataSettings.value.copy(satsTotal = sats, radiosTotal = radios) }.collect {} } } + private fun updateStationPosition() { + val isSuccessful = locationSource.setGpsPosition() + locationSettings.value = locationSettings.value.copy(isUpdating = isSuccessful) + } + private fun setUtc(value: Boolean) { settings.setUtcState(value) otherSettings.value = otherSettings.value.copy(getUtc = value) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index d11510fe..7bfb23f2 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -158,24 +158,23 @@ Формат данных Нет разрешения использовать bluetooth - Другие настройки - Показывать время по UTC - Обновлять данные автоматически - Показывать анимацию радара - Использовать сенсоры устройства + • Показывать время по UTC + • Обновлять данные автоматически + • Показывать анимацию радара + • Использовать сенсоры устройства Я хотел бы сказать спасибо: - • Вам за использование этого приложения! \nЖелаю всегда чистого неба над головой! - \n • David A. B. Johnson и + • Вам за использование этого приложения! \nЖелаю всегда чистого неба над головой! + \n• David A. B. Johnson и Dave Moten за работу над predict4java и доступ к ней по GNU GPLv2 - \n • Alexandru Csete за его программу + \n• Alexandru Csete за его программу Gpredict, которая послужила для меня вдохновением. - \n • Доктору T.S. Kelso за его сайт + \n• Доктору T.S. Kelso за его сайт Celestrak и доступ к файлам орбит Two-Line Element. - \n • Libre Space Foundation за проект + \n• Libre Space Foundation за проект SatNOGS, API и базу данных с информацией о спутниках. - \n • DownloadAstro + \n• DownloadAstro за интерес к приложению и публикацию интервью на сайте. Это ПО поставляется без гарантий. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 9ab3f9b6..c870ada7 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -158,25 +158,24 @@ 数据格式 请检查蓝牙权限 - 其他偏好 - 以 UTC 时间显示 - 启用卫星数据自动更新 - 启用雷达扫描动画 - 使用传感器旋转雷达视图 + • 以 UTC 时间显示 + • 启用卫星数据自动更新 + • 启用雷达扫描动画 + • 使用传感器旋转雷达视图 我要感谢: • 感谢下载和使用 Look4Sat!愿你的天空永远晴朗! - \n • 简体中文版翻译由 BA7LWN 于 2022.5.10 创建。 - \n • David A. B. Johnson + \n• 简体中文版翻译由 BA7LWN 于 2022.5.10 创建。 + \n• David A. B. Johnson and Dave Moten for creating predict4java lib under the GNU GPLv2. - \n • Alexandru Csete for creating Gpredict + \n• Alexandru Csete for creating Gpredict satellite tracker that inspired the creation of Look4Sat. - \n • Dr T.S. Kelso for maintaining his + \n• Dr T.S. Kelso for maintaining his Celestrak website that provides access to the TLE data. - \n • Libre Space Foundation for their + \n• Libre Space Foundation for their SatNOGS API and DB providing a huge amount of satellite data. - \n • DownloadAstro + \n• DownloadAstro team for their interest to the app and the interview published. 该应用程序不提供任何保修. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eae6accc..c770b002 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -147,6 +147,8 @@ Update completed successfully Data was cleared successfully + d MMM yyyy - HH:mm:ss + Rotator preferences Enable remote control IP address @@ -158,24 +160,22 @@ Data format Check your bluetooth permission - Other preferences - Show pass time in UTC - Enable automatic data update - Enable radar sweep animation - Use sensors to rotate radar view + • Show pass time in UTC + • Enable automatic data update + • Enable radar sweep animation + • Use sensors to rotate radar view I would like to say thanks to: • You for downloading and using Look4Sat! \nMay your sky always be clear! - \n\n• David A. B. Johnson - and Dave Moten for creating predict4java lib - under the GNU GPLv2. - \n\n• Alexandru Csete for creating Gpredict - satellite tracker that inspired the creation of Look4Sat. - \n\n• Dr T.S. Kelso for maintaining his + \n• David A. B. Johnson + and Dave Moten for creating a super useful predict4java library. + \n• Alexandru Csete for creating Gpredict + tracker that inspired the creation of Look4Sat. + \n• Dr T.S. Kelso for maintaining his Celestrak website that provides access to the TLE data. - \n\n• Libre Space Foundation for their + \n• Libre Space Foundation for their SatNOGS API and DB providing a huge amount of data. - \n\n• DownloadAstro + \n• DownloadAstro team for their interest to the app and the interview published. The app comes with no warranty.