Skip to content

Commit 88f0522

Browse files
committed
refactor: Set minSdk to 32 and remove legacy code
This commit increases the minimum SDK version to 32 (Android 12L), allowing for the removal of compatibility code for older Android versions. Key changes include: * Updated `MIN_SDK` from 26 to 32. * Removed conditional logic for API levels below 32, particularly for permissions, foreground services, and UI components. * Simplified Bluetooth permission handling to only target modern APIs. * Cleaned up AndroidManifest by removing legacy permission tags. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
1 parent 5676041 commit 88f0522

File tree

11 files changed

+25
-122
lines changed

11 files changed

+25
-122
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,10 @@
2828
android:name="android.hardware.location.gps"
2929
android:required="false" />
3030

31-
<!-- Request legacy Bluetooth permissions on older devices -->
32-
<uses-permission android:name="android.permission.BLUETOOTH"
33-
android:maxSdkVersion="30" />
34-
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
35-
android:maxSdkVersion="30" />
36-
37-
<!-- API 31+ Bluetooth permissions -->
31+
<!-- API 31+ Bluetooth permissions (Min SDK is 32) -->
3832
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
3933
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
40-
android:usesPermissionFlags="neverForLocation"
41-
tools:targetApi="s" />
34+
android:usesPermissionFlags="neverForLocation" />
4235

4336
<!-- API 33+ Notification runtime permissions -->
4437
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

app/src/main/java/com/geeksville/mesh/MainActivity.kt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import android.content.Intent
2323
import android.graphics.Color
2424
import android.hardware.usb.UsbManager
2525
import android.net.Uri
26-
import android.os.Build
2726
import android.os.Bundle
2827
import androidx.activity.SystemBarStyle
2928
import androidx.activity.compose.setContent
@@ -67,14 +66,9 @@ class MainActivity : AppCompatActivity() {
6766

6867
override fun onCreate(savedInstanceState: Bundle?) {
6968
installSplashScreen()
70-
enableEdgeToEdge(
71-
// Disable three-button navbar scrim on pre-Q devices
72-
navigationBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT),
73-
)
74-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
75-
// Disable three-button navbar scrim
76-
window.setNavigationBarContrastEnforced(false)
77-
}
69+
enableEdgeToEdge(navigationBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT))
70+
// Disable three-button navbar scrim (unconditional on API 32+)
71+
window.setNavigationBarContrastEnforced(false)
7872

7973
super.onCreate(savedInstanceState)
8074

