Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 66 additions & 3 deletions app/src/main/java/com/sameerasw/essentials/domain/diy/Action.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ sealed interface Action {
) : Action {
override val title: Int get() = R.string.diy_action_dim_wallpaper
override val icon: Int get() = R.drawable.rounded_mobile_screensaver_24
override val permissions: List<String> = listOf("shizuku", "root")
override val permissions: List<String> = listOf("SHIZUKU", "ROOT")
override val isConfigurable: Boolean = true
}

Expand All @@ -76,7 +76,7 @@ sealed interface Action {
) : Action {
override val title: Int get() = R.string.diy_action_device_effects
override val icon: Int get() = R.drawable.rounded_bed_24
override val permissions: List<String> = listOf("notification_policy")
override val permissions: List<String> = listOf("NOTIFICATION_POLICY")
override val isConfigurable: Boolean = true
}

Expand All @@ -103,7 +103,7 @@ sealed interface Action {
SoundModeType.VIBRATE -> R.drawable.rounded_mobile_vibrate_24
SoundModeType.SILENT -> R.drawable.rounded_volume_off_24
}
override val permissions: List<String> = listOf("notification_policy")
override val permissions: List<String> = listOf("NOTIFICATION_POLICY")
override val isConfigurable: Boolean = true
}

Expand Down Expand Up @@ -187,4 +187,67 @@ sealed interface Action {
override val icon: Int = R.drawable.rounded_shield_lock_24
override val permissions: List<String> = listOf("shizuku", "root")
}

@Keep
data object TurnOnWifi : Action {
override val title: Int = R.string.diy_action_wifi_on
override val icon: Int = R.drawable.rounded_android_wifi_4_bar_plus_24
override val permissions: List<String> = listOf("SHIZUKU", "ROOT")
}

@Keep
data object TurnOffWifi : Action {
override val title: Int = R.string.diy_action_wifi_off
override val icon: Int = R.drawable.rounded_android_wifi_4_bar_plus_24
override val permissions: List<String> = listOf("SHIZUKU", "ROOT")
}

@Keep
data object TurnOnCellularData : Action {
override val title: Int = R.string.diy_action_cellular_on
override val icon: Int = R.drawable.rounded_signal_cellular_alt_24
override val permissions: List<String> = listOf("SHIZUKU", "ROOT")
}

@Keep
data object TurnOffCellularData : Action {
override val title: Int = R.string.diy_action_cellular_off
override val icon: Int = R.drawable.rounded_signal_cellular_alt_24
override val permissions: List<String> = listOf("SHIZUKU", "ROOT")
}

@Keep
data object TurnOnAutoBrightness : Action {
override val title: Int = R.string.diy_action_auto_brightness_on
override val icon: Int = R.drawable.rounded_brightness_auto_24
override val permissions: List<String> = listOf("WRITE_SETTINGS")
}

@Keep
data object TurnOffAutoBrightness : Action {
override val title: Int = R.string.diy_action_auto_brightness_off
override val icon: Int = R.drawable.rounded_brightness_auto_24
override val permissions: List<String> = listOf("WRITE_SETTINGS")
}

@Keep
data class FreezeApps(
@SerializedName("packageNames") val packageNames: List<String> = emptyList()
) : Action {
override val title: Int get() = R.string.diy_action_freeze_apps
override val icon: Int get() = R.drawable.rounded_mode_cool_24
override val permissions: List<String> = listOf("SHIZUKU", "ROOT")
override val isConfigurable: Boolean = true
}

@Keep
data class UnfreezeApps(
@SerializedName("packageNames") val packageNames: List<String> = emptyList()
) : Action {
override val title: Int get() = R.string.diy_action_unfreeze_apps
override val icon: Int get() = R.drawable.rounded_mode_cool_off_24
override val permissions: List<String> = listOf("SHIZUKU", "ROOT")
override val isConfigurable: Boolean = true
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,25 @@ object CombinedActionExecutor {
Toast.makeText(context, "Failed to pin app: ${e.message}", Toast.LENGTH_SHORT).show()
}
}

is Action.TurnOnWifi -> setWifiEnabled(context, true)
is Action.TurnOffWifi -> setWifiEnabled(context, false)
is Action.TurnOnCellularData -> setCellularDataEnabled(context, true)
is Action.TurnOffCellularData -> setCellularDataEnabled(context, false)
is Action.TurnOnAutoBrightness -> setAutoBrightnessEnabled(context, true)
is Action.TurnOffAutoBrightness -> setAutoBrightnessEnabled(context, false)
is Action.FreezeApps -> {
action.packageNames.forEach { pkg ->
com.sameerasw.essentials.utils.FreezeManager.freezeApp(context, pkg)
}
}
is Action.UnfreezeApps -> {
action.packageNames.forEach { pkg ->
com.sameerasw.essentials.utils.FreezeManager.unfreezeApp(context, pkg)
}
}
}

}
}

