diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ba7daedd..5ed56135 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,11 +14,10 @@ android { compileSdk = 35 defaultConfig { applicationId = "com.rtbishop.look4sat" - minSdk = 26 + minSdk = 24 targetSdk = 35 - versionCode = 314 - versionName = "3.1.4" - resourceConfigurations.addAll(listOf("en", "ru", "si", "zh-rCN", "anydpi")) + versionCode = 400 + versionName = "4.0.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildFeatures { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index c5b80038..e69de29b 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,14 +0,0 @@ -# JSR 305 annotations are for embedding nullability information. --dontwarn javax.annotation.** - -# A resource is loaded with a relative path so the package of this class must be preserved. --adaptresourcefilenames okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz - -# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. --dontwarn org.codehaus.mojo.animal_sniffer.* - -# OkHttp platform used only on JVM and when Conscrypt and other security providers are available. --dontwarn okhttp3.internal.platform.** --dontwarn org.conscrypt.** --dontwarn org.bouncycastle.** --dontwarn org.openjsse.** 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 c24105b5..5bf39a5a 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 @@ -10,18 +10,26 @@ import android.graphics.Rect import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.State import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView import androidx.core.content.ContextCompat import androidx.lifecycle.Lifecycle @@ -86,6 +94,7 @@ private fun MapScreen(uiState: State) { val rotateMod = Modifier.rotate(180f) val timeString = uiState.value.mapData?.aosTime ?: "00:00:00" val isTimeAos = uiState.value.mapData?.isTimeAos ?: true + val osmInfo = "Wikimedia maps | Map data © OpenStreetMap contributors" Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { TimerRow { CardIcon(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod) @@ -93,14 +102,48 @@ private fun MapScreen(uiState: State) { CardIcon(onClick = selectNext, iconId = R.drawable.ic_arrow) } NextPassRow(pass = uiState.value.orbitalPass) - ElevatedCard(modifier = Modifier.fillMaxSize()) { - MapView(modifier = Modifier.fillMaxSize(), isLightUi = uiState.value.isLightUi) { mapView -> - uiState.value.stationPosition?.let { setStationPosition(it, mapView) } - uiState.value.positions?.let { setPositions(it, mapView, onItemClick) } - uiState.value.track?.let { setSatelliteTrack(it, mapView) } - uiState.value.footprint?.let { setFootprint(it, mapView) } + ElevatedCard(modifier = Modifier.weight(1f)) { + Box(contentAlignment = Alignment.BottomCenter) { + MapView(isLightUi = uiState.value.isLightUi) { mapView -> + uiState.value.stationPosition?.let { setStationPosition(it, mapView) } + uiState.value.positions?.let { setPositions(it, mapView, onItemClick) } + uiState.value.track?.let { setSatelliteTrack(it, mapView) } + uiState.value.footprint?.let { setFootprint(it, mapView) } + } + Text(text = osmInfo, textAlign = TextAlign.Center, fontSize = 14.sp) } } + uiState.value.mapData?.let { mapData -> + ElevatedCard { + MapDataCard(mapData) + } + } + } +} + +@Composable +private fun MapDataCard(mapData: MapData) { + Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) { + Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { + Text(text = stringResource(R.string.map_period, mapData.period), color = MaterialTheme.colorScheme.primary) + Text(text = stringResource(R.string.map_phase, mapData.phase), color = MaterialTheme.colorScheme.primary) + } + Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { + Text(text = stringResource(R.string.map_azimuth, mapData.azimuth)) + Text(text = stringResource(R.string.map_elevation, mapData.elevation)) + } + Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { + Text(text = stringResource(R.string.map_altitude, mapData.altitude), color = MaterialTheme.colorScheme.primary) + Text(text = stringResource(R.string.map_distance, mapData.range), color = MaterialTheme.colorScheme.primary) + } + Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { + Text(text = stringResource(R.string.map_latitude, mapData.osmPos.latitude)) + Text(text = stringResource(R.string.map_longitude, mapData.osmPos.longitude)) + } + Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { + Text(text = stringResource(R.string.map_qth, mapData.qthLoc), color = MaterialTheme.colorScheme.primary) + Text(text = stringResource(R.string.map_velocity, mapData.velocity), color = MaterialTheme.colorScheme.primary) + } } } @@ -195,30 +238,6 @@ private fun setFootprint(orbitalPos: OrbitalPos, mapView: MapView) { } } -//private fun handleMapData(mapData: MapData) { -// binding.apply { -// mapTimer.text = mapData.aosTime -// mapDataPeriod.text = String.format(getString(R.string.map_period, mapData.period)) -// mapDataPhase.text = String.format(getString(R.string.map_phase), mapData.phase) -// mapAzimuth.text = String.format(getString(R.string.map_azimuth), mapData.azimuth) -// mapElevation.text = String.format(getString(R.string.map_elevation), mapData.elevation) -// mapDataAlt.text = String.format(getString(R.string.map_altitude), mapData.altitude) -// mapDataDst.text = String.format(getString(R.string.map_distance), mapData.range) -// mapDataLat.text = -// String.format(getString(R.string.map_latitude), mapData.osmPos.lat) -// mapDataLon.text = -// String.format(getString(R.string.map_longitude), mapData.osmPos.lon) -// mapDataQth.text = String.format(getString(R.string.map_qth), mapData.qthLoc) -// mapDataVel.text = String.format(getString(R.string.map_velocity), mapData.velocity) -// if (mapData.eclipsed) { -// mapDataVisibility.text = getString(R.string.map_eclipsed) -// } else { -// mapDataVisibility.text = getString(R.string.map_visible) -// } -// } -// binding.mapView.invalidate() -//} - @Composable private fun MapView( modifier: Modifier = Modifier, @@ -237,9 +256,10 @@ private fun rememberMapViewWithLifecycle(isLightTheme: Boolean): MapView { setMultiTouchControls(true) setUseDataConnection(false) setTileSource(tileSource) - minZoomLevel = getMinZoom(resources.displayMetrics.heightPixels) + 0.25 - maxZoomLevel = 8.0 - controller.setZoom(minZoomLevel + 1) + minZoomLevel = getMinZoom(resources.displayMetrics.heightPixels) + maxZoomLevel = 7.0 + controller.setCenter(GeoPoint(48.8575, 6.3514)) + controller.setZoom(minZoomLevel + 2) zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER) overlayManager.tilesOverlay.loadingBackgroundColor = Color.TRANSPARENT overlayManager.tilesOverlay.loadingLineColor = Color.TRANSPARENT 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 c0d1a4c8..21f6703b 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 @@ -1,8 +1,15 @@ package com.rtbishop.look4sat.presentation.radar +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween import androidx.compose.foundation.MarqueeSpacing import androidx.compose.foundation.background import androidx.compose.foundation.basicMarquee +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -23,6 +30,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource @@ -80,7 +88,7 @@ private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) { CardIcon(onClick = addToCalendar, iconId = R.drawable.ic_calendar) } NextPassRow(pass = uiState.currentPass ?: getDefaultPass()) - Box(contentAlignment = Alignment.Center, modifier = Modifier.aspectRatio(1f)) { + Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.aspectRatio(1f)) { uiState.orbitalPos?.let { position -> ElevatedCard { RadarViewCompose( @@ -127,6 +135,30 @@ private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) { ) } } + if (uiState.orbitalPos.eclipsed) { + val infiniteTransition = rememberInfiniteTransition() + val textAlpha = infiniteTransition.animateFloat( + initialValue = 1.0f, + targetValue = 0.0f, + animationSpec = infiniteRepeatable( + animation = tween( + durationMillis = 500, + delayMillis = 25, + easing = LinearEasing + ), + repeatMode = RepeatMode.Reverse + )) + Text( + text = "Eclipsed!", + fontSize = 18.sp, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier + .alpha(textAlpha.value) + .border(2.dp, MaterialTheme.colorScheme.primary, MaterialTheme.shapes.medium) + .padding(16.dp) + ) + } } } ElevatedCard(modifier = Modifier.fillMaxSize()) { diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarView.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarView.kt index cfdd72ee..9d0a97fb 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarView.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarView.kt @@ -53,7 +53,7 @@ fun RadarViewCompose( val radarColor = MaterialTheme.colorScheme.secondary val trackColor = MaterialTheme.colorScheme.primary val aimColor = MaterialTheme.colorScheme.error - val strokeWidth = 4f + val strokeWidth = 6f val animTransition = rememberInfiniteTransition(label = "animScale") val animSpec = infiniteRepeatable(tween(1000)) val animScale = animTransition.animateFloat(16f, 64f, animSpec, label = "animScale") @@ -167,7 +167,7 @@ private fun DrawScope.drawInfo(radius: Float, color: Color, measurer: TextMeasur } private fun DrawScope.drawTrack(path: Path, effect: PathEffect, color: Color, effectColor: Color) { - drawPath(path, color, style = Stroke(4f)) + drawPath(path, color, style = Stroke(6f)) drawPath(path, effectColor, style = Stroke(pathEffect = effect)) } @@ -213,7 +213,7 @@ private fun createTrackPath(positions: List, radius: Float): Path { private fun createTrackEffect(trackPath: Path): PathEffect { val shape = Path() - val shapeRadius = 16f + val shapeRadius = 24f val angle = 120.0.toRadians() shape.moveTo((shapeRadius * cos(angle)).toFloat(), (shapeRadius * sin(angle)).toFloat()) for (i in 1 until 3) { diff --git a/app/src/main/res/mipmap/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from app/src/main/res/mipmap/ic_launcher.xml rename to app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/app/src/main/res/mipmap/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from app/src/main/res/mipmap/ic_launcher_round.xml rename to app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/app/src/main/res/mipmap-nodpi/ic_launcher.webp b/app/src/main/res/mipmap-nodpi/ic_launcher.webp new file mode 100644 index 00000000..24aea602 Binary files /dev/null and b/app/src/main/res/mipmap-nodpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-nodpi/ic_launcher_round.webp b/app/src/main/res/mipmap-nodpi/ic_launcher_round.webp new file mode 100644 index 00000000..f69a6825 Binary files /dev/null and b/app/src/main/res/mipmap-nodpi/ic_launcher_round.webp differ diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 63d12dcf..aed6f1f3 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -12,7 +12,7 @@ android { namespace = "com.rtbishop.look4sat.data" compileSdk = 35 defaultConfig { - minSdk = 26 + minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4ece4007..bbf55fb8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -android-gradle-plugin = "8.7.3" +android-gradle-plugin = "8.8.0" google-ksp = "2.0.21-1.0.26" kotlin = "2.0.21" @@ -7,18 +7,18 @@ androidx-core-ktx = "1.15.0" androidx-core-splashscreen = "1.0.1" androidx-room = "2.6.1" -compose-bom = "2024.11.00" -compose-activity = "1.9.3" +compose-bom = "2025.01.00" +compose-activity = "1.10.0" compose-lifecycle = "2.8.7" -compose-navigation = "2.8.4" +compose-navigation = "2.8.5" -other-coroutines = "1.9.0" +other-coroutines = "1.10.1" other-json = "20240303" other-okhttp = "4.12.0" other-osmdroid = "6.1.20" test-junit4 = "4.13.2" -test-mockk = "1.13.13" +test-mockk = "1.13.14" androidTest-junit = "1.2.1" androidTest-espresso = "3.6.1" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d8aae7cb..a6f1f963 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Oct 25 23:59:14 BST 2024 +#Sat Jan 04 21:11:19 GMT 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists