Skip to content
Merged
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
14 changes: 1 addition & 13 deletions AndroidApp/.idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import me.nya_n.notificationnotifier.domain.usecase.NotifyUseCase
import me.nya_n.notificationnotifier.domain.usecase.PackageVisibilityGrantedUseCase
import me.nya_n.notificationnotifier.domain.usecase.SaveAddressUseCase
import me.nya_n.notificationnotifier.domain.usecase.SaveFilterConditionUseCase
import me.nya_n.notificationnotifier.domain.usecase.ToggleIgnoreSummaryUseCase
import me.nya_n.notificationnotifier.domain.usecase.impl.AddTargetAppUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.CheckPackageVisibilityUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.DeleteTargetAppUseCaseImpl
Expand All @@ -34,6 +35,7 @@ import me.nya_n.notificationnotifier.domain.usecase.impl.NotifyUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.PackageVisibilityGrantedUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.SaveAddressUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.SaveFilterConditionUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.ToggleIgnoreSummaryUseCaseImpl
import me.nya_n.notificationnotifier.model.AppConfig
import me.nya_n.notificationnotifier.ui.screen.app.AppViewModel
import me.nya_n.notificationnotifier.ui.screen.detail.DetailViewModel
Expand Down Expand Up @@ -84,7 +86,7 @@ class App : Application() {
// ViewModel
viewModel { AppViewModel(get(), packageName, get(), get()) }
viewModel { SelectionViewModel(get(), get(), get()) }
viewModel { params -> DetailViewModel(get(), get(), get(), params.get()) }
viewModel { params -> DetailViewModel(get(), get(), get(), get(), params.get()) }
viewModel { TargetViewModel(get(), get()) }
viewModel { SettingsViewModel(get(), get(), get(), get(), get(), get()) }

Expand All @@ -104,5 +106,6 @@ class App : Application() {
factory<CheckPackageVisibilityUseCase> { CheckPackageVisibilityUseCaseImpl(get()) }
factory<SaveAddressUseCase> { SaveAddressUseCaseImpl(get()) }
factory<SaveFilterConditionUseCase> { SaveFilterConditionUseCaseImpl(get()) }
factory<ToggleIgnoreSummaryUseCase> { ToggleIgnoreSummaryUseCaseImpl(get()) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import android.os.Bundle
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.text.SpannableString
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import me.nya_n.notificationnotifier.domain.usecase.NotifyTargetAppNotificationUseCase
import org.koin.android.ext.android.inject

Expand All @@ -29,7 +32,7 @@ class NotificationService : NotificationListenerService() {
val extras = sbn.notification.extras
val title = getTitle(extras) ?: return@launch
val message = extras.getCharSequence("android.text").toString()
useCase(sbn.packageName, title, message)
useCase.invoke(sbn.packageName, title, message, sbn.notification.flags)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "85b4a3de210e1ef5dfec5df608f693db",
"entities": [
{
"tableName": "conditions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`target_package_name` TEXT NOT NULL, `is_ignore_summary` INTEGER NOT NULL DEFAULT 0, `condition` TEXT NOT NULL, PRIMARY KEY(`target_package_name`))",
"fields": [
{
"fieldPath": "targetPackageName",
"columnName": "target_package_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isIgnoreSummary",
"columnName": "is_ignore_summary",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "condition",
"columnName": "condition",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"target_package_name"
]
}
},
{
"tableName": "targets",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`label` TEXT NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`package_name`))",
"fields": [
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"package_name"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '85b4a3de210e1ef5dfec5df608f693db')"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface AppRepository {

/* フィルタリング条件関連 */
suspend fun getFilterCondition(targetPackageName: String): FilterCondition?
suspend fun getFilterConditionOrDefault(targetPackageName: String): FilterCondition

suspend fun getFilterConditionList(): List<FilterCondition>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class AppRepositoryImpl(
}
}

override suspend fun getFilterConditionOrDefault(targetPackageName: String): FilterCondition {
return getFilterCondition(targetPackageName) ?: FilterCondition.default(targetPackageName)
}

override suspend fun getFilterConditionList(): List<FilterCondition> {
return withContext(Dispatchers.IO) {
filterConditionDao.getAll()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.nya_n.notificationnotifier.data.repository.source

import android.content.Context
import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
Expand All @@ -12,9 +13,11 @@ import me.nya_n.notificationnotifier.model.InstalledApp
FilterCondition::class,
InstalledApp::class
],
version = 1,
version = 2,
exportSchema = true,
autoMigrations = []
autoMigrations = [
AutoMigration(from = 1, to = 2)
]
)
abstract class DB : RoomDatabase() {

Expand Down Expand Up @@ -58,7 +61,7 @@ abstract class DB : RoomDatabase() {
fun version(): Int {
return try {
INSTANCE?.openHelper?.readableDatabase?.version ?: -1
} catch (e: Exception) {
} catch (_: Exception) {
-1
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import me.nya_n.notificationnotifier.data.repository.impl.UserSettingsRepository
import me.nya_n.notificationnotifier.data.repository.source.DB
import me.nya_n.notificationnotifier.data.repository.source.UserSettingsDataStore
import me.nya_n.notificationnotifier.data.repository.util.SharedPreferenceProvider
import me.nya_n.notificationnotifier.domain.usecase.*
import me.nya_n.notificationnotifier.domain.usecase.SaveFilterConditionUseCase
import me.nya_n.notificationnotifier.domain.usecase.ToggleIgnoreSummaryUseCase
import me.nya_n.notificationnotifier.domain.usecase.impl.AddTargetAppUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.DeleteTargetAppUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.ExportDataUseCaseImpl
Expand All @@ -27,6 +28,8 @@ import me.nya_n.notificationnotifier.domain.usecase.impl.NotifyUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.PackageVisibilityGrantedUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.SaveAddressUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.SaveFilterConditionUseCaseImpl
import me.nya_n.notificationnotifier.domain.usecase.impl.ToggleIgnoreSummaryUseCaseImpl
import me.nya_n.notificationnotifier.model.FilterCondition
import me.nya_n.notificationnotifier.model.InstalledApp
import org.junit.Before
import org.junit.Ignore
Expand Down Expand Up @@ -116,15 +119,24 @@ class UseCaseTest {
runBlocking {
val cond = "test"
val updatedCond = "updated"
val app = InstalledApp("sample", "com.sample.www")
val saver = SaveFilterConditionUseCaseImpl(appRepository)
saver(SaveFilterConditionUseCase.Args(app, cond))
val packageName = "com.sample.www"
val app = InstalledApp("sample", packageName)

val saver = SaveFilterConditionUseCaseImpl(appRepository)
val toggler = ToggleIgnoreSummaryUseCaseImpl(appRepository)
val loader = LoadFilterConditionUseCaseImpl(appRepository)
assertThat(loader(app)).isEqualTo(cond)

// 追加
saver(SaveFilterConditionUseCase.Args(app, cond))
assertThat(loader(app)).isEqualTo(FilterCondition(packageName, false, cond))

// メッセージ条件の更新
saver(SaveFilterConditionUseCase.Args(app, updatedCond))
assertThat(loader(app)).isEqualTo(updatedCond)
assertThat(loader(app)).isEqualTo(FilterCondition(packageName, false, updatedCond))

// サマリー条件の更新
toggler.invoke(ToggleIgnoreSummaryUseCase.Args(app))
assertThat(loader(app)).isEqualTo(FilterCondition(packageName, true, updatedCond))
}
}

Expand Down Expand Up @@ -214,14 +226,17 @@ class UseCaseTest {
val targetSaver = AddTargetAppUseCaseImpl(appRepository)
val condSaver = SaveFilterConditionUseCaseImpl(appRepository)
val addrSaver = SaveAddressUseCaseImpl(userSettingsRepository)
val toggler = ToggleIgnoreSummaryUseCaseImpl(appRepository)

// 初期値の保存
// ターゲット
val app = InstalledApp("export", "test.export")
val packageName = "test.export"
val app = InstalledApp("export", packageName)
targetSaver(app)
// 条件
val cond = ".*"
condSaver(SaveFilterConditionUseCase.Args(app, cond))
toggler.invoke(ToggleIgnoreSummaryUseCase.Args(app))
// アドレス
val addr = "192.168.1.4:5050"
addrSaver(addr)
Expand All @@ -234,6 +249,7 @@ class UseCaseTest {
targetSaver(InstalledApp("new", "new"))
// 条件
condSaver(SaveFilterConditionUseCase.Args(app, "new"))
toggler.invoke(ToggleIgnoreSummaryUseCase.Args(app))

// 復元
ImportDataUseCaseImpl(userSettingsRepository, appRepository)(appContext, uri)
Expand All @@ -248,7 +264,7 @@ class UseCaseTest {
}
// 条件
val restoreCond = LoadFilterConditionUseCaseImpl(appRepository)(app)
assertThat(restoreCond).isEqualTo(cond)
assertThat(restoreCond).isEqualTo(FilterCondition(packageName, true, cond))
// アドレス
val restoreAddr = LoadAddressUseCaseImpl(userSettingsRepository)()
assertThat(restoreAddr).isEqualTo(addr)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package me.nya_n.notificationnotifier.domain.usecase

import me.nya_n.notificationnotifier.model.FilterCondition
import me.nya_n.notificationnotifier.model.InstalledApp

/** 通知条件を読み込む */
interface LoadFilterConditionUseCase {
suspend operator fun invoke(target: InstalledApp): String
suspend operator fun invoke(target: InstalledApp): FilterCondition
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ interface NotifyTargetAppNotificationUseCase {
suspend operator fun invoke(
packageName: String,
title: String,
message: String
message: String,
flags: Int
): Result<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package me.nya_n.notificationnotifier.domain.usecase

import me.nya_n.notificationnotifier.model.InstalledApp

interface ToggleIgnoreSummaryUseCase {
suspend fun invoke(args: Args): Boolean

data class Args(val target: InstalledApp)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package me.nya_n.notificationnotifier.domain.usecase.impl

import me.nya_n.notificationnotifier.data.repository.AppRepository
import me.nya_n.notificationnotifier.domain.usecase.LoadFilterConditionUseCase
import me.nya_n.notificationnotifier.model.FilterCondition
import me.nya_n.notificationnotifier.model.InstalledApp

class LoadFilterConditionUseCaseImpl(
private val appRepository: AppRepository
) : LoadFilterConditionUseCase {
override suspend operator fun invoke(target: InstalledApp): String {
val cond = appRepository.getFilterCondition(target.packageName)
return cond?.condition ?: ""
override suspend operator fun invoke(target: InstalledApp): FilterCondition {
return appRepository.getFilterConditionOrDefault(target.packageName)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.nya_n.notificationnotifier.domain.usecase.impl

import android.app.Notification
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import me.nya_n.notificationnotifier.data.repository.AppRepository
Expand All @@ -13,7 +14,8 @@ class NotifyTargetAppNotificationUseCaseImpl(
override suspend operator fun invoke(
packageName: String,
title: String,
message: String
message: String,
flags: Int
): Result<Unit> {
return runCatching {
val targets = appRepository.getTargetAppList()
Expand All @@ -22,9 +24,15 @@ class NotifyTargetAppNotificationUseCaseImpl(
}

val cond = appRepository.getFilterCondition(packageName)
if (cond != null && cond.condition.isNotEmpty()) {
val regex = Regex(pattern = cond.condition)
if (!regex.matches("$title $message")) {
if (cond != null) {
if (cond.condition.isNotEmpty()) {
val regex = Regex(pattern = cond.condition)
if (!regex.matches("$title $message")) {
return Result.success(Unit)
}
Comment on lines +28 to +32
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

無効な正規表現パターンによる例外処理を検討してください。

Regex(pattern = cond.condition) は、ユーザーが無効な正規表現を入力した場合に PatternSyntaxException をスローします。runCatching でキャッチされますが、Result.failure が返却されるため、通知が失敗扱いになります。

また、matches() は文字列全体がパターンに一致する必要があります。部分一致を期待している場合は containsMatchIn() の使用を検討してください。

🔧 例外ハンドリングと部分一致の提案
             if (cond.condition.isNotEmpty()) {
-                val regex = Regex(pattern = cond.condition)
-                if (!regex.matches("$title $message")) {
-                    return Result.success(Unit)
+                val regex = runCatching { Regex(pattern = cond.condition) }.getOrNull()
+                if (regex != null && !regex.containsMatchIn("$title $message")) {
+                    return Result.success(Unit)
                 }
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (cond.condition.isNotEmpty()) {
val regex = Regex(pattern = cond.condition)
if (!regex.matches("$title $message")) {
return Result.success(Unit)
}
if (cond.condition.isNotEmpty()) {
val regex = runCatching { Regex(pattern = cond.condition) }.getOrNull()
if (regex != null && !regex.containsMatchIn("$title $message")) {
return Result.success(Unit)
}
🤖 Prompt for AI Agents
In
@AndroidApp/domain/src/main/kotlin/me/nya_n/notificationnotifier/domain/usecase/impl/NotifyTargetAppNotificationUseCaseImpl.kt
around lines 28 - 32, The current code in NotifyTargetAppNotificationUseCaseImpl
constructs Regex(pattern = cond.condition) and calls matches("$title $message"),
which can throw PatternSyntaxException and treats invalid patterns as failures
via the outer runCatching; change it so invalid regexes are caught and treated
as "skip" (return Result.success(Unit)) rather than a failure—wrap the Regex
construction in a try/catch (or runCatching) that logs the invalid pattern and
returns success on failure, and replace matches(...) with
containsMatchIn("$title $message") to perform a partial match check; reference
the cond.condition usage and the regex check block when applying this fix.

}
val isSummary = flags and Notification.FLAG_GROUP_SUMMARY != 0
if (cond.isIgnoreSummary && isSummary) {
return Result.success(Unit)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ class SaveFilterConditionUseCaseImpl(
private val appRepository: AppRepository
) : SaveFilterConditionUseCase {
override suspend operator fun invoke(args: Args) {
val target = args.target.packageName
val data = appRepository.getFilterConditionOrDefault(target)
appRepository.saveFilterCondition(
FilterCondition(
args.target.packageName,
args.condition ?: ""
targetPackageName = args.target.packageName,
condition = args.condition ?: "",
isIgnoreSummary = data.isIgnoreSummary
)
)
}
Expand Down
Loading