Expand All @@ -420,4 +438,30 @@ object CombinedActionExecutor {
e.printStackTrace()
}
}

private fun setWifiEnabled(context: Context, enabled: Boolean) {
val state = if (enabled) "enable" else "disable"
com.sameerasw.essentials.utils.ShellUtils.runCommand(context, "svc wifi $state")
}

private fun setCellularDataEnabled(context: Context, enabled: Boolean) {
val state = if (enabled) "enable" else "disable"
com.sameerasw.essentials.utils.ShellUtils.runCommand(context, "svc data $state")
}

private fun setAutoBrightnessEnabled(context: Context, enabled: Boolean) {
val value = if (enabled) 1 else 0
try {
android.provider.Settings.System.putInt(
context.contentResolver,
android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE,
value
)
} catch (e: Exception) {
com.sameerasw.essentials.utils.ShellUtils.runCommand(
context,
"settings put system screen_brightness_mode $value"
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.accessibilityservice.AccessibilityService
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.media.AudioManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
Expand All @@ -14,6 +15,7 @@ import android.os.Looper
import android.provider.Settings
import android.util.Log
import androidx.core.app.NotificationCompat
import android.view.inputmethod.InputMethodManager
import com.google.gson.Gson
import com.sameerasw.essentials.domain.diy.Automation
import com.sameerasw.essentials.domain.diy.DIYRepository
Expand Down Expand Up @@ -103,13 +105,62 @@ class AppFlowHandler(
private val ignoredSystemPackages = listOf(
"android",
"com.android.systemui",
"com.google.android.inputmethod.latin"
"com.google.android.inputmethod.latin",
"com.google.android.gms"
)

private fun isIgnoredPackage(packageName: String): Boolean {
if (ignoredSystemPackages.contains(packageName)) return true

val lowerPkg = packageName.lowercase()
if (lowerPkg.contains("systemui") ||
lowerPkg.contains("keyguard") ||
lowerPkg.contains("volume") ||
lowerPkg.contains("soundassistant") ||
lowerPkg.contains("dialer") ||
lowerPkg.contains("telecom") ||
lowerPkg.contains("phone") ||
lowerPkg.contains("incallui") ||
lowerPkg.contains("packageinstaller") ||
lowerPkg.contains("permissioncontroller")
) {
return true
}

// Check active call state via AudioManager mode
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
if (audioManager != null) {
val mode = audioManager.mode
if (mode == AudioManager.MODE_IN_CALL ||
mode == AudioManager.MODE_IN_COMMUNICATION ||
mode == AudioManager.MODE_RINGTONE
) {
return true
}
}

return try {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
val ims = imm?.enabledInputMethodList
ims?.any { it.packageName == packageName } == true
} catch (_: Exception) {
false
}
}

fun onPackageChanged(packageName: String, isFromUsageStats: Boolean = false) {
val prefs = context.getSharedPreferences("essentials_prefs", Context.MODE_PRIVATE)
val useUsageAccess = prefs.getBoolean("use_usage_access", false)

Log.d("AppFlowHandler", "onPackageChanged: packageName=$packageName, isFromUsageStats=$isFromUsageStats, useUsageAccess=$useUsageAccess, currentPackage=$currentPackage")

// If the new foreground window belongs to a system overlay (status bar, quick settings,
// notifications), a keyboard (IME), a volume dialog, or a phone call, completely ignore it.
// We do NOT update currentPackage so that state-dependent features remain stable.
if (isIgnoredPackage(packageName)) {
Log.d("AppFlowHandler", "onPackageChanged: Ignoring system/IME/volume/call package $packageName")
return
}
val oldPackage = currentPackage
if (isFromUsageStats == useUsageAccess) {
currentPackage = packageName
Expand Down Expand Up @@ -213,7 +264,7 @@ class AppFlowHandler(

pendingNLRunnable?.let { handler.removeCallbacks(it) }

if (ignoredSystemPackages.contains(packageName)) {
if (isIgnoredPackage(packageName)) {
Log.d("NightLight", "Ignoring system package $packageName")
return
}
Expand Down Expand Up @@ -285,6 +336,10 @@ class AppFlowHandler(
}

private fun checkAppAutomations(packageName: String) {
if (isIgnoredPackage(packageName)) {
Log.d("AppFlowHandler", "checkAppAutomations: Ignoring system/IME package $packageName")
return
}
scope.launch {
val automations = DIYRepository.automations.value
val appAutomations =
Expand Down
Loading