v4.4.3 - Added required tweaks to support Android 17 (API 37)

This commit is contained in:
Arty Bishop committed 2026-06-27 14:15:05 +01:00
1 parent 976fdfd949
commit b17ea2d918
18 files changed
+120 -54

No files matched your search

+2 -2
View File
@@ -22,7 +22,7 @@ jobs:
uses: actions/setup-java@v5 uses: actions/setup-java@v5
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: '17' java-version: '21'
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v6 uses: gradle/actions/setup-gradle@v6
@@ -58,7 +58,7 @@ jobs:
- name: Setup Ruby - name: Setup Ruby
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
ruby-version: '3.3' ruby-version: '3.4'
- name: Deploy Bundle to Google Play - name: Deploy Bundle to Google Play
run: | run: |
+2 -2
View File
@@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_LOCAL_NETWORK" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission <uses-permission
@@ -18,8 +19,7 @@
android:allowBackup="false" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round">
android:usesCleartextTraffic="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
+1
View File
@@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_LOCAL_NETWORK" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission <uses-permission
@@ -17,6 +17,9 @@
*/ */
package com.rtbishop.look4sat.core.data.framework package com.rtbishop.look4sat.core.data.framework
import java.util.Locale
import kotlin.math.roundToLong
object Ft817CatProtocol { object Ft817CatProtocol {
const val CMD_SET_FREQ: Byte = 0x01 const val CMD_SET_FREQ: Byte = 0x01
@@ -50,7 +53,7 @@ object Ft817CatProtocol {
fun encodeFrequencyBcd(frequencyHz: Long): ByteArray { fun encodeFrequencyBcd(frequencyHz: Long): ByteArray {
val freq10Hz = frequencyHz / 10 val freq10Hz = frequencyHz / 10
val bcd = ByteArray(4) val bcd = ByteArray(4)
val digits = String.format("%08d", freq10Hz) val digits = String.format(Locale.US, "%08d", freq10Hz)
for (i in 0 until 4) { for (i in 0 until 4) {
val high = digits[i * 2] - '0' val high = digits[i * 2] - '0'
val low = digits[i * 2 + 1] - '0' val low = digits[i * 2 + 1] - '0'
@@ -78,8 +81,8 @@ object Ft817CatProtocol {
* 67.0 Hz → 670 (in 0.1 Hz) → BCD [0x06, 0x70] * 67.0 Hz → 670 (in 0.1 Hz) → BCD [0x06, 0x70]
*/ */
fun encodeCtcssToneBcd(toneHz: Double): ByteArray { fun encodeCtcssToneBcd(toneHz: Double): ByteArray {
val tone01Hz = (toneHz * 10).toLong() val tone01Hz = (toneHz * 10).roundToLong()
val digits = String.format("%04d", tone01Hz) val digits = String.format(Locale.US, "%04d", tone01Hz)
val bcd = ByteArray(2) val bcd = ByteArray(2)
for (i in 0 until 2) { for (i in 0 until 2) {
val high = digits[i * 2] - '0' val high = digits[i * 2] - '0'
@@ -95,7 +98,7 @@ object Ft817CatProtocol {
} }
fun buildSetModeCommand(mode: String): ByteArray? { fun buildSetModeCommand(mode: String): ByteArray? {
val modeByte = MODE_TO_BYTE[mode.uppercase()] ?: return null val modeByte = MODE_TO_BYTE[mode.uppercase(Locale.US)] ?: return null
return byteArrayOf(modeByte, 0x00, 0x00, 0x00, CMD_SET_MODE) return byteArrayOf(modeByte, 0x00, 0x00, 0x00, CMD_SET_MODE)
} }
@@ -29,6 +29,7 @@ import kotlinx.coroutines.withContext
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.util.UUID import java.util.UUID
import kotlin.time.Duration.Companion.milliseconds
class Ft817Controller( class Ft817Controller(
private val bluetoothManager: BluetoothManager, private val bluetoothManager: BluetoothManager,
@@ -39,10 +40,12 @@ class Ft817Controller(
private val sppId: UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb") private val sppId: UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb")
private val ioMutex = Mutex() private val ioMutex = Mutex()
private val commandDelayMs = 200L private val commandDelayMs = 200L
private val maxAckReadFailures = 3
private var socket: BluetoothSocket? = null private var socket: BluetoothSocket? = null
private var outputStream: OutputStream? = null private var outputStream: OutputStream? = null
private var inputStream: InputStream? = null private var inputStream: InputStream? = null
private var ackReadFailureCount = 0
override var isConnected: Boolean = false override var isConnected: Boolean = false
private set private set
@@ -57,6 +60,7 @@ class Ft817Controller(
socket = btSocket socket = btSocket
outputStream = btSocket.outputStream outputStream = btSocket.outputStream
inputStream = btSocket.inputStream inputStream = btSocket.inputStream
ackReadFailureCount = 0
isConnected = true isConnected = true
Log.i(tag, "Connected to $deviceAddress") Log.i(tag, "Connected to $deviceAddress")
true true
@@ -79,6 +83,7 @@ class Ft817Controller(
inputStream = null inputStream = null
outputStream = null outputStream = null
socket = null socket = null
ackReadFailureCount = 0
isConnected = false isConnected = false
Log.i(tag, "Disconnected from $deviceAddress") Log.i(tag, "Disconnected from $deviceAddress")
} }
@@ -112,8 +117,8 @@ class Ft817Controller(
ioMutex.withLock { ioMutex.withLock {
val sent = sendCommand(Ft817CatProtocol.buildReadFreqModeCommand()) val sent = sendCommand(Ft817CatProtocol.buildReadFreqModeCommand())
if (!sent) return@withContext null if (!sent) return@withContext null
delay(commandDelayMs) delay(commandDelayMs.milliseconds)
val response = readResponse(5) ?: return@withContext null val response = readResponse() ?: return@withContext null
Ft817CatProtocol.parseReadResponse(response) Ft817CatProtocol.parseReadResponse(response)
} }
} }
@@ -127,10 +132,11 @@ class Ft817Controller(
} }
private suspend fun sendCommand(bytes: ByteArray): Boolean { private suspend fun sendCommand(bytes: ByteArray): Boolean {
return try { return withContext(Dispatchers.IO) {
outputStream?.write(bytes) ?: return false try {
outputStream?.write(bytes) ?: return@withContext false
outputStream?.flush() outputStream?.flush()
delay(commandDelayMs) delay(commandDelayMs.milliseconds)
true true
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, "Send error: ${e.message}") Log.e(tag, "Send error: ${e.message}")
@@ -138,26 +144,56 @@ class Ft817Controller(
false false
} }
} }
}
/** Send command and read the 1-byte ACK response (0x00 = OK). */ /** Send command and read the 1-byte ACK response (0x00 = OK). */
private suspend fun sendCommandWithAck(bytes: ByteArray): Boolean { private suspend fun sendCommandWithAck(bytes: ByteArray): Boolean {
if (!sendCommand(bytes)) return false if (!sendCommand(bytes)) return false
return try { return withContext(Dispatchers.IO) {
val ack = inputStream?.read() ?: return false try {
val ack = inputStream?.read() ?: run {
ackReadFailureCount = 0
isConnected = false
return@withContext false
}
if (ack < 0) {
ackReadFailureCount = 0
Log.i(tag, "ACK stream closed by remote device")
isConnected = false
return@withContext false
}
ackReadFailureCount = 0
ack == 0x00 ack == 0x00
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, "ACK read error: ${e.message}") ackReadFailureCount += 1
true // command was sent, ACK read failed - continue anyway Log.w(tag, "ACK read error (${ackReadFailureCount}/$maxAckReadFailures): ${e.message}")
if (ackReadFailureCount >= 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? { private suspend fun readResponse(): ByteArray? {
return try { return withContext(Dispatchers.IO) {
val buffer = ByteArray(length) try {
val responseSize = 5
val buffer = ByteArray(responseSize)
var read = 0 var read = 0
while (read < length) { while (read < responseSize) {
val count = inputStream?.read(buffer, read, length - read) ?: return null val count = inputStream?.read(buffer, read, responseSize - read) ?: run {
if (count < 0) return null isConnected = false
return@withContext null
}
if (count < 0) {
Log.i(tag, "Response stream closed by remote device")
isConnected = false
return@withContext null
}
read += count read += count
} }
buffer buffer
@@ -168,3 +204,4 @@ class Ft817Controller(
} }
} }
} }
}
@@ -17,7 +17,9 @@
*/ */
package com.rtbishop.look4sat.core.domain.utility 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.DEG2RAD
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
import com.rtbishop.look4sat.core.domain.predict.RAD2DEG import com.rtbishop.look4sat.core.domain.predict.RAD2DEG
import kotlin.math.acos import kotlin.math.acos
import kotlin.math.atan2 import kotlin.math.atan2
@@ -85,6 +87,13 @@ fun clipLon(longitude: Double): Double {
return clip(result, MIN_LONGITUDE, MAX_LONGITUDE) 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 { private fun clip(currentValue: Double, minValue: Double, maxValue: Double): Double {
return min(max(currentValue, minValue), maxValue) return min(max(currentValue, minValue), maxValue)
} }
@@ -132,6 +132,7 @@
<string name="prefs_bt_frequency_device_hint">Id dispositivo</string> <string name="prefs_bt_frequency_device_hint">Id dispositivo</string>
<string name="prefs_bt_frequency_output_hint">Formato de datos</string> <string name="prefs_bt_frequency_output_hint">Formato de datos</string>
<string name="prefs_bt_perm_error">Revisa los permisos de Bluetooth</string> <string name="prefs_bt_perm_error">Revisa los permisos de Bluetooth</string>
<string name="prefs_net_perm_error">Revisa los permisos de red</string>
<string name="prefs_other_title">Otros ajustes</string> <string name="prefs_other_title">Otros ajustes</string>
<string name="prefs_other_switch_utc">Mostrar tiempos de pase en UTC</string> <string name="prefs_other_switch_utc">Mostrar tiempos de pase en UTC</string>
@@ -131,6 +131,7 @@
<string name="prefs_bt_frequency_device_hint">Id устройства</string> <string name="prefs_bt_frequency_device_hint">Id устройства</string>
<string name="prefs_bt_frequency_output_hint">Формат</string> <string name="prefs_bt_frequency_output_hint">Формат</string>
<string name="prefs_bt_perm_error">Нет разрешения использовать bluetooth</string> <string name="prefs_bt_perm_error">Нет разрешения использовать bluetooth</string>
<string name="prefs_net_perm_error">Нет разрешения использовать сеть</string>
<string name="prefs_other_title">Другие настройки</string> <string name="prefs_other_title">Другие настройки</string>
<string name="prefs_other_switch_utc">Показывать время по UTC</string> <string name="prefs_other_switch_utc">Показывать время по UTC</string>
@@ -132,6 +132,7 @@
<string name="prefs_bt_frequency_device_hint">උපාංග id</string> <string name="prefs_bt_frequency_device_hint">උපාංග id</string>
<string name="prefs_bt_frequency_output_hint">දත්ත අකාරය</string> <string name="prefs_bt_frequency_output_hint">දත්ත අකාරය</string>
<string name="prefs_bt_perm_error">Bluetooth අවසර පරික්‍ෂා ක°</string> <string name="prefs_bt_perm_error">Bluetooth අවසර පරික්‍ෂා ක°</string>
<string name="prefs_net_perm_error">Network අවසර පරික්‍ෂා කරන්න</string>
<string name="prefs_other_title">වෙනත් සැකසුම්</string> <string name="prefs_other_title">වෙනත් සැකසුම්</string>
<string name="prefs_other_switch_utc">පසුකර වේලාවන් UTC මගින්</string> <string name="prefs_other_switch_utc">පසුකර වේලාවන් UTC මගින්</string>
@@ -175,6 +175,7 @@
<string name="prefs_bt_frequency_device_hint">Cihaz kimliği</string> <string name="prefs_bt_frequency_device_hint">Cihaz kimliği</string>
<string name="prefs_bt_frequency_output_hint">Veri biçimi</string> <string name="prefs_bt_frequency_output_hint">Veri biçimi</string>
<string name="prefs_bt_perm_error">Bluetooth izninizi kontrol edin</string> <string name="prefs_bt_perm_error">Bluetooth izninizi kontrol edin</string>
<string name="prefs_net_perm_error">Ağ izninizi kontrol edin</string>
<string name="prefs_other_title">Diğer ayarlar</string> <string name="prefs_other_title">Diğer ayarlar</string>
<string name="prefs_other_switch_utc">Geçiş saatini UTC olarak göster</string> <string name="prefs_other_switch_utc">Geçiş saatini UTC olarak göster</string>
@@ -132,6 +132,7 @@
<string name="prefs_bt_frequency_device_hint">Id пристрою</string> <string name="prefs_bt_frequency_device_hint">Id пристрою</string>
<string name="prefs_bt_frequency_output_hint">Формат даних</string> <string name="prefs_bt_frequency_output_hint">Формат даних</string>
<string name="prefs_bt_perm_error">Перевірте дозвіл Bluetooth</string> <string name="prefs_bt_perm_error">Перевірте дозвіл Bluetooth</string>
<string name="prefs_net_perm_error">Перевірте дозвіл мережі</string>
<string name="prefs_other_title">Інші налаштування</string> <string name="prefs_other_title">Інші налаштування</string>
<string name="prefs_other_switch_utc">Показувати час в UTC</string> <string name="prefs_other_switch_utc">Показувати час в UTC</string>
@@ -124,6 +124,7 @@
<string name="prefs_bt_frequency_device_hint">蓝牙设备 ID</string> <string name="prefs_bt_frequency_device_hint">蓝牙设备 ID</string>
<string name="prefs_bt_frequency_output_hint">数据格式</string> <string name="prefs_bt_frequency_output_hint">数据格式</string>
<string name="prefs_bt_perm_error">请检查蓝牙权限</string> <string name="prefs_bt_perm_error">请检查蓝牙权限</string>
<string name="prefs_net_perm_error">请检查网络权限</string>
<string name="prefs_other_title">其他设置</string> <string name="prefs_other_title">其他设置</string>
<string name="prefs_other_switch_utc">以 UTC 时间显示</string> <string name="prefs_other_switch_utc">以 UTC 时间显示</string>
@@ -49,9 +49,8 @@
\n\nPlease update the database at least weekly to get accurate predictions.</string> \n\nPlease update the database at least weekly to get accurate predictions.</string>
<string name="pass_whatsnew_title" translatable="false">What\'s new in Look4Sat</string> <string name="pass_whatsnew_title" translatable="false">What\'s new in Look4Sat</string>
<string name="pass_whatsnew_message" translatable="false"> <string name="pass_whatsnew_message" translatable="false">
* Fixed transceivers retention on a failed fetch from server * Added required tweaks to support Android 17 (API 37)
\n\n* Fixed SSTV frequency display, no transceiver selected hint \n\n* Added ACCESS_LOCAL_NETWORK permission check to support network data output
\n\n* Fixed All satellite category retention on Satellites screen
</string> </string>
<!-- Radar screen --> <!-- Radar screen -->
@@ -174,6 +173,7 @@
<string name="prefs_bt_frequency_device_hint">Device id</string> <string name="prefs_bt_frequency_device_hint">Device id</string>
<string name="prefs_bt_frequency_output_hint">Data format</string> <string name="prefs_bt_frequency_output_hint">Data format</string>
<string name="prefs_bt_perm_error">Check your bluetooth permission</string> <string name="prefs_bt_perm_error">Check your bluetooth permission</string>
<string name="prefs_net_perm_error">Check your network permission</string>
<string name="prefs_other_title">Other settings</string> <string name="prefs_other_title">Other settings</string>
<string name="prefs_other_switch_utc">Show pass time in UTC</string> <string name="prefs_other_switch_utc">Show pass time in UTC</string>
@@ -1,3 +1,2 @@
* Fixed transceivers retention on a failed fetch from server * Added required tweaks to support Android 17 (API 37)
* Fixed SSTV frequency display, no transceiver selected hint * Added ACCESS_LOCAL_NETWORK permission check to support network data output
* Fixed All satellite category retention on Satellites screen
@@ -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.clipLat
import com.rtbishop.look4sat.core.domain.utility.clipLon import com.rtbishop.look4sat.core.domain.utility.clipLon
import com.rtbishop.look4sat.core.domain.utility.positionToQth 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.toDegrees
import com.rtbishop.look4sat.core.domain.utility.toTimerString import com.rtbishop.look4sat.core.domain.utility.toTimerString
import com.rtbishop.look4sat.core.presentation.getDefaultPass import com.rtbishop.look4sat.core.presentation.getDefaultPass
@@ -166,9 +167,7 @@ class MapViewModel(
var localSelectedPos: OrbitalPos? = null var localSelectedPos: OrbitalPos? = null
for (satellite in chunk) { for (satellite in chunk) {
val satPos = satelliteRepo.getPosition(satellite, pos, date.time) val satPos = satelliteRepo.getPosition(satellite, pos, date.time)
val osmLat = clipLat(satPos.latitude.toDegrees()) localPositions.add(satellite to satPos.toMapGeoPos())
val osmLon = clipLon(satPos.longitude.toDegrees())
localPositions.add(satellite to GeoPos(osmLat, osmLon))
if (satellite === selected) { if (satellite === selected) {
localSelectedPos = satPos localSelectedPos = satPos
} }
@@ -242,9 +241,7 @@ class MapViewModel(
} }
val azimuth = satPos.azimuth.toDegrees() val azimuth = satPos.azimuth.toDegrees()
val elevation = satPos.elevation.toDegrees() val elevation = satPos.elevation.toDegrees()
val osmLat = clipLat(satPos.latitude.toDegrees()) val osmPos = satPos.toMapGeoPos()
val osmLon = clipLon(satPos.longitude.toDegrees())
val osmPos = GeoPos(osmLat, osmLon)
val qthLoc = positionToQth(osmPos.latitude, osmPos.longitude) ?: "-- --" val qthLoc = positionToQth(osmPos.latitude, osmPos.longitude) ?: "-- --"
val velocity = satPos.getOrbitalVelocity() val velocity = satPos.getOrbitalVelocity()
val phase = satPos.phase.toDegrees() val phase = satPos.phase.toDegrees()
@@ -274,18 +271,16 @@ class MapViewModel(
val endDate = Date(date.time + (orbitalObject.data.orbitalPeriod * 2.4 * 60000L).toLong()) val endDate = Date(date.time + (orbitalObject.data.orbitalPeriod * 2.4 * 60000L).toLong())
var oldLongitude = 0.0 var oldLongitude = 0.0
satelliteRepo.getTrack(orbitalObject, pos, date.time, endDate.time).forEach { satPos -> satelliteRepo.getTrack(orbitalObject, pos, date.time, endDate.time).forEach { satPos ->
val osmLat = clipLat(satPos.latitude.toDegrees()) val currentPosition = satPos.toMapGeoPos()
val osmLon = clipLon(satPos.longitude.toDegrees())
val currentPosition = GeoPos(osmLat, osmLon)
if (oldLongitude < -170.0 && currentPosition.longitude > 170.0) { if (oldLongitude < -170.0 && currentPosition.longitude > 170.0) {
// adding left terminal position // adding left terminal position
currentTrack.add(GeoPos(osmLat, -180.0)) currentTrack.add(GeoPos(currentPosition.latitude, -180.0))
val finishedTrack = mutableListOf<GeoPos>().apply { addAll(currentTrack) } val finishedTrack = mutableListOf<GeoPos>().apply { addAll(currentTrack) }
satTracks.add(finishedTrack) satTracks.add(finishedTrack)
currentTrack.clear() currentTrack.clear()
} else if (oldLongitude > 170.0 && currentPosition.longitude < -170.0) { } else if (oldLongitude > 170.0 && currentPosition.longitude < -170.0) {
// adding right terminal position // adding right terminal position
currentTrack.add(GeoPos(osmLat, 180.0)) currentTrack.add(GeoPos(currentPosition.latitude, 180.0))
val finishedTrack = mutableListOf<GeoPos>().apply { addAll(currentTrack) } val finishedTrack = mutableListOf<GeoPos>().apply { addAll(currentTrack) }
satTracks.add(finishedTrack) satTracks.add(finishedTrack)
currentTrack.clear() currentTrack.clear()
@@ -91,7 +91,8 @@ private fun SettingsScreen(uiState: SettingsState, onAction: (SettingsAction) ->
val dialogs = rememberDialogVisibility() val dialogs = rememberDialogVisibility()
val permissions = rememberSettingsPermissions( val permissions = rememberSettingsPermissions(
sendAction = onAction, sendAction = onAction,
onBluetoothGranted = { dialogs.bluetooth = true } onBluetoothGranted = { dialogs.bluetooth = true },
onNetworkGranted = { dialogs.network = true }
) )
// Dialogs // Dialogs
@@ -273,7 +274,7 @@ private fun SettingsScreen(uiState: SettingsState, onAction: (SettingsAction) ->
} }
item(span = { GridItemSpan(maxLineSpan) }) { item(span = { GridItemSpan(maxLineSpan) }) {
OutputCard( OutputCard(
onNetworkClick = { dialogs.network = true }, onNetworkClick = permissions.launchNetwork,
onBluetoothClick = permissions.launchBluetooth, onBluetoothClick = permissions.launchBluetooth,
onRadioControlClick = { dialogs.radioControl = true } onRadioControlClick = { dialogs.radioControl = true }
) )
@@ -651,13 +652,15 @@ private class SettingsPermissions(
val launchLocation: () -> Unit, val launchLocation: () -> Unit,
val launchTleImport: () -> Unit, val launchTleImport: () -> Unit,
val launchTransceiverImport: () -> Unit, val launchTransceiverImport: () -> Unit,
val launchBluetooth: () -> Unit val launchBluetooth: () -> Unit,
val launchNetwork: () -> Unit
) )
@Composable @Composable
private fun rememberSettingsPermissions( private fun rememberSettingsPermissions(
sendAction: (SettingsAction) -> Unit, sendAction: (SettingsAction) -> Unit,
onBluetoothGranted: () -> Unit onBluetoothGranted: () -> Unit,
onNetworkGranted: () -> Unit
): SettingsPermissions { ): SettingsPermissions {
val locationError = stringResource(R.string.prefs_loc_gps_error) val locationError = stringResource(R.string.prefs_loc_gps_error)
val locationRequest = rememberLauncherForActivityResult( val locationRequest = rememberLauncherForActivityResult(
@@ -685,6 +688,12 @@ private fun rememberSettingsPermissions(
else sendAction(SettingsAction.ShowToast(bluetoothError)) 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 { return remember {
SettingsPermissions( SettingsPermissions(
launchLocation = { launchLocation = {
@@ -694,7 +703,14 @@ private fun rememberSettingsPermissions(
}, },
launchTleImport = { tleRequest.launch("*/*") }, launchTleImport = { tleRequest.launch("*/*") },
launchTransceiverImport = { transceiversRequest.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()
}
}
) )
} }
} }
+7 -7
View File
@@ -1,26 +1,26 @@
[versions] [versions]
#noinspection UnusedVersionCatalogEntry #noinspection UnusedVersionCatalogEntry
appVersionCode = "442" appVersionCode = "443"
#noinspection UnusedVersionCatalogEntry #noinspection UnusedVersionCatalogEntry
appVersionName = "4.4.2" appVersionName = "4.4.3"
#noinspection GradleDependency,UnusedVersionCatalogEntry #noinspection UnusedVersionCatalogEntry
compileSdk = "36" compileSdk = "37"
#noinspection UnusedVersionCatalogEntry #noinspection UnusedVersionCatalogEntry
minSdk = "24" minSdk = "24"
#noinspection UnusedVersionCatalogEntry #noinspection UnusedVersionCatalogEntry
jdkVersion = "17" jdkVersion = "21"
#noinspection UnusedVersionCatalogEntry #noinspection UnusedVersionCatalogEntry
packageName = "com.rtbishop.look4sat" packageName = "com.rtbishop.look4sat"
android-gradle-plugin = "9.2.1" 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-core-splashscreen = "1.2.0"
androidx-room = "2.8.4" androidx-room = "2.8.4"
compose-bom = "2026.06.00" compose-bom = "2026.06.00"
compose-activity = "1.13.0" compose-activity = "1.13.0"
compose-lifecycle = "2.10.0" compose-lifecycle = "2.11.0"
compose-navigation3 = "1.1.3" compose-navigation3 = "1.1.3"
google-ksp = "2.3.8" google-ksp = "2.3.8"
+2 -2
View File
@@ -1,7 +1,7 @@
#Sun Jun 21 14:57:04 BST 2026 #Sat Jun 27 12:51:12 BST 2026
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME