diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1c1a5f9e..b06f8efd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Setup Gradle uses: gradle/actions/setup-gradle@v6 @@ -58,7 +58,7 @@ jobs: - name: Setup Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.3' + ruby-version: '3.4' - name: Deploy Bundle to Google Play run: | diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e8a8cf54..cd9649c9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + + android:roundIcon="@mipmap/ic_launcher_round"> + = maxAckReadFailures) { + Log.e(tag, "Too many ACK read errors, marking radio disconnected") + isConnected = false + false + } else { + true // Command was sent, treat transient ACK read failures as best-effort + } + } } } - private fun readResponse(length: Int): ByteArray? { - return try { - val buffer = ByteArray(length) - var read = 0 - while (read < length) { - val count = inputStream?.read(buffer, read, length - read) ?: return null - if (count < 0) return null - read += count + private suspend fun readResponse(): ByteArray? { + return withContext(Dispatchers.IO) { + try { + val responseSize = 5 + val buffer = ByteArray(responseSize) + var read = 0 + while (read < responseSize) { + val count = inputStream?.read(buffer, read, responseSize - read) ?: run { + isConnected = false + return@withContext null + } + if (count < 0) { + Log.i(tag, "Response stream closed by remote device") + isConnected = false + return@withContext null + } + read += count + } + buffer + } catch (e: Exception) { + Log.e(tag, "Read error: ${e.message}") + isConnected = false + null } - buffer - } catch (e: Exception) { - Log.e(tag, "Read error: ${e.message}") - isConnected = false - null } } } diff --git a/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/utility/GeoConverter.kt b/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/utility/GeoConverter.kt index f41930e1..ecadb1f9 100644 --- a/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/utility/GeoConverter.kt +++ b/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/utility/GeoConverter.kt @@ -17,7 +17,9 @@ */ package com.rtbishop.look4sat.core.domain.utility +import com.rtbishop.look4sat.core.domain.predict.GeoPos import com.rtbishop.look4sat.core.domain.predict.DEG2RAD +import com.rtbishop.look4sat.core.domain.predict.OrbitalPos import com.rtbishop.look4sat.core.domain.predict.RAD2DEG import kotlin.math.acos import kotlin.math.atan2 @@ -85,6 +87,13 @@ fun clipLon(longitude: Double): Double { return clip(result, MIN_LONGITUDE, MAX_LONGITUDE) } +fun OrbitalPos.toMapGeoPos(): GeoPos { + return GeoPos( + latitude = clipLat(latitude.toDegrees()), + longitude = clipLon(longitude.toDegrees()) + ) +} + private fun clip(currentValue: Double, minValue: Double, maxValue: Double): Double { return min(max(currentValue, minValue), maxValue) } diff --git a/core/presentation/src/main/res/values-es/strings.xml b/core/presentation/src/main/res/values-es/strings.xml index 75d6c764..b2bb7db9 100644 --- a/core/presentation/src/main/res/values-es/strings.xml +++ b/core/presentation/src/main/res/values-es/strings.xml @@ -132,6 +132,7 @@ Id dispositivo Formato de datos Revisa los permisos de Bluetooth + Revisa los permisos de red Otros ajustes Mostrar tiempos de pase en UTC diff --git a/core/presentation/src/main/res/values-ru/strings.xml b/core/presentation/src/main/res/values-ru/strings.xml index 358fd036..bb107c73 100644 --- a/core/presentation/src/main/res/values-ru/strings.xml +++ b/core/presentation/src/main/res/values-ru/strings.xml @@ -131,6 +131,7 @@ Id устройства Формат Нет разрешения использовать bluetooth + Нет разрешения использовать сеть Другие настройки Показывать время по UTC diff --git a/core/presentation/src/main/res/values-si/strings.xml b/core/presentation/src/main/res/values-si/strings.xml index 6777d1b5..ad77e225 100644 --- a/core/presentation/src/main/res/values-si/strings.xml +++ b/core/presentation/src/main/res/values-si/strings.xml @@ -132,6 +132,7 @@ උපාංග id දත්ත අකාරය Bluetooth අවසර පරික්‍ෂා ක° + Network අවසර පරික්‍ෂා කරන්න වෙනත් සැකසුම් පසුකර වේලාවන් UTC මගින් diff --git a/core/presentation/src/main/res/values-tr/strings.xml b/core/presentation/src/main/res/values-tr/strings.xml index 231e5263..071009d6 100644 --- a/core/presentation/src/main/res/values-tr/strings.xml +++ b/core/presentation/src/main/res/values-tr/strings.xml @@ -175,6 +175,7 @@ Cihaz kimliği Veri biçimi Bluetooth izninizi kontrol edin + Ağ izninizi kontrol edin Diğer ayarlar Geçiş saatini UTC olarak göster diff --git a/core/presentation/src/main/res/values-uk/strings.xml b/core/presentation/src/main/res/values-uk/strings.xml index 85d94a03..8604a1b6 100644 --- a/core/presentation/src/main/res/values-uk/strings.xml +++ b/core/presentation/src/main/res/values-uk/strings.xml @@ -132,6 +132,7 @@ Id пристрою Формат даних Перевірте дозвіл Bluetooth + Перевірте дозвіл мережі Інші налаштування Показувати час в UTC diff --git a/core/presentation/src/main/res/values-zh/strings.xml b/core/presentation/src/main/res/values-zh/strings.xml index a16ac1ed..8f69176a 100644 --- a/core/presentation/src/main/res/values-zh/strings.xml +++ b/core/presentation/src/main/res/values-zh/strings.xml @@ -124,6 +124,7 @@ 蓝牙设备 ID 数据格式 请检查蓝牙权限 + 请检查网络权限 其他设置 以 UTC 时间显示 diff --git a/core/presentation/src/main/res/values/strings.xml b/core/presentation/src/main/res/values/strings.xml index b73c01cf..de886431 100644 --- a/core/presentation/src/main/res/values/strings.xml +++ b/core/presentation/src/main/res/values/strings.xml @@ -49,9 +49,8 @@ \n\nPlease update the database at least weekly to get accurate predictions. What\'s new in Look4Sat - * Fixed transceivers retention on a failed fetch from server - \n\n* Fixed SSTV frequency display, no transceiver selected hint - \n\n* Fixed All satellite category retention on Satellites screen + * Added required tweaks to support Android 17 (API 37) + \n\n* Added ACCESS_LOCAL_NETWORK permission check to support network data output @@ -174,6 +173,7 @@ Device id Data format Check your bluetooth permission + Check your network permission Other settings Show pass time in UTC diff --git a/fastlane/metadata/android/en-US/changelogs/default.txt b/fastlane/metadata/android/en-US/changelogs/default.txt index b0b03c0b..751fc60a 100644 --- a/fastlane/metadata/android/en-US/changelogs/default.txt +++ b/fastlane/metadata/android/en-US/changelogs/default.txt @@ -1,3 +1,2 @@ -* Fixed transceivers retention on a failed fetch from server -* Fixed SSTV frequency display, no transceiver selected hint -* Fixed All satellite category retention on Satellites screen +* Added required tweaks to support Android 17 (API 37) +* Added ACCESS_LOCAL_NETWORK permission check to support network data output diff --git a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt index ecaaf1c9..118a089d 100644 --- a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt +++ b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt @@ -32,6 +32,7 @@ import com.rtbishop.look4sat.core.domain.repository.ISettingsRepo import com.rtbishop.look4sat.core.domain.utility.clipLat import com.rtbishop.look4sat.core.domain.utility.clipLon import com.rtbishop.look4sat.core.domain.utility.positionToQth +import com.rtbishop.look4sat.core.domain.utility.toMapGeoPos import com.rtbishop.look4sat.core.domain.utility.toDegrees import com.rtbishop.look4sat.core.domain.utility.toTimerString import com.rtbishop.look4sat.core.presentation.getDefaultPass @@ -166,9 +167,7 @@ class MapViewModel( var localSelectedPos: OrbitalPos? = null for (satellite in chunk) { val satPos = satelliteRepo.getPosition(satellite, pos, date.time) - val osmLat = clipLat(satPos.latitude.toDegrees()) - val osmLon = clipLon(satPos.longitude.toDegrees()) - localPositions.add(satellite to GeoPos(osmLat, osmLon)) + localPositions.add(satellite to satPos.toMapGeoPos()) if (satellite === selected) { localSelectedPos = satPos } @@ -242,9 +241,7 @@ class MapViewModel( } val azimuth = satPos.azimuth.toDegrees() val elevation = satPos.elevation.toDegrees() - val osmLat = clipLat(satPos.latitude.toDegrees()) - val osmLon = clipLon(satPos.longitude.toDegrees()) - val osmPos = GeoPos(osmLat, osmLon) + val osmPos = satPos.toMapGeoPos() val qthLoc = positionToQth(osmPos.latitude, osmPos.longitude) ?: "-- --" val velocity = satPos.getOrbitalVelocity() val phase = satPos.phase.toDegrees() @@ -274,18 +271,16 @@ class MapViewModel( val endDate = Date(date.time + (orbitalObject.data.orbitalPeriod * 2.4 * 60000L).toLong()) var oldLongitude = 0.0 satelliteRepo.getTrack(orbitalObject, pos, date.time, endDate.time).forEach { satPos -> - val osmLat = clipLat(satPos.latitude.toDegrees()) - val osmLon = clipLon(satPos.longitude.toDegrees()) - val currentPosition = GeoPos(osmLat, osmLon) + val currentPosition = satPos.toMapGeoPos() if (oldLongitude < -170.0 && currentPosition.longitude > 170.0) { // adding left terminal position - currentTrack.add(GeoPos(osmLat, -180.0)) + currentTrack.add(GeoPos(currentPosition.latitude, -180.0)) val finishedTrack = mutableListOf().apply { addAll(currentTrack) } satTracks.add(finishedTrack) currentTrack.clear() } else if (oldLongitude > 170.0 && currentPosition.longitude < -170.0) { // adding right terminal position - currentTrack.add(GeoPos(osmLat, 180.0)) + currentTrack.add(GeoPos(currentPosition.latitude, 180.0)) val finishedTrack = mutableListOf().apply { addAll(currentTrack) } satTracks.add(finishedTrack) currentTrack.clear() diff --git a/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsScreen.kt b/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsScreen.kt index 38604330..4d96e05a 100644 --- a/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsScreen.kt +++ b/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsScreen.kt @@ -91,7 +91,8 @@ private fun SettingsScreen(uiState: SettingsState, onAction: (SettingsAction) -> val dialogs = rememberDialogVisibility() val permissions = rememberSettingsPermissions( sendAction = onAction, - onBluetoothGranted = { dialogs.bluetooth = true } + onBluetoothGranted = { dialogs.bluetooth = true }, + onNetworkGranted = { dialogs.network = true } ) // Dialogs @@ -273,7 +274,7 @@ private fun SettingsScreen(uiState: SettingsState, onAction: (SettingsAction) -> } item(span = { GridItemSpan(maxLineSpan) }) { OutputCard( - onNetworkClick = { dialogs.network = true }, + onNetworkClick = permissions.launchNetwork, onBluetoothClick = permissions.launchBluetooth, onRadioControlClick = { dialogs.radioControl = true } ) @@ -651,13 +652,15 @@ private class SettingsPermissions( val launchLocation: () -> Unit, val launchTleImport: () -> Unit, val launchTransceiverImport: () -> Unit, - val launchBluetooth: () -> Unit + val launchBluetooth: () -> Unit, + val launchNetwork: () -> Unit ) @Composable private fun rememberSettingsPermissions( sendAction: (SettingsAction) -> Unit, - onBluetoothGranted: () -> Unit + onBluetoothGranted: () -> Unit, + onNetworkGranted: () -> Unit ): SettingsPermissions { val locationError = stringResource(R.string.prefs_loc_gps_error) val locationRequest = rememberLauncherForActivityResult( @@ -685,6 +688,12 @@ private fun rememberSettingsPermissions( else sendAction(SettingsAction.ShowToast(bluetoothError)) } + val networkError = stringResource(R.string.prefs_net_perm_error) + val networkRequest = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { granted -> + if (granted) onNetworkGranted() + else sendAction(SettingsAction.ShowToast(networkError)) + } + return remember { SettingsPermissions( launchLocation = { @@ -694,7 +703,14 @@ private fun rememberSettingsPermissions( }, launchTleImport = { tleRequest.launch("*/*") }, launchTransceiverImport = { transceiversRequest.launch("*/*") }, - launchBluetooth = { bluetoothRequest.launch(bluetoothPerm) } + launchBluetooth = { bluetoothRequest.launch(bluetoothPerm) }, + launchNetwork = { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CINNAMON_BUN) { + networkRequest.launch(Manifest.permission.ACCESS_LOCAL_NETWORK) + } else { + onNetworkGranted() + } + } ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5e8de41c..b49a71a7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,26 +1,26 @@ [versions] #noinspection UnusedVersionCatalogEntry -appVersionCode = "442" +appVersionCode = "443" #noinspection UnusedVersionCatalogEntry -appVersionName = "4.4.2" -#noinspection GradleDependency,UnusedVersionCatalogEntry -compileSdk = "36" +appVersionName = "4.4.3" +#noinspection UnusedVersionCatalogEntry +compileSdk = "37" #noinspection UnusedVersionCatalogEntry minSdk = "24" #noinspection UnusedVersionCatalogEntry -jdkVersion = "17" +jdkVersion = "21" #noinspection UnusedVersionCatalogEntry packageName = "com.rtbishop.look4sat" android-gradle-plugin = "9.2.1" -androidx-core-ktx = "1.18.0" +androidx-core-ktx = "1.19.0" androidx-core-splashscreen = "1.2.0" androidx-room = "2.8.4" compose-bom = "2026.06.00" compose-activity = "1.13.0" -compose-lifecycle = "2.10.0" +compose-lifecycle = "2.11.0" compose-navigation3 = "1.1.3" google-ksp = "2.3.8" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 681b4f8b..a8002a4a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ -#Sun Jun 21 14:57:04 BST 2026 +#Sat Jun 27 12:51:12 BST 2026 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME