Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import com.sameerasw.essentials.domain.diy.Action
import com.sameerasw.essentials.domain.diy.ActionGsonAdapter
import com.sameerasw.essentials.domain.model.AppIcon
import com.sameerasw.essentials.domain.model.AppSelection
import com.sameerasw.essentials.domain.model.AutoDismissAppConfig
import com.sameerasw.essentials.domain.model.AutoDismissChannel
import com.sameerasw.essentials.domain.model.AutoDismissStat
import com.sameerasw.essentials.domain.model.BackGestureAppConfig
import com.sameerasw.essentials.domain.model.AppTag
import com.sameerasw.essentials.domain.model.DnsPreset
Expand Down Expand Up @@ -270,6 +273,11 @@ class SettingsRepository(
const val KEY_SNOOZE_DISCOVERED_CHANNELS = "snooze_discovered_channels"
const val KEY_SNOOZE_BLOCKED_CHANNELS = "snooze_blocked_channels"
const val KEY_SNOOZE_HEADS_UP_ENABLED = "snooze_heads_up_enabled"
const val KEY_AUTO_DISMISS_ENABLED = "auto_dismiss_notifications_enabled"
const val KEY_AUTO_DISMISS_APPS = "auto_dismiss_notifications_apps"
const val KEY_AUTO_DISMISS_LAST_MINUTES = "auto_dismiss_notifications_last_minutes"
const val KEY_AUTO_DISMISS_CHANNELS = "auto_dismiss_notifications_channels"
const val KEY_AUTO_DISMISS_STATS = "auto_dismiss_notifications_stats"

const val KEY_FLASHLIGHT_ALWAYS_TURN_OFF_ENABLED = "flashlight_always_turn_off_enabled"
const val KEY_FLASHLIGHT_FADE_ENABLED = "flashlight_fade_enabled"
Expand Down Expand Up @@ -1635,6 +1643,86 @@ class SettingsRepository(
enabled: Boolean,
) = updateAppSelection(KEY_AOD_WALLPAPER_MEDIA_EXCLUDED_APPS, packageName, enabled)

fun isAutoDismissEnabled(): Boolean = getBoolean(KEY_AUTO_DISMISS_ENABLED, false)

fun setAutoDismissEnabled(enabled: Boolean) = putBoolean(KEY_AUTO_DISMISS_ENABLED, enabled)

fun getAutoDismissLastMinutes(): Int = getInt(KEY_AUTO_DISMISS_LAST_MINUTES, 15)

fun setAutoDismissLastMinutes(minutes: Int) = putInt(KEY_AUTO_DISMISS_LAST_MINUTES, minutes)

fun loadAutoDismissConfigs(): List<AutoDismissAppConfig> {
val json = prefs.getString(KEY_AUTO_DISMISS_APPS, null)
return if (json != null) {
try {
gson
.fromJson(
json,
Array<AutoDismissAppConfig>::class.java,
).toList()
} catch (e: Exception) {
emptyList()
}
} else {
emptyList()
}
}

fun saveAutoDismissConfigs(configs: List<AutoDismissAppConfig>) {
val json = gson.toJson(configs)
putString(KEY_AUTO_DISMISS_APPS, json)
}

fun loadAutoDismissChannels(): List<AutoDismissChannel> {
val json = prefs.getString(KEY_AUTO_DISMISS_CHANNELS, null)
return if (json != null) {
try {
gson
.fromJson(
json,
Array<AutoDismissChannel>::class.java,
).toList()
} catch (e: Exception) {
emptyList()
}
} else {
emptyList()
}
}

fun saveAutoDismissChannels(channels: List<AutoDismissChannel>) {
val json = gson.toJson(channels)
putString(KEY_AUTO_DISMISS_CHANNELS, json)
}

fun loadAutoDismissStats(): Map<String, AutoDismissStat> {
val json = prefs.getString(KEY_AUTO_DISMISS_STATS, null) ?: return emptyMap()
return try {
val type = object : TypeToken<Map<String, AutoDismissStat>>() {}.type
gson.fromJson<Map<String, AutoDismissStat>>(json, type) ?: emptyMap()
} catch (e: Exception) {
emptyMap()
}
}

fun saveAutoDismissStats(stats: Map<String, AutoDismissStat>) {
val json = gson.toJson(stats)
putString(KEY_AUTO_DISMISS_STATS, json)
}

fun incrementAutoDismissCount(packageName: String) {
val current = loadAutoDismissStats().toMutableMap()
val existing = current[packageName]
val updated =
if (existing != null) {
existing.copy(count = existing.count + 1)
} else {
AutoDismissStat(1, System.currentTimeMillis())
}
current[packageName] = updated
saveAutoDismissStats(current)
}

/**
* Executes the load shut up configs operation.
* @return The resulting List<com data.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2026 sameerasw.com
* License: MIT License
*
* Feature Module: Domain Layer Models & Registries
* File: AutoDismissAppConfig.kt
* Description: Domain model and business logic entry for AutoDismissAppConfig.kt.
*/

package com.sameerasw.essentials.domain.model

import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName

@Keep
data class AutoDismissAppConfig(
@SerializedName("packageName") val packageName: String,
@SerializedName("minutes") val minutes: Int,
@SerializedName("excludedChannels") val excludedChannels: List<String>? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2026 sameerasw.com
* License: MIT License
*
* Feature Module: Domain Layer Models & Registries
* File: AutoDismissChannel.kt
* Description: Domain model and business logic entry for AutoDismissChannel.kt.
*/

package com.sameerasw.essentials.domain.model

import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName

@Keep
data class AutoDismissChannel(
@SerializedName("packageName") val packageName: String,
@SerializedName("id") val id: String,
@SerializedName("name") val name: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2026 sameerasw.com
* License: MIT License
*
* Feature Module: Domain Layer Models & Registries
* File: AutoDismissStat.kt
* Description: Domain model and business logic entry for AutoDismissStat.kt.
*/

package com.sameerasw.essentials.domain.model

import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName

@Keep
data class AutoDismissStat(
@SerializedName("count") val count: Int,
@SerializedName("since") val since: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -1554,6 +1554,49 @@ object FeatureRegistry {
context = context,
)
},
object : Feature(
id = "Auto-dismiss notifications",
title = R.string.feat_auto_dismiss_notifications_title,
iconRes = R.drawable.rounded_timer_24,
category = R.string.cat_interface,
description = R.string.feat_auto_dismiss_notifications_desc,
aboutDescription = R.string.about_desc_auto_dismiss_notifications,
permissionKeys = listOf("NOTIFICATION_LISTENER"),
showToggle = true,
hasMoreSettings = true,
searchableSettings = emptyList(),
parentFeatureId = "Notifications",
) {
override fun isEnabled(viewModel: MainViewModel) = viewModel.isAutoDismissEnabled.value

override fun isToggleEnabled(
viewModel: MainViewModel,
context: Context,
) = viewModel.isNotificationListenerEnabled.value

override fun onToggle(
viewModel: MainViewModel,
context: Context,
enabled: Boolean,
) = viewModel.setAutoDismissEnabled(
enabled,
context = context,
)

override fun onClick(
context: Context,
viewModel: MainViewModel,
) {
val intent =
Intent(
context,
FeatureSettingsActivity::class.java,
).apply {
putExtra("feature", "Auto-dismiss notifications")
}
context.startActivity(intent)
}
},
object : Feature(
id = "Notification snoozing",
title = R.string.feat_notification_snoozing_title,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2026 sameerasw.com
* License: MIT License
*
* Feature Module: Background Services & Receivers
* File: AutoDismissMath.kt
* Description: Pure calculation logic for notification auto-dismiss deadlines.
*/

package com.sameerasw.essentials.services

import com.sameerasw.essentials.domain.model.AutoDismissAppConfig

val AUTO_DISMISS_MINUTES = listOf(1, 5, 10, 15, 30, 60, 120, 180, 360, 720, 1440)

fun autoDismissDeadline(
packageName: String,
postTime: Long,
isClearable: Boolean,
isGroupSummary: Boolean,
ownPackage: String,
configs: List<AutoDismissAppConfig>,
channelId: String? = null,
): Long? {
if (packageName == ownPackage) return null
if (!isClearable) return null
if (isGroupSummary) return null
val config = configs.firstOrNull { it.packageName == packageName } ?: return null
if (channelId != null && channelId in config.excludedChannels.orEmpty()) return null
return postTime + config.minutes * 60_000L
}
Loading
Loading