mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-24 22:25:19 +00:00
check for support
This commit is contained in:
@@ -43,7 +43,7 @@ class BitchatApplication : Application() {
|
||||
|
||||
// Initialize Wi‑Fi Aware controller with persisted default
|
||||
try {
|
||||
val enabled = com.bitchat.android.ui.debug.DebugPreferenceManager.getWifiAwareEnabled(false)
|
||||
val enabled = com.bitchat.android.ui.debug.DebugPreferenceManager.getWifiAwareEnabled(true)
|
||||
com.bitchat.android.wifiaware.WifiAwareController.initialize(this, enabled)
|
||||
} catch (_: Exception) { }
|
||||
|
||||
|
||||
@@ -26,18 +26,17 @@ class PermissionManager(private val context: Context) {
|
||||
private fun shouldRequireWifiAwarePermission(): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return false
|
||||
val enabled = try {
|
||||
com.bitchat.android.ui.debug.DebugPreferenceManager.getWifiAwareEnabled(false)
|
||||
com.bitchat.android.ui.debug.DebugPreferenceManager.getWifiAwareEnabled(true)
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
true
|
||||
}
|
||||
if (!enabled) return false
|
||||
|
||||
val hasFeature = try {
|
||||
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
|
||||
return try {
|
||||
com.bitchat.android.wifiaware.WifiAwareSupport.isSupported(context)
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
return hasFeature
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -113,7 +113,7 @@ object DebugPreferenceManager {
|
||||
if (ready()) prefs.edit().putBoolean(KEY_BLE_ENABLED, value).apply()
|
||||
}
|
||||
|
||||
fun getWifiAwareEnabled(default: Boolean = false): Boolean =
|
||||
fun getWifiAwareEnabled(default: Boolean = true): Boolean =
|
||||
if (ready()) prefs.getBoolean(KEY_WIFI_AWARE_ENABLED, default) else default
|
||||
|
||||
fun setWifiAwareEnabled(value: Boolean) {
|
||||
|
||||
@@ -44,7 +44,7 @@ class DebugSettingsManager private constructor() {
|
||||
private val _bleEnabled = MutableStateFlow(true)
|
||||
val bleEnabled: StateFlow<Boolean> = _bleEnabled.asStateFlow()
|
||||
|
||||
private val _wifiAwareEnabled = MutableStateFlow(false)
|
||||
private val _wifiAwareEnabled = MutableStateFlow(true)
|
||||
val wifiAwareEnabled: StateFlow<Boolean> = _wifiAwareEnabled.asStateFlow()
|
||||
|
||||
// Master transport toggles
|
||||
@@ -76,7 +76,7 @@ class DebugSettingsManager private constructor() {
|
||||
_maxClientConnections.value = DebugPreferenceManager.getMaxConnectionsClient(8)
|
||||
// Transport toggles
|
||||
_bleEnabled.value = DebugPreferenceManager.getBleEnabled(true)
|
||||
_wifiAwareEnabled.value = DebugPreferenceManager.getWifiAwareEnabled(false)
|
||||
_wifiAwareEnabled.value = DebugPreferenceManager.getWifiAwareEnabled(true)
|
||||
_wifiAwareVerbose.value = DebugPreferenceManager.getWifiAwareVerbose(false)
|
||||
} catch (_: Exception) {
|
||||
// Preferences not ready yet; keep defaults. They will be applied on first change.
|
||||
|
||||
@@ -130,6 +130,9 @@ fun DebugSettingsSheet(
|
||||
val wifiAwareVerbose by manager.wifiAwareVerbose.collectAsState()
|
||||
val wifiAwareDiscovered by manager.wifiAwareDiscovered.collectAsState()
|
||||
val wifiAwareConnected by manager.wifiAwareConnected.collectAsState()
|
||||
val wifiAwareSupported by com.bitchat.android.wifiaware.WifiAwareController.supported.collectAsState()
|
||||
val wifiAwareAvailable by com.bitchat.android.wifiaware.WifiAwareController.available.collectAsState()
|
||||
val wifiAwareSupportStatus by com.bitchat.android.wifiaware.WifiAwareController.supportStatus.collectAsState()
|
||||
// Persistent notification is now controlled solely by MeshServicePreferences.isBackgroundEnabled
|
||||
val listState = rememberLazyListState()
|
||||
val isScrolled by remember {
|
||||
@@ -327,7 +330,23 @@ fun DebugSettingsSheet(
|
||||
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) })
|
||||
val wifiSwitchEnabled = wifiAwareSupported
|
||||
Text(
|
||||
when {
|
||||
!wifiAwareSupported -> "unsupported"
|
||||
wifiAwareAvailable -> "available"
|
||||
else -> "unavailable"
|
||||
},
|
||||
fontFamily = FontFamily.Monospace,
|
||||
fontSize = 11.sp,
|
||||
color = colorScheme.onSurface.copy(alpha = 0.6f)
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Switch(
|
||||
checked = wifiAwareEnabled && wifiAwareSupported,
|
||||
enabled = wifiSwitchEnabled,
|
||||
onCheckedChange = { manager.setWifiAwareEnabled(it) }
|
||||
)
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Spacer(Modifier.width(24.dp))
|
||||
@@ -601,17 +620,39 @@ fun DebugSettingsSheet(
|
||||
item {
|
||||
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
|
||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
val running by com.bitchat.android.wifiaware.WifiAwareController.running.collectAsState()
|
||||
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))
|
||||
val wifiStatusText = when {
|
||||
!wifiAwareSupported -> "unsupported"
|
||||
running -> "running"
|
||||
!wifiAwareAvailable -> "unavailable"
|
||||
else -> "stopped"
|
||||
}
|
||||
Text(wifiStatusText, fontFamily = FontFamily.Monospace, fontSize = 12.sp, color = colorScheme.onSurface.copy(alpha = 0.7f))
|
||||
}
|
||||
if (!wifiAwareSupported) {
|
||||
Text(
|
||||
wifiAwareSupportStatus?.reason ?: "Wi-Fi Aware is not supported on this device",
|
||||
fontFamily = FontFamily.Monospace,
|
||||
fontSize = 11.sp,
|
||||
color = colorScheme.onSurface.copy(alpha = 0.6f)
|
||||
)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
AssistChip(onClick = { manager.setWifiAwareEnabled(true) }, label = { Text("Start") })
|
||||
AssistChip(
|
||||
onClick = { manager.setWifiAwareEnabled(true) },
|
||||
enabled = wifiAwareSupported,
|
||||
label = { Text("Start") }
|
||||
)
|
||||
AssistChip(onClick = { manager.setWifiAwareEnabled(false) }, label = { Text("Stop") })
|
||||
AssistChip(onClick = { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendBroadcastAnnounce() }, label = { Text("Announce") })
|
||||
AssistChip(
|
||||
onClick = { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendBroadcastAnnounce() },
|
||||
enabled = running,
|
||||
label = { Text("Announce") }
|
||||
)
|
||||
}
|
||||
Text("Discovered: ${wifiAwareDiscovered.size}", fontFamily = FontFamily.Monospace, fontSize = 12.sp)
|
||||
if (wifiAwareDiscovered.isEmpty()) {
|
||||
|
||||
@@ -30,12 +30,22 @@ object WifiAwareController {
|
||||
private var starting = false
|
||||
private val restartInFlight = AtomicBoolean(false)
|
||||
private var awareReceiverRegistered = false
|
||||
private var lastBlockedReason: String? = null
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
private val _enabled = MutableStateFlow(false)
|
||||
val enabled: StateFlow<Boolean> = _enabled.asStateFlow()
|
||||
|
||||
private val _supported = MutableStateFlow(false)
|
||||
val supported: StateFlow<Boolean> = _supported.asStateFlow()
|
||||
|
||||
private val _available = MutableStateFlow(false)
|
||||
val available: StateFlow<Boolean> = _available.asStateFlow()
|
||||
|
||||
private val _supportStatus = MutableStateFlow<WifiAwareSupport.Status?>(null)
|
||||
val supportStatus: StateFlow<WifiAwareSupport.Status?> = _supportStatus.asStateFlow()
|
||||
|
||||
private val _running = MutableStateFlow(false)
|
||||
val running: StateFlow<Boolean> = _running.asStateFlow()
|
||||
|
||||
@@ -51,7 +61,12 @@ object WifiAwareController {
|
||||
|
||||
fun initialize(context: Context, enabledByDefault: Boolean) {
|
||||
appContext = context.applicationContext
|
||||
registerAwareStateReceiver(appContext!!)
|
||||
val status = refreshSupportStatus(appContext!!)
|
||||
if (status.supported) {
|
||||
registerAwareStateReceiver(appContext!!)
|
||||
} else {
|
||||
Log.i(TAG, "Wi-Fi Aware unsupported: ${status.reason}")
|
||||
}
|
||||
setEnabled(enabledByDefault)
|
||||
// Start background poller for debug surfacing
|
||||
scope.launch {
|
||||
@@ -95,16 +110,20 @@ object WifiAwareController {
|
||||
synchronized(lifecycleLock) { starting = false }
|
||||
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) {}
|
||||
|
||||
val status = refreshSupportStatus(ctx)
|
||||
if (!status.supported) {
|
||||
val reason = status.reason ?: "not supported"
|
||||
Log.w(TAG, "Wi‑Fi Aware unsupported; not starting ($reason)")
|
||||
addBlockedDebugMessage("unsupported:$reason", "Wi-Fi Aware not supported on this device ($reason)")
|
||||
synchronized(lifecycleLock) { starting = false }
|
||||
return
|
||||
}
|
||||
val awareManager = ctx.getSystemService(android.net.wifi.aware.WifiAwareManager::class.java)
|
||||
if (awareManager == null || !awareManager.isAvailable) {
|
||||
|
||||
val awareManager = WifiAwareSupport.getManager(ctx)
|
||||
if (awareManager == null || !status.available) {
|
||||
Log.w(TAG, "Wi-Fi Aware is not currently available; not starting")
|
||||
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("Wi‑Fi Aware is not available on this device right now")) } catch (_: Exception) {}
|
||||
addBlockedDebugMessage("unavailable", "Wi-Fi Aware is not available right now")
|
||||
synchronized(lifecycleLock) { starting = false }
|
||||
return
|
||||
}
|
||||
@@ -121,7 +140,7 @@ object WifiAwareController {
|
||||
|
||||
if (!locationEnabled) {
|
||||
Log.w(TAG, "Location services are disabled; Wi-Fi Aware cannot start.")
|
||||
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("Enable Location Services to start Wi-Fi Aware")) } catch (_: Exception) {}
|
||||
addBlockedDebugMessage("location-disabled", "Enable Location Services to start Wi-Fi Aware")
|
||||
synchronized(lifecycleLock) { starting = false }
|
||||
return
|
||||
}
|
||||
@@ -131,7 +150,7 @@ object WifiAwareController {
|
||||
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) {}
|
||||
addBlockedDebugMessage("missing-nearby-wifi", "Grant Nearby Wi-Fi Devices to start Wi-Fi Aware")
|
||||
synchronized(lifecycleLock) { starting = false }
|
||||
return
|
||||
}
|
||||
@@ -152,6 +171,7 @@ object WifiAwareController {
|
||||
_running.value = true
|
||||
}
|
||||
try { com.bitchat.android.service.MeshServiceHolder.unifiedMeshService?.refreshDelegates() } catch (_: Exception) { }
|
||||
clearBlockedDebugMessage()
|
||||
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("Wi‑Fi Aware started")) } catch (_: Exception) {}
|
||||
} else {
|
||||
if (reusableService == null) {
|
||||
@@ -216,6 +236,8 @@ object WifiAwareController {
|
||||
if (delayMs > 0L) delay(delayMs)
|
||||
var attempt = 0
|
||||
while (_enabled.value && !_running.value && attempt < MAX_RESTART_ATTEMPTS) {
|
||||
val ctx = appContext
|
||||
if (ctx != null && !refreshSupportStatus(ctx).supported) break
|
||||
startIfPossible()
|
||||
if (_running.value) break
|
||||
attempt++
|
||||
@@ -234,17 +256,16 @@ object WifiAwareController {
|
||||
*/
|
||||
private fun registerAwareStateReceiver(ctx: Context) {
|
||||
if (awareReceiverRegistered) return
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return
|
||||
if (!refreshSupportStatus(ctx).supported) return
|
||||
try {
|
||||
val filter = android.content.IntentFilter(
|
||||
android.net.wifi.aware.WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED
|
||||
)
|
||||
ctx.registerReceiver(object : android.content.BroadcastReceiver() {
|
||||
override fun onReceive(c: Context?, intent: android.content.Intent?) {
|
||||
val mgr = ctx.getSystemService(android.net.wifi.aware.WifiAwareManager::class.java)
|
||||
val available = mgr?.isAvailable == true
|
||||
Log.i(TAG, "Wi-Fi Aware availability changed: available=$available enabled=${_enabled.value} running=${_running.value}")
|
||||
if (available) {
|
||||
val status = refreshSupportStatus(ctx)
|
||||
Log.i(TAG, "Wi-Fi Aware availability changed: supported=${status.supported} available=${status.available} enabled=${_enabled.value} running=${_running.value}")
|
||||
if (status.available) {
|
||||
if (_enabled.value) restartIfStillEnabled(500)
|
||||
} else if (_running.value) {
|
||||
// Aware went away; tear down cleanly so we can re-attach when it returns.
|
||||
@@ -259,5 +280,26 @@ object WifiAwareController {
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshSupportStatus(ctx: Context): WifiAwareSupport.Status {
|
||||
val status = WifiAwareSupport.evaluate(ctx)
|
||||
_supported.value = status.supported
|
||||
_available.value = status.available
|
||||
_supportStatus.value = status
|
||||
return status
|
||||
}
|
||||
|
||||
private fun addBlockedDebugMessage(key: String, message: String) {
|
||||
if (lastBlockedReason == key) return
|
||||
lastBlockedReason = key
|
||||
try {
|
||||
com.bitchat.android.ui.debug.DebugSettingsManager.getInstance()
|
||||
.addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage(message))
|
||||
} catch (_: Exception) { }
|
||||
}
|
||||
|
||||
private fun clearBlockedDebugMessage() {
|
||||
lastBlockedReason = null
|
||||
}
|
||||
|
||||
fun getService(): WifiAwareMeshService? = service
|
||||
}
|
||||
|
||||
@@ -318,6 +318,15 @@ class WifiAwareMeshService(private val context: Context) : MeshService, Transpor
|
||||
Log.i(TAG, "Wi-Fi Aware transport disabled by debug settings; not starting")
|
||||
return
|
||||
}
|
||||
val supportStatus = com.bitchat.android.wifiaware.WifiAwareSupport.evaluate(context)
|
||||
if (!supportStatus.supported) {
|
||||
Log.i(TAG, "Wi-Fi Aware unsupported on this device; not starting (${supportStatus.reason})")
|
||||
return
|
||||
}
|
||||
if (!supportStatus.available) {
|
||||
Log.i(TAG, "Wi-Fi Aware unavailable right now; not starting (${supportStatus.reason})")
|
||||
return
|
||||
}
|
||||
if (recoveryInProgress) {
|
||||
Log.i(TAG, "Wi-Fi Aware recovery cleanup still in progress; deferring start")
|
||||
return
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.bitchat.android.wifiaware
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.wifi.aware.WifiAwareManager
|
||||
import android.os.Build
|
||||
|
||||
/**
|
||||
* Centralized Wi-Fi Aware capability checks.
|
||||
*
|
||||
* "Supported" is stable device/API capability. "Available" is runtime state and can change
|
||||
* when Wi-Fi, location, airplane mode, or system radio state changes.
|
||||
*/
|
||||
object WifiAwareSupport {
|
||||
data class Status(
|
||||
val supported: Boolean,
|
||||
val available: Boolean,
|
||||
val reason: String? = null
|
||||
)
|
||||
|
||||
fun evaluate(context: Context): Status {
|
||||
val appContext = context.applicationContext
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
return Status(
|
||||
supported = false,
|
||||
available = false,
|
||||
reason = "requires Android 10+"
|
||||
)
|
||||
}
|
||||
|
||||
val hasFeature = try {
|
||||
appContext.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
if (!hasFeature) {
|
||||
return Status(
|
||||
supported = false,
|
||||
available = false,
|
||||
reason = "device does not advertise Wi-Fi Aware support"
|
||||
)
|
||||
}
|
||||
|
||||
val manager = getManager(appContext)
|
||||
?: return Status(
|
||||
supported = false,
|
||||
available = false,
|
||||
reason = "WifiAwareManager unavailable"
|
||||
)
|
||||
|
||||
val available = try {
|
||||
manager.isAvailable
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
return Status(
|
||||
supported = true,
|
||||
available = available,
|
||||
reason = if (available) null else "Wi-Fi Aware temporarily unavailable"
|
||||
)
|
||||
}
|
||||
|
||||
fun isSupported(context: Context): Boolean = evaluate(context).supported
|
||||
|
||||
fun getManager(context: Context): WifiAwareManager? {
|
||||
return try {
|
||||
context.applicationContext.getSystemService(WifiAwareManager::class.java)
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user