mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 17:05:20 +00:00
wifi aware wip
This commit is contained in:
@@ -38,6 +38,12 @@ class BitchatApplication : Application() {
|
||||
// Initialize debug preference manager (persists debug toggles)
|
||||
try { com.bitchat.android.ui.debug.DebugPreferenceManager.init(this) } catch (_: Exception) { }
|
||||
|
||||
// Initialize Wi‑Fi Aware controller with persisted default
|
||||
try {
|
||||
val enabled = com.bitchat.android.ui.debug.DebugPreferenceManager.getWifiAwareEnabled(false)
|
||||
com.bitchat.android.wifiaware.WifiAwareController.initialize(this, enabled)
|
||||
} catch (_: Exception) { }
|
||||
|
||||
// TorManager already initialized above
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,6 +209,7 @@ class OnboardingCoordinator(
|
||||
return when {
|
||||
permission.contains("BLUETOOTH") -> "Bluetooth/Nearby Devices"
|
||||
permission.contains("LOCATION") -> "Location (for Bluetooth scanning)"
|
||||
permission.contains("NEARBY_WIFI") -> "Nearby Wi‑Fi Devices (for Wi‑Fi Aware)"
|
||||
permission.contains("NOTIFICATION") -> "Notifications"
|
||||
else -> permission.substringAfterLast(".")
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.material.icons.filled.Notifications
|
||||
import androidx.compose.material.icons.filled.Power
|
||||
import androidx.compose.material.icons.filled.Mic
|
||||
import androidx.compose.material.icons.filled.Security
|
||||
import androidx.compose.material.icons.filled.Wifi
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material3.*
|
||||
@@ -242,6 +243,7 @@ private fun getPermissionIcon(permissionType: PermissionType): ImageVector {
|
||||
PermissionType.PRECISE_LOCATION -> Icons.Filled.LocationOn
|
||||
PermissionType.MICROPHONE -> Icons.Filled.Mic
|
||||
PermissionType.NOTIFICATIONS -> Icons.Filled.Notifications
|
||||
PermissionType.WIFI_AWARE -> Icons.Filled.Wifi
|
||||
PermissionType.BATTERY_OPTIMIZATION -> Icons.Filled.Power
|
||||
PermissionType.OTHER -> Icons.Filled.Settings
|
||||
}
|
||||
|
||||
@@ -67,6 +67,11 @@ class PermissionManager(private val context: Context) {
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
))
|
||||
|
||||
// Wi‑Fi Aware: Android 13+ requires NEARBY_WIFI_DEVICES runtime permission
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
permissions.add(Manifest.permission.NEARBY_WIFI_DEVICES)
|
||||
}
|
||||
|
||||
// Notification permission intentionally excluded to keep it optional
|
||||
|
||||
return permissions
|
||||
@@ -177,6 +182,20 @@ class PermissionManager(private val context: Context) {
|
||||
)
|
||||
)
|
||||
|
||||
// Wi‑Fi Aware category (Android 13+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val wifiAwarePermissions = listOf(Manifest.permission.NEARBY_WIFI_DEVICES)
|
||||
categories.add(
|
||||
PermissionCategory(
|
||||
type = PermissionType.WIFI_AWARE,
|
||||
description = "Enable Wi‑Fi Aware to discover and connect to nearby bitchat users over Wi‑Fi.",
|
||||
permissions = wifiAwarePermissions,
|
||||
isGranted = wifiAwarePermissions.all { isPermissionGranted(it) },
|
||||
systemDescription = "Allow bitchat to discover nearby Wi‑Fi devices"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Notifications category (if applicable)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
categories.add(
|
||||
@@ -262,6 +281,7 @@ enum class PermissionType(val nameValue: String) {
|
||||
PRECISE_LOCATION("Precise Location"),
|
||||
MICROPHONE("Microphone"),
|
||||
NOTIFICATIONS("Notifications"),
|
||||
WIFI_AWARE("Wi‑Fi Aware"),
|
||||
BATTERY_OPTIMIZATION("Battery Optimization"),
|
||||
OTHER("Other")
|
||||
}
|
||||
|
||||
@@ -576,9 +576,21 @@ private fun PeerItem(
|
||||
tint = Color.Gray
|
||||
)
|
||||
} else {
|
||||
val wifiAwarePeers by com.bitchat.android.wifiaware.WifiAwareController.connectedPeers.collectAsState()
|
||||
val isWifiAware = wifiAwarePeers.containsKey(peerID)
|
||||
val icon = when {
|
||||
isWifiAware -> Icons.Filled.Wifi
|
||||
isDirect -> Icons.Outlined.SettingsInputAntenna
|
||||
else -> Icons.Filled.Route
|
||||
}
|
||||
val cd = when {
|
||||
isWifiAware -> "Direct Wi‑Fi Aware"
|
||||
isDirect -> "Direct Bluetooth"
|
||||
else -> "Routed"
|
||||
}
|
||||
Icon(
|
||||
imageVector = if (isDirect) Icons.Outlined.SettingsInputAntenna else Icons.Filled.Route,
|
||||
contentDescription = if (isDirect) "Direct Bluetooth" else "Routed",
|
||||
imageVector = icon,
|
||||
contentDescription = cd,
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = colorScheme.onSurface.copy(alpha = 0.8f)
|
||||
)
|
||||
|
||||
@@ -20,6 +20,10 @@ object DebugPreferenceManager {
|
||||
// GCS keys (no migration/back-compat)
|
||||
private const val KEY_GCS_MAX_BYTES = "gcs_max_filter_bytes"
|
||||
private const val KEY_GCS_FPR = "gcs_filter_fpr_percent"
|
||||
// Transport master toggles
|
||||
private const val KEY_BLE_ENABLED = "ble_enabled"
|
||||
private const val KEY_WIFI_AWARE_ENABLED = "wifi_aware_enabled"
|
||||
private const val KEY_WIFI_AWARE_VERBOSE = "wifi_aware_verbose"
|
||||
|
||||
private lateinit var prefs: SharedPreferences
|
||||
|
||||
@@ -100,4 +104,26 @@ object DebugPreferenceManager {
|
||||
fun setGcsFprPercent(value: Double) {
|
||||
if (ready()) prefs.edit().putLong(KEY_GCS_FPR, java.lang.Double.doubleToRawLongBits(value)).apply()
|
||||
}
|
||||
|
||||
// Transport toggles
|
||||
fun getBleEnabled(default: Boolean = true): Boolean =
|
||||
if (ready()) prefs.getBoolean(KEY_BLE_ENABLED, default) else default
|
||||
|
||||
fun setBleEnabled(value: Boolean) {
|
||||
if (ready()) prefs.edit().putBoolean(KEY_BLE_ENABLED, value).apply()
|
||||
}
|
||||
|
||||
fun getWifiAwareEnabled(default: Boolean = false): Boolean =
|
||||
if (ready()) prefs.getBoolean(KEY_WIFI_AWARE_ENABLED, default) else default
|
||||
|
||||
fun setWifiAwareEnabled(value: Boolean) {
|
||||
if (ready()) prefs.edit().putBoolean(KEY_WIFI_AWARE_ENABLED, value).apply()
|
||||
}
|
||||
|
||||
fun getWifiAwareVerbose(default: Boolean = false): Boolean =
|
||||
if (ready()) prefs.getBoolean(KEY_WIFI_AWARE_VERBOSE, default) else default
|
||||
|
||||
fun setWifiAwareVerbose(value: Boolean) {
|
||||
if (ready()) prefs.edit().putBoolean(KEY_WIFI_AWARE_VERBOSE, value).apply()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,16 @@ class DebugSettingsManager private constructor() {
|
||||
private val _packetRelayEnabled = MutableStateFlow(true)
|
||||
val packetRelayEnabled: StateFlow<Boolean> = _packetRelayEnabled.asStateFlow()
|
||||
|
||||
// Master transport toggles
|
||||
private val _bleEnabled = MutableStateFlow(true)
|
||||
val bleEnabled: StateFlow<Boolean> = _bleEnabled.asStateFlow()
|
||||
|
||||
private val _wifiAwareEnabled = MutableStateFlow(false)
|
||||
val wifiAwareEnabled: StateFlow<Boolean> = _wifiAwareEnabled.asStateFlow()
|
||||
|
||||
private val _wifiAwareVerbose = MutableStateFlow(false)
|
||||
val wifiAwareVerbose: StateFlow<Boolean> = _wifiAwareVerbose.asStateFlow()
|
||||
|
||||
// Connection limit overrides (debug)
|
||||
private val _maxConnectionsOverall = MutableStateFlow(8)
|
||||
val maxConnectionsOverall: StateFlow<Int> = _maxConnectionsOverall.asStateFlow()
|
||||
@@ -54,6 +64,10 @@ class DebugSettingsManager private constructor() {
|
||||
_maxConnectionsOverall.value = DebugPreferenceManager.getMaxConnectionsOverall(8)
|
||||
_maxServerConnections.value = DebugPreferenceManager.getMaxConnectionsServer(8)
|
||||
_maxClientConnections.value = DebugPreferenceManager.getMaxConnectionsClient(8)
|
||||
// Transport toggles
|
||||
_bleEnabled.value = DebugPreferenceManager.getBleEnabled(true)
|
||||
_wifiAwareEnabled.value = DebugPreferenceManager.getWifiAwareEnabled(false)
|
||||
_wifiAwareVerbose.value = DebugPreferenceManager.getWifiAwareVerbose(false)
|
||||
} catch (_: Exception) {
|
||||
// Preferences not ready yet; keep defaults. They will be applied on first change.
|
||||
}
|
||||
@@ -140,6 +154,27 @@ class DebugSettingsManager private constructor() {
|
||||
))
|
||||
}
|
||||
|
||||
fun setBleEnabled(enabled: Boolean) {
|
||||
DebugPreferenceManager.setBleEnabled(enabled)
|
||||
_bleEnabled.value = enabled
|
||||
addDebugMessage(DebugMessage.SystemMessage(if (enabled) "🟢 BLE enabled" else "🔴 BLE disabled"))
|
||||
}
|
||||
|
||||
fun setWifiAwareEnabled(enabled: Boolean) {
|
||||
DebugPreferenceManager.setWifiAwareEnabled(enabled)
|
||||
_wifiAwareEnabled.value = enabled
|
||||
addDebugMessage(DebugMessage.SystemMessage(if (enabled) "🟢 Wi‑Fi Aware enabled" else "🔴 Wi‑Fi Aware disabled"))
|
||||
try {
|
||||
com.bitchat.android.wifiaware.WifiAwareController.setEnabled(enabled)
|
||||
} catch (_: Exception) { }
|
||||
}
|
||||
|
||||
fun setWifiAwareVerbose(enabled: Boolean) {
|
||||
DebugPreferenceManager.setWifiAwareVerbose(enabled)
|
||||
_wifiAwareVerbose.value = enabled
|
||||
addDebugMessage(DebugMessage.SystemMessage(if (enabled) "🔊 Wi‑Fi Aware verbose logging enabled" else "🔇 Wi‑Fi Aware verbose logging disabled"))
|
||||
}
|
||||
|
||||
fun setMaxConnectionsOverall(value: Int) {
|
||||
val clamped = value.coerceIn(1, 32)
|
||||
DebugPreferenceManager.setMaxConnectionsOverall(clamped)
|
||||
@@ -197,6 +232,16 @@ class DebugSettingsManager private constructor() {
|
||||
fun updateConnectedDevices(devices: List<ConnectedDevice>) {
|
||||
_connectedDevices.value = devices
|
||||
}
|
||||
|
||||
// Wi‑Fi Aware debug collections
|
||||
private val _wifiAwareDiscovered = MutableStateFlow<Map<String, String>>(emptyMap()) // peerID->nickname
|
||||
val wifiAwareDiscovered: StateFlow<Map<String, String>> = _wifiAwareDiscovered.asStateFlow()
|
||||
|
||||
private val _wifiAwareConnected = MutableStateFlow<Map<String, String>>(emptyMap()) // peerID->ip
|
||||
val wifiAwareConnected: StateFlow<Map<String, String>> = _wifiAwareConnected.asStateFlow()
|
||||
|
||||
fun updateWifiAwareDiscovered(map: Map<String, String>) { _wifiAwareDiscovered.value = map }
|
||||
fun updateWifiAwareConnected(map: Map<String, String>) { _wifiAwareConnected.value = map }
|
||||
|
||||
fun updateRelayStats(stats: PacketRelayStats) {
|
||||
_relayStats.value = stats
|
||||
|
||||
@@ -8,6 +8,8 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Bluetooth
|
||||
import androidx.compose.material.icons.filled.Wifi
|
||||
import androidx.compose.material.icons.filled.WifiTethering
|
||||
import androidx.compose.material.icons.filled.BugReport
|
||||
import androidx.compose.material.icons.filled.Cancel
|
||||
import androidx.compose.material.icons.filled.Devices
|
||||
@@ -53,6 +55,11 @@ fun DebugSettingsSheet(
|
||||
val seenCapacity by manager.seenPacketCapacity.collectAsState()
|
||||
val gcsMaxBytes by manager.gcsMaxBytes.collectAsState()
|
||||
val gcsFpr by manager.gcsFprPercent.collectAsState()
|
||||
val bleEnabled by manager.bleEnabled.collectAsState()
|
||||
val wifiAwareEnabled by manager.wifiAwareEnabled.collectAsState()
|
||||
val wifiAwareVerbose by manager.wifiAwareVerbose.collectAsState()
|
||||
val wifiAwareDiscovered by manager.wifiAwareDiscovered.collectAsState()
|
||||
val wifiAwareConnected by manager.wifiAwareConnected.collectAsState()
|
||||
|
||||
// Push live connected devices from mesh service whenever sheet is visible
|
||||
LaunchedEffect(isPresented) {
|
||||
@@ -76,6 +83,15 @@ fun DebugSettingsSheet(
|
||||
)
|
||||
}
|
||||
manager.updateConnectedDevices(devices)
|
||||
// Also surface Wi‑Fi Aware status
|
||||
try {
|
||||
val ctrl = com.bitchat.android.wifiaware.WifiAwareController
|
||||
val known = ctrl.knownPeers.value
|
||||
val discovered = ctrl.discoveredPeers.value
|
||||
val discoveredMap = discovered.associateWith { pid -> known[pid] ?: "" }
|
||||
manager.updateWifiAwareDiscovered(discoveredMap)
|
||||
manager.updateWifiAwareConnected(ctrl.connectedPeers.value)
|
||||
} catch (_: Exception) { }
|
||||
kotlinx.coroutines.delay(1000)
|
||||
}
|
||||
}
|
||||
@@ -198,6 +214,46 @@ fun DebugSettingsSheet(
|
||||
}
|
||||
}
|
||||
|
||||
// Transport toggles (BLE + Wi‑Fi Aware)
|
||||
item {
|
||||
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
|
||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Icon(Icons.Filled.Devices, contentDescription = null, tint = Color(0xFF4CAF50))
|
||||
Text("Transports", fontFamily = FontFamily.Monospace, fontSize = 14.sp, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(Icons.Filled.Bluetooth, contentDescription = null, tint = Color(0xFF007AFF))
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text("BLE", fontFamily = FontFamily.Monospace, modifier = Modifier.weight(1f))
|
||||
Switch(checked = bleEnabled, onCheckedChange = {
|
||||
manager.setBleEnabled(it)
|
||||
scope.launch {
|
||||
if (it) {
|
||||
if (gattServerEnabled) meshService.connectionManager.startServer()
|
||||
if (gattClientEnabled) meshService.connectionManager.startClient()
|
||||
} else {
|
||||
meshService.connectionManager.stopServer()
|
||||
meshService.connectionManager.stopClient()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(Icons.Filled.Wifi, contentDescription = null, tint = Color(0xFF9C27B0))
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text("Wi‑Fi Aware", fontFamily = FontFamily.Monospace, modifier = Modifier.weight(1f))
|
||||
Switch(checked = wifiAwareEnabled, onCheckedChange = { manager.setWifiAwareEnabled(it) })
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Spacer(Modifier.width(24.dp))
|
||||
Text("Wi‑Fi Aware verbose", fontFamily = FontFamily.Monospace, modifier = Modifier.weight(1f))
|
||||
Switch(checked = wifiAwareVerbose, onCheckedChange = { manager.setWifiAwareVerbose(it) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Packet relay controls and stats
|
||||
item {
|
||||
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
|
||||
@@ -289,6 +345,43 @@ fun DebugSettingsSheet(
|
||||
}
|
||||
}
|
||||
|
||||
// Wi‑Fi Aware controls and status
|
||||
item {
|
||||
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
|
||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Icon(Icons.Filled.WifiTethering, contentDescription = null, tint = Color(0xFF9C27B0))
|
||||
Text("Wi‑Fi Aware", fontFamily = FontFamily.Monospace, fontSize = 14.sp, fontWeight = FontWeight.Medium)
|
||||
Spacer(Modifier.weight(1f))
|
||||
val running by com.bitchat.android.wifiaware.WifiAwareController.running.collectAsState()
|
||||
Text(if (running) "running" else "stopped", fontFamily = FontFamily.Monospace, fontSize = 12.sp, color = colorScheme.onSurface.copy(alpha = 0.7f))
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
AssistChip(onClick = { com.bitchat.android.wifiaware.WifiAwareController.startIfPossible() }, label = { Text("Start") })
|
||||
AssistChip(onClick = { com.bitchat.android.wifiaware.WifiAwareController.stop() }, label = { Text("Stop") })
|
||||
AssistChip(onClick = { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendBroadcastAnnounce() }, label = { Text("Announce") })
|
||||
}
|
||||
Text("Discovered: ${wifiAwareDiscovered.size}", fontFamily = FontFamily.Monospace, fontSize = 12.sp)
|
||||
if (wifiAwareDiscovered.isEmpty()) {
|
||||
Text("No discoveries yet", fontFamily = FontFamily.Monospace, fontSize = 11.sp, color = colorScheme.onSurface.copy(alpha = 0.6f))
|
||||
} else {
|
||||
wifiAwareDiscovered.entries.take(50).forEach { (peer, nick) ->
|
||||
Text("• ${if (nick.isBlank()) peer.take(8) + "…" else nick} (${peer.take(8)}…) ", fontFamily = FontFamily.Monospace, fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
Text("Connected: ${wifiAwareConnected.size}", fontFamily = FontFamily.Monospace, fontSize = 12.sp)
|
||||
if (wifiAwareConnected.isEmpty()) {
|
||||
Text("No active sockets", fontFamily = FontFamily.Monospace, fontSize = 11.sp, color = colorScheme.onSurface.copy(alpha = 0.6f))
|
||||
} else {
|
||||
wifiAwareConnected.entries.take(50).forEach { (peer, ip) ->
|
||||
Text("• ${peer.take(8)}… @ $ip", fontFamily = FontFamily.Monospace, fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connected devices
|
||||
item {
|
||||
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.bitchat.android.wifiaware
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* WifiAwareController manages lifecycle and debug surfacing for the WifiAwareMeshService.
|
||||
* It starts/stops the service based on debug preferences and exposes simple flows for UI.
|
||||
*/
|
||||
object WifiAwareController {
|
||||
private const val TAG = "WifiAwareController"
|
||||
|
||||
private var service: WifiAwareMeshService? = null
|
||||
private var appContext: Context? = null
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
private val _enabled = MutableStateFlow(false)
|
||||
val enabled: StateFlow<Boolean> = _enabled.asStateFlow()
|
||||
|
||||
private val _running = MutableStateFlow(false)
|
||||
val running: StateFlow<Boolean> = _running.asStateFlow()
|
||||
|
||||
// Simple debug surfacing
|
||||
private val _connectedPeers = MutableStateFlow<Map<String, String>>(emptyMap()) // peerID -> ip
|
||||
val connectedPeers: StateFlow<Map<String, String>> = _connectedPeers.asStateFlow()
|
||||
|
||||
private val _knownPeers = MutableStateFlow<Map<String, String>>(emptyMap()) // peerID -> nickname
|
||||
val knownPeers: StateFlow<Map<String, String>> = _knownPeers.asStateFlow()
|
||||
|
||||
private val _discoveredPeers = MutableStateFlow<Set<String>>(emptySet())
|
||||
val discoveredPeers: StateFlow<Set<String>> = _discoveredPeers.asStateFlow()
|
||||
|
||||
fun initialize(context: Context, enabledByDefault: Boolean) {
|
||||
appContext = context.applicationContext
|
||||
setEnabled(enabledByDefault)
|
||||
// Start background poller for debug surfacing
|
||||
scope.launch {
|
||||
while (isActive) {
|
||||
try {
|
||||
val s = service
|
||||
if (s != null) {
|
||||
_connectedPeers.value = s.getDeviceAddressToPeerMapping().entries.associate { (ip, peer) -> peer to ip }
|
||||
_knownPeers.value = s.getPeerNicknames()
|
||||
_discoveredPeers.value = s.getDiscoveredPeerIds()
|
||||
} else {
|
||||
_connectedPeers.value = emptyMap()
|
||||
_knownPeers.value = emptyMap()
|
||||
_discoveredPeers.value = emptySet()
|
||||
}
|
||||
} catch (_: Exception) { }
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setEnabled(value: Boolean) {
|
||||
_enabled.value = value
|
||||
if (value) startIfPossible() else stop()
|
||||
}
|
||||
|
||||
fun startIfPossible() {
|
||||
if (_running.value) return
|
||||
val ctx = appContext ?: return
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
Log.w(TAG, "Wi‑Fi Aware requires Android 10 (Q)+; disabled.")
|
||||
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("Wi‑Fi Aware not supported on this device (requires Android 10+)")) } catch (_: Exception) {}
|
||||
return
|
||||
}
|
||||
// Android 13+: require NEARBY_WIFI_DEVICES runtime permission
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val granted = androidx.core.content.ContextCompat.checkSelfPermission(ctx, android.Manifest.permission.NEARBY_WIFI_DEVICES) == android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||
if (!granted) {
|
||||
Log.w(TAG, "Missing NEARBY_WIFI_DEVICES permission; not starting Wi‑Fi Aware")
|
||||
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("Grant Nearby Wi‑Fi Devices to start Wi‑Fi Aware")) } catch (_: Exception) {}
|
||||
return
|
||||
}
|
||||
}
|
||||
try {
|
||||
service = WifiAwareMeshService(ctx).also {
|
||||
it.startServices()
|
||||
_running.value = true
|
||||
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("Wi‑Fi Aware started")) } catch (_: Exception) {}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "Failed to start WifiAwareMeshService", e)
|
||||
_running.value = false
|
||||
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("Wi‑Fi Aware failed to start: ${e.message}")) } catch (_: Exception) {}
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
try { service?.stopServices() } catch (_: Exception) { }
|
||||
service = null
|
||||
_running.value = false
|
||||
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("Wi‑Fi Aware stopped")) } catch (_: Exception) {}
|
||||
}
|
||||
|
||||
fun getService(): WifiAwareMeshService? = service
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user