diff --git a/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt b/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt
index cd9f0ab0c..081b240ed 100644
--- a/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt
+++ b/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt
@@ -384,6 +384,37 @@ object FeatureRegistry {
viewModel.setWirelessDisplayCertificationEnabled(enabled, context)
},
+ object : Feature(
+ id = "Auto turn off Wi-Fi",
+ title = R.string.wifi_auto_off_title,
+ iconRes = R.drawable.rounded_power_settings_new_24,
+ category = R.string.cat_connectivity,
+ description = R.string.wifi_auto_off_desc,
+ permissionKeys = listOf("ACCESSIBILITY"),
+ searchableSettings = listOf(
+ SearchSetting(
+ R.string.search_wifi_auto_off_title,
+ R.string.search_wifi_auto_off_desc,
+ "wifi_auto_off_toggle"
+ ),
+ SearchSetting(
+ R.string.search_wifi_auto_off_timeout_title,
+ R.string.search_wifi_auto_off_timeout_desc,
+ "wifi_auto_off_timeout_slider"
+ )
+ ),
+ parentFeatureId = "Networks"
+ ) {
+ override fun isEnabled(viewModel: MainViewModel) =
+ viewModel.isWifiAutoOffEnabled.value
+
+ override fun isToggleEnabled(viewModel: MainViewModel, context: Context) =
+ viewModel.isAccessibilityEnabled.value
+
+ override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) =
+ viewModel.setWifiAutoOffEnabled(enabled)
+ },
+
object : Feature(
id = "Watch",
title = R.string.feat_watch_title,
diff --git a/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt b/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt
index 363d44654..c05fa4dda 100644
--- a/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt
+++ b/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt
@@ -152,6 +152,14 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene
}
}
+ private var isWifiConnected = false
+ private var networkCallback: android.net.ConnectivityManager.NetworkCallback? = null
+ private val wifiAutoOffHandler = Handler(Looper.getMainLooper())
+ private val wifiAutoOffRunnable = Runnable {
+ android.util.Log.d("ScreenOffService", "Executing Wifi auto turn off")
+ com.sameerasw.essentials.utils.ShellUtils.runCommand(this, "svc wifi disable")
+ }
+
private fun schedulePocketFlashlightTurnOff() {
pocketFlashlightHandler.removeCallbacks(pocketFlashlightRunnable)
pocketFlashlightHandler.postDelayed(pocketFlashlightRunnable, 1500L)
@@ -213,6 +221,13 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene
updatePocketModeExcludedAppsSet()
} else if (key == SettingsRepository.KEY_SMART_PIXELS_ENABLED || key == SettingsRepository.KEY_SMART_PIXELS_INTENSITY) {
smartPixelsHandler.updateState()
+ } else if (key == "wifi_auto_off_enabled" || key == "wifi_auto_off_timeout") {
+ val isAutoOffEnabled = prefs.getBoolean("wifi_auto_off_enabled", false)
+ if (!isAutoOffEnabled) {
+ wifiAutoOffHandler.removeCallbacks(wifiAutoOffRunnable)
+ } else if (!isWifiConnected) {
+ checkAndScheduleWifiAutoOff()
+ }
}
}
@@ -235,6 +250,7 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene
flashlightHandler.register()
statusBarIconHandler.register()
smartPixelsHandler.init()
+ registerWifiNetworkCallback()
// Screen Receiver
screenReceiver = object : BroadcastReceiver() {
@@ -396,9 +412,83 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene
getSharedPreferences("essentials_prefs", MODE_PRIVATE)
.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener)
instance = null
+ unregisterWifiNetworkCallback()
super.onDestroy()
}
+ private fun registerWifiNetworkCallback() {
+ val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as? android.net.ConnectivityManager ?: return
+
+ isWifiConnected = isWifiCurrentlyConnected(connectivityManager)
+
+ val networkRequest = android.net.NetworkRequest.Builder()
+ .addTransportType(android.net.NetworkCapabilities.TRANSPORT_WIFI)
+ .build()
+
+ val callback = object : android.net.ConnectivityManager.NetworkCallback() {
+ override fun onAvailable(network: android.net.Network) {
+ super.onAvailable(network)
+ isWifiConnected = true
+ wifiAutoOffHandler.removeCallbacks(wifiAutoOffRunnable)
+ android.util.Log.d("ScreenOffService", "Wi-Fi Connected - Cancelled auto off timer")
+ }
+
+ override fun onLost(network: android.net.Network) {
+ super.onLost(network)
+ isWifiConnected = false
+ checkAndScheduleWifiAutoOff()
+ }
+ }
+
+ networkCallback = callback
+ try {
+ connectivityManager.registerNetworkCallback(networkRequest, callback)
+ } catch (e: Exception) {
+ android.util.Log.e("ScreenOffService", "Failed to register network callback", e)
+ }
+ }
+
+ private fun unregisterWifiNetworkCallback() {
+ wifiAutoOffHandler.removeCallbacks(wifiAutoOffRunnable)
+ val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as? android.net.ConnectivityManager
+ networkCallback?.let {
+ try {
+ connectivityManager?.unregisterNetworkCallback(it)
+ } catch (e: Exception) {
+ // Ignore
+ }
+ }
+ networkCallback = null
+ }
+
+ private fun isWifiCurrentlyConnected(connectivityManager: android.net.ConnectivityManager): Boolean {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
+ val activeNetwork = connectivityManager.activeNetwork ?: return false
+ val capabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
+ return capabilities.hasTransport(android.net.NetworkCapabilities.TRANSPORT_WIFI)
+ } else {
+ @Suppress("DEPRECATION")
+ val activeNetworkInfo = connectivityManager.activeNetworkInfo
+ @Suppress("DEPRECATION")
+ return activeNetworkInfo != null && activeNetworkInfo.type == android.net.ConnectivityManager.TYPE_WIFI && activeNetworkInfo.isConnected
+ }
+ }
+
+ private fun checkAndScheduleWifiAutoOff() {
+ val isAutoOffEnabled = prefs.getBoolean("wifi_auto_off_enabled", false)
+ if (!isAutoOffEnabled) return
+
+ val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as? android.net.wifi.WifiManager
+ if (wifiManager?.isWifiEnabled != true) return
+
+ val timeoutSeconds = prefs.getFloat("wifi_auto_off_timeout", 60f)
+ val delayMs = (timeoutSeconds * 1000).toLong()
+
+ wifiAutoOffHandler.removeCallbacks(wifiAutoOffRunnable)
+ wifiAutoOffHandler.postDelayed(wifiAutoOffRunnable, delayMs)
+ android.util.Log.d("ScreenOffService", "Scheduled Wi-Fi auto turn off in $timeoutSeconds seconds")
+ }
+
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
if (event == null) return
diff --git a/app/src/main/java/com/sameerasw/essentials/ui/features/network/NetworksSettingsUI.kt b/app/src/main/java/com/sameerasw/essentials/ui/features/network/NetworksSettingsUI.kt
index e6ff1dd25..d7e65dd0c 100644
--- a/app/src/main/java/com/sameerasw/essentials/ui/features/network/NetworksSettingsUI.kt
+++ b/app/src/main/java/com/sameerasw/essentials/ui/features/network/NetworksSettingsUI.kt
@@ -248,6 +248,33 @@ fun NetworksSettingsUI(
iconRes = R.drawable.rounded_cast_24,
modifier = Modifier.highlight(highlightSetting == "wireless_display_certification_toggle")
)
+
+ IconToggleItem(
+ title = stringResource(R.string.wifi_auto_off_title),
+ description = stringResource(R.string.wifi_auto_off_desc),
+ isChecked = viewModel.isWifiAutoOffEnabled.value,
+ onCheckedChange = { enabled ->
+ viewModel.setWifiAutoOffEnabled(enabled)
+ },
+ enabled = true,
+ iconRes = R.drawable.rounded_power_settings_new_24,
+ modifier = Modifier.highlight(highlightSetting == "wifi_auto_off_toggle")
+ )
+
+ ConfigSliderItem(
+ title = stringResource(R.string.wifi_auto_off_timeout_title),
+ value = viewModel.wifiAutoOffTimeout.floatValue,
+ onValueChange = { seconds ->
+ viewModel.setWifiAutoOffTimeout(seconds)
+ },
+ valueRange = 10f..300f,
+ steps = 29,
+ increment = 10f,
+ valueFormatter = { "${it.toInt()}s" },
+ enabled = viewModel.isWifiAutoOffEnabled.value,
+ iconRes = R.drawable.rounded_timer_24,
+ modifier = Modifier.highlight(highlightSetting == "wifi_auto_off_timeout_slider")
+ )
}
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9a4316f02..f088d203a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -422,6 +422,15 @@
Smart WiFi
Hide mobile data when WiFi is connected
Hide mobile data in certain modes
+
+ Auto Turn Off Wi-Fi
+ Automatically turn off Wi-Fi when disconnected from any network
+ Auto Turn Off Timeout
+ Delay before Wi-Fi turns off: %1$d seconds
+ Auto Turn Off Wi-Fi
+ Automatically turn off Wi-Fi when disconnected from network
+ Wi-Fi auto turn off timeout
+ Adjust delay before Wi-Fi is automatically disabled
Reset All Icons
More Settings
Advanced (Requires Shizuku/Root)