Skip to content

Commit 3e21787

Browse files
committed
fix: make WiFi connected constraints more reliable
1 parent e02ffbf commit 3e21787

7 files changed

Lines changed: 76 additions & 69 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424

2525
## Fixed
2626

27-
- Restoring subgroups works and does not freeze Key Mapper
28-
- Do not show duplicate constraint shortcuts
27+
- Restoring subgroups works and does not freeze Key Mapper.
28+
- Do not show duplicate constraint shortcuts.
29+
- Make WiFi connected constraints more reliable
2930

3031
## [3.2.1](https://github.com/sds100/KeyMapper/releases/tag/v3.2.1)
3132

base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintViewModel.kt

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,14 @@ class ChooseConstraintViewModel @Inject constructor(
137137
ConstraintId.APP_NOT_IN_FOREGROUND,
138138
ConstraintId.APP_PLAYING_MEDIA,
139139
ConstraintId.APP_NOT_PLAYING_MEDIA,
140-
-> onSelectAppConstraint(constraintType)
140+
-> onSelectAppConstraint(constraintType)
141141

142142
ConstraintId.MEDIA_PLAYING -> returnResult.emit(ConstraintData.MediaPlaying)
143143
ConstraintId.MEDIA_NOT_PLAYING -> returnResult.emit(ConstraintData.NoMediaPlaying)
144144

145145
ConstraintId.BT_DEVICE_CONNECTED,
146146
ConstraintId.BT_DEVICE_DISCONNECTED,
147-
-> onSelectBluetoothConstraint(
147+
-> onSelectBluetoothConstraint(
148148
constraintType,
149149
)
150150

@@ -185,13 +185,13 @@ class ChooseConstraintViewModel @Inject constructor(
185185

186186
ConstraintId.WIFI_CONNECTED,
187187
ConstraintId.WIFI_DISCONNECTED,
188-
-> onSelectWifiConnectedConstraint(
188+
-> onSelectWifiConnectedConstraint(
189189
constraintType,
190190
)
191191

192192
ConstraintId.IME_CHOSEN,
193193
ConstraintId.IME_NOT_CHOSEN,
194-
-> onSelectImeChosenConstraint(constraintType)
194+
-> onSelectImeChosenConstraint(constraintType)
195195

196196
ConstraintId.DEVICE_IS_LOCKED ->
197197
returnResult.emit(ConstraintData.DeviceIsLocked)
@@ -272,45 +272,32 @@ class ChooseConstraintViewModel @Inject constructor(
272272
}
273273

274274
private suspend fun onSelectWifiConnectedConstraint(type: ConstraintId) {
275-
val knownSSIDs = useCase.getKnownWiFiSSIDs()
275+
val knownSSIDs: List<String> = useCase.getKnownWiFiSSIDs()
276276

277277
val chosenSSID: String?
278278

279-
if (knownSSIDs == null) {
280-
val savedWifiSSIDs = useCase.getSavedWifiSSIDs().first()
279+
val savedWifiSSIDs: List<String> = useCase.getSavedWifiSSIDs().first()
281280

282-
val dialog = DialogModel.Text(
283-
hint = getString(R.string.hint_wifi_ssid),
284-
allowEmpty = true,
285-
message = getString(R.string.constraint_wifi_message_cant_list_networks),
286-
autoCompleteEntries = savedWifiSSIDs,
287-
)
281+
val ssidEntries = buildList {
282+
addAll(savedWifiSSIDs)
283+
addAll(knownSSIDs)
284+
}.distinct()
288285

289-
val ssidText = showDialog("type_ssid", dialog) ?: return
286+
val dialog = DialogModel.Text(
287+
hint = getString(R.string.hint_wifi_ssid),
288+
allowEmpty = true,
289+
message = getString(R.string.constraint_wifi_message_cant_list_networks),
290+
autoCompleteEntries = ssidEntries,
291+
)
290292

291-
if (ssidText.isBlank()) {
292-
chosenSSID = null
293-
} else {
294-
chosenSSID = ssidText
293+
val ssidText = showDialog("type_ssid", dialog) ?: return
295294

296-
useCase.saveWifiSSID(chosenSSID)
297-
}
295+
if (ssidText.isBlank()) {
296+
chosenSSID = null
298297
} else {
299-
val anySSIDItem =
300-
"any" to getString(R.string.constraint_wifi_pick_network_any)
301-
302-
val ssidItems = knownSSIDs.map { "ssid_$it" to it }
298+
chosenSSID = ssidText
303299

304-
val items = listOf(anySSIDItem).plus(ssidItems)
305-
306-
val chosenItem =
307-
showDialog("choose_ssid", DialogModel.SingleChoice(items)) ?: return
308-
309-
if (chosenItem == anySSIDItem.first) {
310-
chosenSSID = null
311-
} else {
312-
chosenSSID = items.single { it.first == chosenItem }.second
313-
}
300+
useCase.saveWifiSSID(chosenSSID)
314301
}
315302

316303
when (type) {

base/src/main/java/io/github/sds100/keymapper/base/constraints/CreateConstraintUseCase.kt

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.github.sds100.keymapper.base.constraints
22

33
import android.content.pm.PackageManager
4-
import android.os.Build
54
import io.github.sds100.keymapper.common.utils.KMError
65
import io.github.sds100.keymapper.data.Keys
76
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
@@ -25,29 +24,19 @@ class CreateConstraintUseCaseImpl @Inject constructor(
2524
override fun isSupported(constraint: ConstraintId): KMError? {
2625
when (constraint) {
2726
ConstraintId.FLASHLIGHT_ON, ConstraintId.FLASHLIGHT_OFF -> {
28-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
29-
return KMError.SdkVersionTooLow(minSdk = Build.VERSION_CODES.M)
30-
}
31-
3227
if (cameraAdapter.getFlashInfo(CameraLens.BACK) == null &&
3328
cameraAdapter.getFlashInfo(CameraLens.FRONT) == null
3429
) {
3530
return KMError.SystemFeatureNotSupported(PackageManager.FEATURE_CAMERA_FLASH)
3631
}
3732
}
38-
39-
ConstraintId.DEVICE_IS_LOCKED, ConstraintId.DEVICE_IS_UNLOCKED ->
40-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
41-
return KMError.SdkVersionTooLow(minSdk = Build.VERSION_CODES.LOLLIPOP_MR1)
42-
}
43-
4433
else -> Unit
4534
}
4635

4736
return null
4837
}
4938

50-
override fun getKnownWiFiSSIDs(): List<String>? = networkAdapter.getKnownWifiSSIDs()
39+
override fun getKnownWiFiSSIDs(): List<String> = networkAdapter.getKnownWifiSSIDs()
5140

5241
override fun getEnabledInputMethods(): List<ImeInfo> = inputMethodAdapter.inputMethods.value
5342

@@ -82,7 +71,7 @@ class CreateConstraintUseCaseImpl @Inject constructor(
8271

8372
interface CreateConstraintUseCase {
8473
fun isSupported(constraint: ConstraintId): KMError?
85-
fun getKnownWiFiSSIDs(): List<String>?
74+
fun getKnownWiFiSSIDs(): List<String>
8675
fun getEnabledInputMethods(): List<ImeInfo>
8776

8877
suspend fun saveWifiSSID(ssid: String)

base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.github.sds100.keymapper.base.constraints
22

3-
import android.os.Build
43
import dagger.assisted.Assisted
54
import dagger.assisted.AssistedFactory
65
import dagger.assisted.AssistedInject
@@ -16,7 +15,6 @@ import io.github.sds100.keymapper.system.network.NetworkAdapter
1615
import io.github.sds100.keymapper.system.phone.PhoneAdapter
1716
import io.github.sds100.keymapper.system.power.PowerAdapter
1817
import kotlinx.coroutines.flow.Flow
19-
import kotlinx.coroutines.flow.emptyFlow
2018
import kotlinx.coroutines.flow.map
2119
import kotlinx.coroutines.flow.merge
2220

@@ -74,11 +72,8 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor(
7472
ConstraintDependency.WIFI_SSID -> networkAdapter.connectedWifiSSIDFlow.map { dependency }
7573
ConstraintDependency.WIFI_STATE -> networkAdapter.isWifiEnabledFlow().map { dependency }
7674
ConstraintDependency.CHOSEN_IME -> inputMethodAdapter.chosenIme.map { dependency }
77-
ConstraintDependency.DEVICE_LOCKED_STATE -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
75+
ConstraintDependency.DEVICE_LOCKED_STATE ->
7876
lockScreenAdapter.isLockedFlow().map { dependency }
79-
} else {
80-
emptyFlow()
81-
}
8277

8378
ConstraintDependency.LOCK_SCREEN_SHOWING ->
8479
merge(

base/src/main/res/values/strings.xml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,7 @@
259259
<string name="constraint_wifi_off">WiFi is off</string>
260260
<string name="constraint_wifi_connected">Connected to a WiFi network</string>
261261
<string name="constraint_wifi_disconnected">Disconnected from a WiFi network</string>
262-
<string name="constraint_wifi_message_cant_list_networks">You will have to type the SSID manually because apps aren\'t allowed to query the list of known WiFi networks on Android 10 and newer.
263-
264-
Leave it empty if any WiFi network should be matched.</string>
265-
<string name="constraint_wifi_pick_network_any">Any</string>
262+
<string name="constraint_wifi_message_cant_list_networks">Leave it empty if any WiFi network should be matched.</string>
266263
<string name="constraint_wifi_connected_description">Connected to %s WiFi</string>
267264
<string name="constraint_wifi_disconnected_description">Disconnected from %s WiFi</string>
268265
<string name="constraint_wifi_connected_any_description">Connected to any WiFi</string>

system/src/main/java/io/github/sds100/keymapper/system/network/AndroidNetworkAdapter.kt

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.github.sds100.keymapper.system.network
22

3+
import android.annotation.SuppressLint
34
import android.content.ActivityNotFoundException
45
import android.content.BroadcastReceiver
56
import android.content.Context
@@ -9,11 +10,13 @@ import android.net.ConnectivityManager
910
import android.net.Network
1011
import android.net.NetworkCapabilities
1112
import android.net.NetworkRequest
13+
import android.net.wifi.WifiInfo
1214
import android.net.wifi.WifiManager
1315
import android.os.Build
1416
import android.provider.Settings
1517
import android.telephony.SubscriptionManager
1618
import android.telephony.TelephonyManager
19+
import androidx.annotation.RequiresApi
1720
import androidx.core.content.ContextCompat
1821
import androidx.core.content.getSystemService
1922
import dagger.hilt.android.qualifiers.ApplicationContext
@@ -74,10 +77,14 @@ class AndroidNetworkAdapter @Inject constructor(
7477

7578
private val isWifiEnabled = MutableStateFlow(isWifiEnabled())
7679

77-
private val networkCallback: ConnectivityManager.NetworkCallback =
78-
object : ConnectivityManager.NetworkCallback() {
80+
// This requires Android S+ because of the FLAG_INCLUDE_LOCATION_INFO, which is needed
81+
// to get the SSID.
82+
private val networkCallback: ConnectivityManager.NetworkCallback by lazy {
83+
@RequiresApi(Build.VERSION_CODES.S)
84+
object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
7985
override fun onAvailable(network: Network) {
8086
super.onAvailable(network)
87+
8188
isWifiConnected.update { getIsWifiConnected() }
8289
}
8390

@@ -88,6 +95,7 @@ class AndroidNetworkAdapter @Inject constructor(
8895
// If multiple Wi-Fi networks were available and one is lost,
8996
// another might still be active.
9097
isWifiConnected.update { getIsWifiConnected() }
98+
connectedWifiSSIDFlow.update { getWifiSSID() }
9199
}
92100

93101
override fun onCapabilitiesChanged(
@@ -97,8 +105,23 @@ class AndroidNetworkAdapter @Inject constructor(
97105
super.onCapabilitiesChanged(network, networkCapabilities)
98106

99107
isWifiConnected.update { getIsWifiConnected() }
108+
109+
val wifiInfo = networkCapabilities.transportInfo as? WifiInfo
110+
111+
// Do nothing if this network is not a wifi network
112+
if (wifiInfo == null) {
113+
return
114+
}
115+
116+
Timber.i("onCapabilitiesChanged, found SSID: ${wifiInfo.ssid}")
117+
118+
val ssid = wifiInfo.ssid?.takeIf { it != WifiManager.UNKNOWN_SSID }
119+
?.removeSurrounding("\"")
120+
121+
connectedWifiSSIDFlow.update { ssid }
100122
}
101123
}
124+
}
102125

103126
init {
104127
IntentFilter().apply {
@@ -113,11 +136,13 @@ class AndroidNetworkAdapter @Inject constructor(
113136
)
114137
}
115138

116-
val networkRequest = NetworkRequest.Builder()
117-
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
118-
.build()
139+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
140+
val networkRequest = NetworkRequest.Builder()
141+
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
142+
.build()
119143

120-
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
144+
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
145+
}
121146
}
122147

123148
override fun isWifiEnabled(): Boolean = wifiManager.isWifiEnabled
@@ -142,6 +167,7 @@ class AndroidNetworkAdapter @Inject constructor(
142167
}
143168
}
144169

170+
@SuppressLint("MissingPermission")
145171
override fun isMobileDataEnabled(): Boolean {
146172
return telephonyManager.isDataEnabled
147173
}
@@ -177,10 +203,20 @@ class AndroidNetworkAdapter @Inject constructor(
177203
/**
178204
* @return Null on Android 10+ because there is no API to do this anymore.
179205
*/
180-
override fun getKnownWifiSSIDs(): List<String>? {
206+
@SuppressLint("MissingPermission")
207+
override fun getKnownWifiSSIDs(): List<String> {
181208
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
182-
return null
209+
// On Android Q and above, apps can't access the list of configured Wi-Fi networks.
210+
// As a fallback, we return the currently connected network's SSID.
211+
val ssid = getWifiSSID()
212+
213+
return if (ssid != null) {
214+
listOf(ssid)
215+
} else {
216+
emptyList()
217+
}
183218
} else {
219+
@Suppress("DEPRECATION")
184220
return wifiManager.configuredNetworks?.map {
185221
it.SSID.removeSurrounding("\"")
186222
} ?: emptyList()
@@ -250,6 +286,9 @@ class AndroidNetworkAdapter @Inject constructor(
250286
}
251287

252288
private fun getIsWifiConnected(): Boolean {
289+
@Suppress("DEPRECATION")
290+
// The deprecation notice is advice to use the callback instead. getAllNetworks() still
291+
// functions
253292
return connectivityManager.allNetworks.any { network ->
254293
connectivityManager.getNetworkCapabilities(network)
255294
?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ?: false

system/src/main/java/io/github/sds100/keymapper/system/network/NetworkAdapter.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import kotlinx.coroutines.flow.Flow
66

77
interface NetworkAdapter {
88
val connectedWifiSSIDFlow: Flow<String?>
9-
109
val isWifiConnected: Flow<Boolean>
1110

1211
fun isWifiEnabled(): Boolean
@@ -21,7 +20,7 @@ interface NetworkAdapter {
2120
fun enableMobileData(): KMResult<*>
2221
fun disableMobileData(): KMResult<*>
2322

24-
fun getKnownWifiSSIDs(): List<String>?
23+
fun getKnownWifiSSIDs(): List<String>
2524

2625
suspend fun sendHttpRequest(
2726
method: HttpMethod,

0 commit comments

Comments
 (0)