app/src/main/java/com/geeksville/mesh/service/MeshService.kt

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import android.app.Service
2323
import android.content.Context
2424
import android.content.Intent
2525
import android.content.pm.ServiceInfo
26-
import android.os.Build
2726
import android.os.IBinder
2827
import android.os.RemoteException
2928
import android.util.Log
@@ -477,14 +476,10 @@ class MeshService : Service() {
477476
this,
478477
SERVICE_NOTIFY_ID,
479478
notification,
480-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
481-
if (hasLocationPermission()) {
482-
ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
483-
} else {
484-
ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
485-
}
479+
if (hasLocationPermission()) {
480+
ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
486481
} else {
487-
0 // No specific type needed for older Android versions
482+
ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
488483
},
489484
)
490485
} catch (ex: Exception) {
@@ -1359,7 +1354,7 @@ class MeshService : Service() {
13591354
val failure =
13601355
when {
13611356
address == null -> "no_active_address"
1362-
myNodeNum == null -> "no_my_node"
1357+
myNodeInfo == null -> "no_my_node"
13631358
else -> null
13641359
}
13651360
if (failure != null) {
@@ -1368,7 +1363,7 @@ class MeshService : Service() {
13681363
}
13691364

13701365
val safeAddress = address!!
1371-
val myNum = myNodeNum!!
1366+
val myNum = myNodeNum
13721367
val storeForwardConfig = moduleConfig.storeForward
13731368
val lastRequest = meshPrefs.getStoreForwardLastRequest(safeAddress)
13741369
val (window, max) =

app/src/main/java/com/geeksville/mesh/service/MeshServiceStarter.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package com.geeksville.mesh.service
1919

2020
import android.app.ForegroundServiceStartNotAllowedException
2121
import android.content.Context
22-
import android.os.Build
2322
import co.touchlab.kermit.Logger
2423
import com.geeksville.mesh.BuildConfig
2524

@@ -36,13 +35,9 @@ fun MeshService.Companion.startService(context: Context) {
3635
Logger.i { "Trying to start service debug=${BuildConfig.DEBUG}" }
3736

3837
val intent = createIntent(context)
39-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
40-
try {
41-
context.startForegroundService(intent)
42-
} catch (ex: ForegroundServiceStartNotAllowedException) {
43-
Logger.e { "Unable to start service: ${ex.message}" }
44-
}
45-
} else {
38+
try {
4639
context.startForegroundService(intent)
40+
} catch (ex: ForegroundServiceStartNotAllowedException) {
41+
Logger.e { "Unable to start service: ${ex.message}" }
4742
}
4843
}

app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
package com.geeksville.mesh.ui.connections
1919

2020
import android.net.InetAddresses
21-
import android.os.Build
22-
import android.util.Patterns
2321
import androidx.compose.foundation.layout.Arrangement
2422
import androidx.compose.foundation.layout.Box
2523
import androidx.compose.foundation.layout.Column
@@ -93,12 +91,7 @@ import org.meshtastic.feature.settings.radio.RadioConfigViewModel
9391
import org.meshtastic.feature.settings.radio.component.PacketResponseStateDialog
9492
import org.meshtastic.proto.ConfigProtos
9593

96-
fun String?.isIPAddress(): Boolean = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
97-
@Suppress("DEPRECATION")
98-
this != null && Patterns.IP_ADDRESS.matcher(this).matches()
99-
} else {
100-
InetAddresses.isNumericAddress(this.toString())
101-
}
94+
fun String?.isIPAddress(): Boolean = InetAddresses.isNumericAddress(this.toString())
10295

10396
/**
10497
* Composable screen for managing device connections (BLE, TCP, USB). It handles permission requests for location and

app/src/main/java/com/geeksville/mesh/ui/connections/components/BLEDevices.kt

Lines changed: 3 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package com.geeksville.mesh.ui.connections.components
1919

2020
import android.Manifest
2121
import android.content.Intent
22-
import android.os.Build
2322
import android.provider.Settings.ACTION_BLUETOOTH_SETTINGS
2423
import androidx.activity.compose.rememberLauncherForActivityResult
2524
import androidx.activity.result.contract.ActivityResultContracts
@@ -40,7 +39,6 @@ import androidx.compose.material3.Text
4039
import androidx.compose.runtime.Composable
4140
import androidx.compose.runtime.getValue
4241
import androidx.compose.runtime.remember
43-
import androidx.compose.runtime.rememberCoroutineScope
4442
import androidx.compose.ui.Alignment
4543
import androidx.compose.ui.Modifier
4644
import androidx.compose.ui.draw.alpha
@@ -52,8 +50,6 @@ import com.geeksville.mesh.model.DeviceListEntry
5250
import com.google.accompanist.permissions.ExperimentalPermissionsApi
5351
import com.google.accompanist.permissions.MultiplePermissionsState
5452
import com.google.accompanist.permissions.rememberMultiplePermissionsState
55-
import com.google.accompanist.permissions.rememberPermissionState
56-
import kotlinx.coroutines.launch
5753
import org.jetbrains.compose.resources.stringResource
5854
import org.meshtastic.core.service.ConnectionState
5955
import org.meshtastic.core.strings.Res
@@ -63,11 +59,9 @@ import org.meshtastic.core.strings.bluetooth_paired_devices
6359
import org.meshtastic.core.strings.grant_permissions
6460
import org.meshtastic.core.strings.no_ble_devices
6561
import org.meshtastic.core.strings.open_settings
66-
import org.meshtastic.core.strings.permission_missing
6762
import org.meshtastic.core.strings.permission_missing_31
6863
import org.meshtastic.core.strings.scan
6964
import org.meshtastic.core.strings.scanning_bluetooth
70-
import org.meshtastic.core.ui.util.showToast
7165

7266
/**
7367
* Composable that displays a list of Bluetooth Low Energy (BLE) devices and allows scanning. It handles Bluetooth
@@ -91,54 +85,21 @@ fun BLEDevices(
9185
scanModel: BTScanModel,
9286
bluetoothEnabled: Boolean,
9387
) {
94-
LocalContext.current // Used implicitly by stringResource
9588
val isScanning by scanModel.spinner.collectAsStateWithLifecycle(false)
9689

97-
// Define permissions needed for Bluetooth scanning based on Android version.
90+
// Bluetooth permissions for Android 12+ (Min SDK is 32)
9891
val bluetoothPermissionsList = remember {
99-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
100-
listOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT)
101-
} else {
102-
listOf(
103-
Manifest.permission.BLUETOOTH,
104-
Manifest.permission.ACCESS_FINE_LOCATION,
105-
Manifest.permission.ACCESS_COARSE_LOCATION,
106-
)
107-
}
92+
listOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT)
10893
}
10994

110-
val context = LocalContext.current
111-
val permsMissing =
112-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
113-
stringResource(Res.string.permission_missing_31)
114-
} else {
115-
stringResource(Res.string.permission_missing)
116-
}
117-
val coroutineScope = rememberCoroutineScope()
118-
119-
val singlePermissionState =
120-
rememberPermissionState(
121-
permission = Manifest.permission.ACCESS_BACKGROUND_LOCATION,
122-
onPermissionResult = { granted ->
123-
scanModel.refreshPermissions()
124-
scanModel.startScan()
125-
},
126-
)
127-
12895
val permissionsState =
12996
rememberMultiplePermissionsState(
13097
permissions = bluetoothPermissionsList,
13198
onPermissionsResult = { permissions ->
13299
val granted = permissions.values.all { it }
133-
if (permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false)) {
134-
coroutineScope.launch { context.showToast(permsMissing) }
135-
singlePermissionState.launchPermissionRequest()
136-
}
137100
if (granted) {
138101
scanModel.refreshPermissions()
139102
scanModel.startScan()
140-
} else {
141-
coroutineScope.launch { context.showToast(permsMissing) }
142103
}
143104
},
144105
)
@@ -231,12 +192,7 @@ fun BLEDevices(
231192
} else {
232193
// Show a message and a button to grant permissions if not all granted
233194
EmptyStateContent(
234-
text =
235-
if (permissionsState.shouldShowRationale) {
236-
stringResource(Res.string.permission_missing)
237-
} else {
238-
stringResource(Res.string.permission_missing_31)
239-
},
195+
text = stringResource(Res.string.permission_missing_31),
240196
actionButton = {
241197
Button(onClick = { checkPermissionsAndScan(permissionsState, scanModel, bluetoothEnabled) }) {
242198
Text(text = stringResource(Res.string.grant_permissions))

config.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ VERSION_CODE_OFFSET=29314197
2020

2121
# Application and SDK versions
2222
APPLICATION_ID=com.geeksville.mesh
23-
MIN_SDK=26
23+
MIN_SDK=32
2424
TARGET_SDK=36
2525
COMPILE_SDK=36
2626

core/common/src/androidMain/kotlin/org/meshtastic/core/common/ContextServices.kt

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import android.Manifest
2121
import android.content.Context
2222
import android.content.pm.PackageManager
2323
import android.location.LocationManager
24-
import android.os.Build
2524
import androidx.core.content.ContextCompat
2625

2726
/** Checks if the device has a GPS receiver. */
@@ -44,22 +43,12 @@ fun Context.gpsDisabled(): Boolean {
4443
* Determines the list of Bluetooth permissions that are currently missing. Internal helper for
4544
* [hasBluetoothPermission].
4645
*
47-
* For Android S (API 31) and above, this includes [Manifest.permission.BLUETOOTH_SCAN] and
48-
* [Manifest.permission.BLUETOOTH_CONNECT]. For older versions, it includes [Manifest.permission.ACCESS_FINE_LOCATION]
49-
* as it is required for Bluetooth scanning.
46+
* This includes [Manifest.permission.BLUETOOTH_SCAN] and [Manifest.permission.BLUETOOTH_CONNECT].
5047
*
5148
* @return Array of missing Bluetooth permission strings. Empty if all are granted.
5249
*/
5350
private fun Context.getBluetoothPermissions(): Array<String> {
54-
val requiredPermissions = mutableListOf<String>()
55-
56-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
57-
requiredPermissions.add(Manifest.permission.BLUETOOTH_SCAN)
58-
requiredPermissions.add(Manifest.permission.BLUETOOTH_CONNECT)
59-
} else {
60-
// ACCESS_FINE_LOCATION is required for Bluetooth scanning on pre-S devices.
61-
requiredPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION)
62-
}
51+
val requiredPermissions = listOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT)
6352
return requiredPermissions
6453
.filter { ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED }
6554
.toTypedArray()

core/data/src/main/kotlin/org/meshtastic/core/data/repository/LocationRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ constructor(
7777

7878
val providerList = buildList {
7979
val providers = allProviders
80-
if (android.os.Build.VERSION.SDK_INT >= 31 && LocationManager.FUSED_PROVIDER in providers) {
80+
if (LocationManager.FUSED_PROVIDER in providers) {
8181
add(LocationManager.FUSED_PROVIDER)
8282
} else {
8383
if (LocationManager.GPS_PROVIDER in providers) add(LocationManager.GPS_PROVIDER)

core/model/src/main/kotlin/org/meshtastic/core/model/util/DistanceExtensions.kt

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,9 @@ enum class DistanceUnit(val symbol: String, val multiplier: Float, val system: I
3333

3434
companion object {
3535
fun getFromLocale(locale: Locale = Locale.getDefault()): DisplayUnits =
36-
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
37-
when (LocaleData.getMeasurementSystem(ULocale.forLocale(locale))) {
38-
LocaleData.MeasurementSystem.SI -> DisplayUnits.METRIC
39-
else -> DisplayUnits.IMPERIAL
40-
}
41-
} else {
42-
when (locale.country.uppercase(locale)) {
43-
"US",
44-
"LR",
45-
"MM",
46-
"GB",
47-
-> DisplayUnits.IMPERIAL
48-
else -> DisplayUnits.METRIC
49-
}
36+
when (LocaleData.getMeasurementSystem(ULocale.forLocale(locale))) {
37+
LocaleData.MeasurementSystem.SI -> DisplayUnits.METRIC
38+
else -> DisplayUnits.IMPERIAL
5039
}
5140
}
5241
}

0 commit comments

Comments
 (0)