mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-08-15 06:56:30 +00:00
fix: request local network access for hotspot sharing
This commit is contained in:
parent
11d0bd794e
commit
42a8edf8ed
@ -1,6 +1,5 @@
|
||||
package com.bitchat.android.hotspot
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
@ -39,9 +38,7 @@ import com.bitchat.android.ui.theme.BitchatFontFamily
|
||||
import com.bitchat.android.ui.theme.BitchatTheme
|
||||
import com.bitchat.android.util.UniversalApkManager
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.google.accompanist.permissions.shouldShowRationale
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
@ -158,18 +155,10 @@ fun HotspotScreen(
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
fun IntroScreen(onStartHotspot: () -> Unit) {
|
||||
// Determine which permission to request based on Android version
|
||||
val requiredPermission = when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> Manifest.permission.NEARBY_WIFI_DEVICES
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Manifest.permission.ACCESS_FINE_LOCATION
|
||||
else -> null // No runtime permission needed on Android < 10
|
||||
}
|
||||
|
||||
val permissionState = requiredPermission?.let {
|
||||
rememberPermissionState(it) { granted ->
|
||||
if (granted) {
|
||||
onStartHotspot()
|
||||
}
|
||||
val requiredPermissions = remember { HotspotPermissions.requiredForSdk() }
|
||||
val permissionState = rememberMultiplePermissionsState(requiredPermissions) { results ->
|
||||
if (requiredPermissions.all { results[it] == true }) {
|
||||
onStartHotspot()
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,7 +208,7 @@ fun IntroScreen(onStartHotspot: () -> Unit) {
|
||||
}
|
||||
|
||||
// Permission rationale (if needed)
|
||||
if (permissionState != null && !permissionState.status.isGranted && permissionState.status.shouldShowRationale) {
|
||||
if (!permissionState.allPermissionsGranted && permissionState.shouldShowRationale) {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
@ -237,10 +226,13 @@ fun IntroScreen(onStartHotspot: () -> Unit) {
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
Text(
|
||||
text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
"BitChat needs nearby devices permission to create a Wi-Fi hotspot for sharing the app offline."
|
||||
} else {
|
||||
"BitChat needs location permission to create a Wi-Fi hotspot. This is required by Android for Wi-Fi scanning, but no location data is collected."
|
||||
text = when {
|
||||
Build.VERSION.SDK_INT >= HotspotPermissions.ANDROID_17_API_LEVEL ->
|
||||
"BitChat needs nearby devices and local network access to create a Wi-Fi hotspot and serve the app to connected devices."
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ->
|
||||
"BitChat needs nearby devices permission to create a Wi-Fi hotspot for sharing the app offline."
|
||||
else ->
|
||||
"BitChat needs location permission to create a Wi-Fi hotspot. This is required by Android for Wi-Fi scanning, but no location data is collected."
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f)
|
||||
@ -278,12 +270,12 @@ fun IntroScreen(onStartHotspot: () -> Unit) {
|
||||
Button(
|
||||
onClick = {
|
||||
// Check permission before starting hotspot
|
||||
if (permissionState == null || permissionState.status.isGranted) {
|
||||
if (permissionState.allPermissionsGranted) {
|
||||
// No permission needed or already granted
|
||||
onStartHotspot()
|
||||
} else {
|
||||
// Request permission (auto-start handled by onPermissionResult callback)
|
||||
permissionState.launchPermissionRequest()
|
||||
permissionState.launchMultiplePermissionRequest()
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
|
||||
@ -6,7 +6,6 @@ import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.wifi.p2p.WifiP2pConfig
|
||||
import android.net.wifi.p2p.WifiP2pGroup
|
||||
import android.net.wifi.p2p.WifiP2pManager
|
||||
@ -16,7 +15,6 @@ import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.PowerManager
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
import java.net.NetworkInterface
|
||||
import java.security.SecureRandom
|
||||
import kotlin.random.Random
|
||||
@ -100,12 +98,15 @@ class HotspotManager(private val context: Context) {
|
||||
return
|
||||
}
|
||||
|
||||
val missingPermission = requiredRuntimePermission()?.takeUnless {
|
||||
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
if (missingPermission != null) {
|
||||
Log.w(TAG, "Cannot start hotspot without $missingPermission")
|
||||
callback.onError("Nearby Wi-Fi permission is required to start the hotspot")
|
||||
val missingPermissions = HotspotPermissions.missingFrom(context)
|
||||
if (missingPermissions.isNotEmpty()) {
|
||||
Log.w(TAG, "Cannot start hotspot; missing required permissions: $missingPermissions")
|
||||
val message = if (Manifest.permission.ACCESS_LOCAL_NETWORK in missingPermissions) {
|
||||
"Local network permission is required to share the app over the hotspot"
|
||||
} else {
|
||||
"Nearby Wi-Fi permission is required to start the hotspot"
|
||||
}
|
||||
callback.onError(message)
|
||||
return
|
||||
}
|
||||
|
||||
@ -248,7 +249,7 @@ class HotspotManager(private val context: Context) {
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "Wi-Fi permission was revoked while creating the group", e)
|
||||
failStartup("Nearby Wi-Fi permission was revoked. Grant it and try again.")
|
||||
failStartup("A required Wi-Fi or local network permission was revoked. Grant it and try again.")
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,17 +370,7 @@ class HotspotManager(private val context: Context) {
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "Wi-Fi permission was revoked while reading group info", e)
|
||||
failStartup("Nearby Wi-Fi permission was revoked. Grant it and try again.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun requiredRuntimePermission(): String? {
|
||||
return when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ->
|
||||
Manifest.permission.NEARBY_WIFI_DEVICES
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ->
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
else -> null
|
||||
failStartup("A required Wi-Fi or local network permission was revoked. Grant it and try again.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
package com.bitchat.android.hotspot
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
internal object HotspotPermissions {
|
||||
const val ANDROID_17_API_LEVEL = 37
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
fun requiredForSdk(sdkInt: Int = Build.VERSION.SDK_INT): List<String> {
|
||||
return when {
|
||||
sdkInt >= ANDROID_17_API_LEVEL -> listOf(
|
||||
Manifest.permission.NEARBY_WIFI_DEVICES,
|
||||
Manifest.permission.ACCESS_LOCAL_NETWORK
|
||||
)
|
||||
sdkInt >= Build.VERSION_CODES.TIRAMISU -> listOf(
|
||||
Manifest.permission.NEARBY_WIFI_DEVICES
|
||||
)
|
||||
sdkInt >= Build.VERSION_CODES.Q -> listOf(
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
)
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun missingFrom(context: Context): List<String> {
|
||||
return requiredForSdk().filter { permission ->
|
||||
ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package com.bitchat.android.hotspot
|
||||
|
||||
import android.Manifest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class HotspotPermissionsTest {
|
||||
|
||||
@Test
|
||||
fun `Android 17 requires nearby Wi-Fi and local network permissions`() {
|
||||
assertEquals(
|
||||
listOf(
|
||||
Manifest.permission.NEARBY_WIFI_DEVICES,
|
||||
Manifest.permission.ACCESS_LOCAL_NETWORK
|
||||
),
|
||||
HotspotPermissions.requiredForSdk(37)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Android 13 through 16 require nearby Wi-Fi permission`() {
|
||||
val expected = listOf(Manifest.permission.NEARBY_WIFI_DEVICES)
|
||||
|
||||
assertEquals(expected, HotspotPermissions.requiredForSdk(33))
|
||||
assertEquals(expected, HotspotPermissions.requiredForSdk(36))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Android 10 through 12 require fine location permission`() {
|
||||
val expected = listOf(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
|
||||
assertEquals(expected, HotspotPermissions.requiredForSdk(29))
|
||||
assertEquals(expected, HotspotPermissions.requiredForSdk(32))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Android 9 and earlier require no hotspot runtime permission`() {
|
||||
assertEquals(emptyList<String>(), HotspotPermissions.requiredForSdk(28))
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user