From f7fedbb5e5b42f09156476c4d3f4f7529f346997 Mon Sep 17 00:00:00 2001 From: Aditya Rajput Date: Mon, 26 Jan 2026 17:20:17 +0530 Subject: [PATCH 1/4] Let filters target all apps --- .../notifilter/data/models/App.kt | 3 + .../services/NotificationListener.kt | 9 ++- .../viewmodels/UpsertFilterViewModel.kt | 4 +- .../notifilter/views/screens/FiltersScreen.kt | 4 +- .../views/screens/UpsertFilterScreen.kt | 81 ++++++++++++++----- app/src/main/res/values/strings.xml | 4 + 6 files changed, 82 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/co/adityarajput/notifilter/data/models/App.kt b/app/src/main/java/co/adityarajput/notifilter/data/models/App.kt index f086668..5ba99e6 100644 --- a/app/src/main/java/co/adityarajput/notifilter/data/models/App.kt +++ b/app/src/main/java/co/adityarajput/notifilter/data/models/App.kt @@ -4,3 +4,6 @@ import kotlinx.serialization.Serializable @Serializable data class App(val name: String, val packageName: String) + +val None = App("", "") +val Any = App("Any app", ".*") diff --git a/app/src/main/java/co/adityarajput/notifilter/services/NotificationListener.kt b/app/src/main/java/co/adityarajput/notifilter/services/NotificationListener.kt index 1a89e21..c81a9b4 100644 --- a/app/src/main/java/co/adityarajput/notifilter/services/NotificationListener.kt +++ b/app/src/main/java/co/adityarajput/notifilter/services/NotificationListener.kt @@ -16,6 +16,7 @@ import co.adityarajput.notifilter.Constants import co.adityarajput.notifilter.R import co.adityarajput.notifilter.data.AppContainer import co.adityarajput.notifilter.data.models.Action +import co.adityarajput.notifilter.data.models.Any import co.adityarajput.notifilter.data.models.Filter import co.adityarajput.notifilter.data.models.Notification import co.adityarajput.notifilter.utils.Logger @@ -115,7 +116,7 @@ class NotificationListener : NotificationListenerService() { Logger.d("NotificationListener.onNotificationPosted", "Received $notification") val filter = filters.filter { - notification.origin == it.app.packageName + (notification.origin == it.app.packageName || it.app == Any) && it.enabled && it.schedule.includesNow() && it.matchesTextOf(notification) @@ -230,7 +231,11 @@ class NotificationListener : NotificationListenerService() { } serviceScope.launch { - repository.registerHit(filter, notification.copy(origin = filter.app.name)) + repository.registerHit( + filter, + if (filter.app == Any) notification + else notification.copy(origin = filter.app.name), + ) notifications = repository.notifications().first() Logger.d( "NotificationListener.onNotificationPosted", diff --git a/app/src/main/java/co/adityarajput/notifilter/viewmodels/UpsertFilterViewModel.kt b/app/src/main/java/co/adityarajput/notifilter/viewmodels/UpsertFilterViewModel.kt index 37c1191..06f2e0c 100644 --- a/app/src/main/java/co/adityarajput/notifilter/viewmodels/UpsertFilterViewModel.kt +++ b/app/src/main/java/co/adityarajput/notifilter/viewmodels/UpsertFilterViewModel.kt @@ -31,7 +31,7 @@ class UpsertFilterViewModel( data class Values( val filterId: Int = 0, val notification: Notification? = null, - val app: App = App("", ""), + val app: App = None, val regexTarget: RegexTarget = RegexTarget.OR, val queryPattern: String = "", val secondaryQueryPattern: String = "", @@ -109,7 +109,7 @@ class UpsertFilterViewModel( when (page) { FormPage.ZAPPER -> return null - FormPage.PACKAGE -> if (values.app.packageName.isBlank()) return FormError.BLANK_FIELDS + FormPage.PACKAGE -> if (values.app == None) return FormError.BLANK_FIELDS FormPage.PATTERN -> { if (values.queryPattern.isBlank()) return FormError.BLANK_FIELDS diff --git a/app/src/main/java/co/adityarajput/notifilter/views/screens/FiltersScreen.kt b/app/src/main/java/co/adityarajput/notifilter/views/screens/FiltersScreen.kt index eebd380..d113fc2 100644 --- a/app/src/main/java/co/adityarajput/notifilter/views/screens/FiltersScreen.kt +++ b/app/src/main/java/co/adityarajput/notifilter/views/screens/FiltersScreen.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.lifecycle.viewmodel.compose.viewModel import co.adityarajput.notifilter.R +import co.adityarajput.notifilter.data.models.Any import co.adityarajput.notifilter.data.models.RegexTarget import co.adityarajput.notifilter.utils.getFirst import co.adityarajput.notifilter.utils.getToggleString @@ -98,7 +99,8 @@ fun FiltersScreen( } }, it.action.verb(), - it.app.name.getFirst(30), + if (it.app == Any) stringResource(R.string.any_app) + else it.app.name.getFirst(30), if (!it.enabled) stringResource(R.string.filter_disabled) else if (!it.historyEnabled) stringResource(R.string.history_disabled) else pluralStringResource(R.plurals.hit, it.hits, it.hits), diff --git a/app/src/main/java/co/adityarajput/notifilter/views/screens/UpsertFilterScreen.kt b/app/src/main/java/co/adityarajput/notifilter/views/screens/UpsertFilterScreen.kt index 44c3149..2948d2e 100644 --- a/app/src/main/java/co/adityarajput/notifilter/views/screens/UpsertFilterScreen.kt +++ b/app/src/main/java/co/adityarajput/notifilter/views/screens/UpsertFilterScreen.kt @@ -35,9 +35,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.lifecycle.viewmodel.compose.viewModel import co.adityarajput.notifilter.R -import co.adityarajput.notifilter.data.models.Action -import co.adityarajput.notifilter.data.models.App -import co.adityarajput.notifilter.data.models.RegexTarget +import co.adityarajput.notifilter.data.models.* import co.adityarajput.notifilter.utils.filterFirst import co.adityarajput.notifilter.utils.getFirst import co.adityarajput.notifilter.utils.hasAccessibilityServicePermission @@ -221,6 +219,7 @@ private fun ZapperPage(viewModel: UpsertFilterViewModel) { private fun PackagePage(viewModel: UpsertFilterViewModel) { var searchString by remember { mutableStateOf("") } var visibleItemsCount by remember { mutableIntStateOf(10) } + var showAdvancedOptions by remember { mutableStateOf(viewModel.state.values.app == Any) } var showSystemPackages by remember { mutableStateOf(false) } val (apps, searchFinished) = (if (showSystemPackages) viewModel.allPackages else viewModel.visibleApps) @@ -244,26 +243,72 @@ private fun PackagePage(viewModel: UpsertFilterViewModel) { ), singleLine = true, ) - Row( - Modifier - .padding(vertical = dimensionResource(R.dimen.padding_medium)) - .toggleable(showSystemPackages) { showSystemPackages = it }, - Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), - Alignment.CenterVertically, + Column( + Modifier.padding(dimensionResource(R.dimen.padding_medium)), + Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)), ) { - Checkbox(showSystemPackages, null) - Column { + Row( + Modifier.toggleable(showAdvancedOptions) { showAdvancedOptions = it }, + Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), + Alignment.CenterVertically, + ) { + Checkbox(showAdvancedOptions, null) Text( - stringResource(R.string.show_system_packages), + stringResource(R.string.advanced_options), style = MaterialTheme.typography.labelLarge, fontWeight = FontWeight.Normal, ) - Text( - stringResource(R.string.system_packages_warning), - Modifier.padding(top = dimensionResource(R.dimen.padding_small)), - style = MaterialTheme.typography.labelMedium, - fontWeight = FontWeight.Normal, - ) + } + if (showAdvancedOptions) { + Row( + Modifier + .padding(horizontal = dimensionResource(R.dimen.padding_small)) + .toggleable(viewModel.state.values.app == Any) { + viewModel.updateForm( + FormPage.PACKAGE, + viewModel.state.values.copy(app = if (it) Any else None), + ) + }, + Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), + Alignment.CenterVertically, + ) { + Checkbox(viewModel.state.values.app == Any, null) + Column { + Text( + stringResource(R.string.target_all_apps), + style = MaterialTheme.typography.labelLarge, + fontWeight = FontWeight.Normal, + ) + Text( + stringResource(R.string.all_apps_warning), + Modifier.padding(top = dimensionResource(R.dimen.padding_small)), + style = MaterialTheme.typography.labelMedium, + fontWeight = FontWeight.Normal, + ) + } + } + Row( + Modifier + .padding(horizontal = dimensionResource(R.dimen.padding_small)) + .toggleable(showSystemPackages) { showSystemPackages = it }, + Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), + Alignment.CenterVertically, + ) { + Checkbox(showSystemPackages, null) + Column { + Text( + stringResource(R.string.show_system_packages), + style = MaterialTheme.typography.labelLarge, + fontWeight = FontWeight.Normal, + ) + Text( + stringResource(R.string.system_packages_warning), + Modifier.padding(top = dimensionResource(R.dimen.padding_small)), + style = MaterialTheme.typography.labelMedium, + fontWeight = FontWeight.Normal, + ) + } + } } } FlowRow( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 906ce07..16778e4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,7 @@ tap \"%1$s\" batch every %1$s delay notifications + Any app history disabled filter disabled @@ -38,6 +39,9 @@ Waiting for active notifications… Choose a notification to target Choose an app to target + Advanced options + Target all apps? + Targeting all apps is rarely necessary. This may cause unexpected issues. Show system packages? Notifications from system packages may contain critical information. Package name From 7002ba6cd2f4de6185124fefc3fa7a268faa2171 Mon Sep 17 00:00:00 2001 From: Aditya Rajput Date: Mon, 26 Jan 2026 17:38:11 +0530 Subject: [PATCH 2/4] Always fetch ANs when on UFS --- .../adityarajput/notifilter/viewmodels/UpsertFilterViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/co/adityarajput/notifilter/viewmodels/UpsertFilterViewModel.kt b/app/src/main/java/co/adityarajput/notifilter/viewmodels/UpsertFilterViewModel.kt index 06f2e0c..624ea48 100644 --- a/app/src/main/java/co/adityarajput/notifilter/viewmodels/UpsertFilterViewModel.kt +++ b/app/src/main/java/co/adityarajput/notifilter/viewmodels/UpsertFilterViewModel.kt @@ -87,7 +87,7 @@ class UpsertFilterViewModel( init { viewModelScope.launch { - while (state.page.isFirstPage()) { + while (true) { activeNotifications = NotificationListener.instance ?.activeNotifications ?.filter { it.notification.flags and FLAG_GROUP_SUMMARY == 0 } From 5ee1f61255c7822aa632e523aa4ed95802e3b61f Mon Sep 17 00:00:00 2001 From: Aditya Rajput Date: Mon, 26 Jan 2026 17:42:45 +0530 Subject: [PATCH 3/4] Pre-shortlist apps list when editing filter --- .../notifilter/views/screens/UpsertFilterScreen.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/co/adityarajput/notifilter/views/screens/UpsertFilterScreen.kt b/app/src/main/java/co/adityarajput/notifilter/views/screens/UpsertFilterScreen.kt index 2948d2e..f8e73c9 100644 --- a/app/src/main/java/co/adityarajput/notifilter/views/screens/UpsertFilterScreen.kt +++ b/app/src/main/java/co/adityarajput/notifilter/views/screens/UpsertFilterScreen.kt @@ -217,7 +217,12 @@ private fun ZapperPage(viewModel: UpsertFilterViewModel) { @OptIn(ExperimentalMaterial3Api::class) @Composable private fun PackagePage(viewModel: UpsertFilterViewModel) { - var searchString by remember { mutableStateOf("") } + var searchString by remember { + mutableStateOf( + if (viewModel.state.values.app == Any) "" + else viewModel.state.values.app.name, + ) + } var visibleItemsCount by remember { mutableIntStateOf(10) } var showAdvancedOptions by remember { mutableStateOf(viewModel.state.values.app == Any) } var showSystemPackages by remember { mutableStateOf(false) } From da09698423a3af62fb1dfabba9b8bfe1fc0f3202 Mon Sep 17 00:00:00 2001 From: Aditya Rajput Date: Mon, 26 Jan 2026 20:41:42 +0530 Subject: [PATCH 4/4] Update version --- app/build.gradle.kts | 4 ++-- app/src/main/res/values/strings.xml | 2 +- metadata/en-US/changelogs/16.txt | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 metadata/en-US/changelogs/16.txt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 689f0d7..abbd8a2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -18,8 +18,8 @@ android { applicationId = "co.adityarajput.notifilter" minSdk = 29 targetSdk = 36 - versionCode = 15 - versionName = "4.0.0" + versionCode = 16 + versionName = "4.1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 16778e4..b30a5ee 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ NotiFilter NotiFilter - 4.0.0 + 4.1.0 No filters added.\nTap + to get started. "on weekdays " diff --git a/metadata/en-US/changelogs/16.txt b/metadata/en-US/changelogs/16.txt new file mode 100644 index 0000000..5f0a5b6 --- /dev/null +++ b/metadata/en-US/changelogs/16.txt @@ -0,0 +1,3 @@ +• fix: Handle TAP_NOTIFICATION on Android 14 & 15 +• feat: Let filters target all apps +• fix: Improve UI of "create/edit filter" screen