From 999d0f1b7146537d82dc1fd2edf459412e584986 Mon Sep 17 00:00:00 2001 From: Richter3766 <97567615+Richter3766@users.noreply.github.com> Date: Fri, 28 Feb 2025 21:29:53 +0900 Subject: [PATCH 1/6] =?UTF-8?q?fix:=20title=20=EB=9D=BC=EC=9A=B0=ED=8C=85?= =?UTF-8?q?=20=EC=8B=9C=20=ED=8C=85=EA=B9=80=20=ED=98=84=EC=83=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit title에 ""이 들어가 있을 경우 라우팅 경로를 올바르게 인식하지 못해 팅기던 현상이 있어 수정함. 해결은 title을 인코딩하여 라우팅하는 것. --- .../example/notimanager/common/objects/Encoder.kt | 15 +++++++++++++++ .../ui/component/NotificationAppListView.kt | 5 +++-- .../ui/component/NotificationTitleListView.kt | 5 +++-- .../presentation/ui/navigation/AppNavHost.kt | 5 +++-- 4 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/com/example/notimanager/common/objects/Encoder.kt diff --git a/app/src/main/java/com/example/notimanager/common/objects/Encoder.kt b/app/src/main/java/com/example/notimanager/common/objects/Encoder.kt new file mode 100644 index 0000000..bef7951 --- /dev/null +++ b/app/src/main/java/com/example/notimanager/common/objects/Encoder.kt @@ -0,0 +1,15 @@ +package com.example.notimanager.common.objects + +import java.net.URLDecoder +import java.net.URLEncoder +import java.nio.charset.StandardCharsets + +object Encoder { + fun getEncodedString(target: String): String { + return URLEncoder.encode(target, StandardCharsets.UTF_8.toString()) + } + + fun getDecodeString(target: String): String { + return URLDecoder.decode(target, StandardCharsets.UTF_8.toString()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/notimanager/presentation/ui/component/NotificationAppListView.kt b/app/src/main/java/com/example/notimanager/presentation/ui/component/NotificationAppListView.kt index 55813e9..162e116 100644 --- a/app/src/main/java/com/example/notimanager/presentation/ui/component/NotificationAppListView.kt +++ b/app/src/main/java/com/example/notimanager/presentation/ui/component/NotificationAppListView.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.example.notimanager.common.objects.DateFormatter.formatTimestamp +import com.example.notimanager.common.objects.Encoder.getEncodedString import com.example.notimanager.domain.model.NotificationApp import com.example.notimanager.presentation.stateholder.state.NotificationAppPriorityState import com.example.notimanager.presentation.stateholder.state.NotificationAppState @@ -74,7 +75,7 @@ fun NotificationAppListView( onClick = { navController .navigate( - "titleScreen/${notification.appName}/${notification.title}" + "titleScreen/${notification.appName}/${getEncodedString(notification.title)}" ) }, viewModel = viewModel, @@ -92,7 +93,7 @@ fun NotificationAppListView( onClick = { navController .navigate( - "titleScreen/${notification.appName}/${notification.title}" + "titleScreen/${notification.appName}/${getEncodedString(notification.title)}" ) }, viewModel = viewModel, diff --git a/app/src/main/java/com/example/notimanager/presentation/ui/component/NotificationTitleListView.kt b/app/src/main/java/com/example/notimanager/presentation/ui/component/NotificationTitleListView.kt index 899cd92..de3e7b9 100644 --- a/app/src/main/java/com/example/notimanager/presentation/ui/component/NotificationTitleListView.kt +++ b/app/src/main/java/com/example/notimanager/presentation/ui/component/NotificationTitleListView.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.navigation.NavController import com.example.notimanager.common.objects.DateFormatter.formatTimestamp +import com.example.notimanager.common.objects.Encoder.getEncodedString import com.example.notimanager.domain.model.NotificationTitle import com.example.notimanager.presentation.stateholder.state.NotificationTitlePriorityState import com.example.notimanager.presentation.stateholder.state.NotificationTitleState @@ -73,7 +74,7 @@ fun NotificationTitleListView( items(currentNotiPriority) { notification -> NotificationTitleItemView(notification = notification, onClick = { navController.navigate( - "notificationScreen/${viewModel.getAppName()}/${notification.title}" + "notificationScreen/${viewModel.getAppName()}/${getEncodedString(notification.title)}" ) }, viewModel = viewModel, priorityViewModel = priorityViewModel) } @@ -85,7 +86,7 @@ fun NotificationTitleListView( items(currentNoti) { notification -> NotificationTitleItemView(notification = notification, onClick = { navController.navigate( - "notificationScreen/${viewModel.getAppName()}/${notification.title}" + "notificationScreen/${viewModel.getAppName()}/${getEncodedString(notification.title)}" ) }, viewModel = viewModel, priorityViewModel = priorityViewModel) } diff --git a/app/src/main/java/com/example/notimanager/presentation/ui/navigation/AppNavHost.kt b/app/src/main/java/com/example/notimanager/presentation/ui/navigation/AppNavHost.kt index 83e56d7..06ee476 100644 --- a/app/src/main/java/com/example/notimanager/presentation/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/com/example/notimanager/presentation/ui/navigation/AppNavHost.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import com.example.notimanager.common.objects.Encoder.getDecodeString import com.example.notimanager.presentation.ui.screen.MainScreen import com.example.notimanager.presentation.ui.screen.NotificationScreen import com.example.notimanager.presentation.ui.screen.TitleScreen @@ -20,12 +21,12 @@ fun AppNavHost(navController: NavHostController) { composable("titleScreen/{appName}/{title}") { backStackEntry -> val appName = backStackEntry.arguments?.getString("appName") val title = backStackEntry.arguments?.getString("title") - TitleScreen(navController, appName!!, title!!) + TitleScreen(navController, appName!!, getDecodeString(title!!)) } composable("notificationScreen/{appName}/{title}") { backStackEntry -> val appName = backStackEntry.arguments?.getString("appName") val title = backStackEntry.arguments?.getString("title") - NotificationScreen(navController, appName!!, title!!) + NotificationScreen(navController, appName!!, getDecodeString(title!!)) } } } \ No newline at end of file From 000363aa9ebce8d0e9f2284f3403bbadfb6c835b Mon Sep 17 00:00:00 2001 From: Richter3766 <97567615+Richter3766@users.noreply.github.com> Date: Sat, 1 Mar 2025 09:33:18 +0900 Subject: [PATCH 2/6] =?UTF-8?q?refactor:=20NotiService=20=EC=9C=A0?= =?UTF-8?q?=ED=8B=B8,=20=EB=8F=84=EB=A9=94=EC=9D=B8=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notimanager/data/service/NotiListenerServiceTest.kt | 4 ++-- .../java/com/example/notimanager/data/utils/NameGetterTest.kt | 1 + .../java/com/example/notimanager/data/dto/NotificationDto.kt | 2 +- .../notimanager/{data => domain}/utils/IntentHelper.kt | 2 +- .../example/notimanager/{data => domain}/utils/NameGetter.kt | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) rename app/src/main/java/com/example/notimanager/{data => domain}/utils/IntentHelper.kt (94%) rename app/src/main/java/com/example/notimanager/{data => domain}/utils/NameGetter.kt (93%) diff --git a/app/src/androidTest/java/com/example/notimanager/data/service/NotiListenerServiceTest.kt b/app/src/androidTest/java/com/example/notimanager/data/service/NotiListenerServiceTest.kt index 6c837c9..47a5f5a 100644 --- a/app/src/androidTest/java/com/example/notimanager/data/service/NotiListenerServiceTest.kt +++ b/app/src/androidTest/java/com/example/notimanager/data/service/NotiListenerServiceTest.kt @@ -14,8 +14,8 @@ import com.example.notimanager.presentation.ui.activity.MainActivity import com.example.notimanager.data.model.NotificationModel import com.example.notimanager.data.repository.NotificationRepository import com.example.notimanager.domain.service.NotiListenerService -import com.example.notimanager.data.utils.NameGetter -import com.example.notimanager.data.utils.IntentHelper +import com.example.notimanager.domain.utils.NameGetter +import com.example.notimanager.domain.utils.IntentHelper import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every diff --git a/app/src/androidTest/java/com/example/notimanager/data/utils/NameGetterTest.kt b/app/src/androidTest/java/com/example/notimanager/data/utils/NameGetterTest.kt index 01057e7..7cb96e9 100644 --- a/app/src/androidTest/java/com/example/notimanager/data/utils/NameGetterTest.kt +++ b/app/src/androidTest/java/com/example/notimanager/data/utils/NameGetterTest.kt @@ -6,6 +6,7 @@ import android.content.pm.PackageManager import android.os.UserHandle import android.service.notification.StatusBarNotification import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.example.notimanager.domain.utils.NameGetter import io.mockk.every import io.mockk.mockk import io.mockk.unmockkAll diff --git a/app/src/main/java/com/example/notimanager/data/dto/NotificationDto.kt b/app/src/main/java/com/example/notimanager/data/dto/NotificationDto.kt index e802e84..70dd94c 100644 --- a/app/src/main/java/com/example/notimanager/data/dto/NotificationDto.kt +++ b/app/src/main/java/com/example/notimanager/data/dto/NotificationDto.kt @@ -1,7 +1,7 @@ package com.example.notimanager.data.dto import android.graphics.BitmapFactory -import com.example.notimanager.data.utils.IntentHelper.retrieveIntent +import com.example.notimanager.domain.utils.IntentHelper.retrieveIntent import com.example.notimanager.domain.model.Notification data class NotificationDto( diff --git a/app/src/main/java/com/example/notimanager/data/utils/IntentHelper.kt b/app/src/main/java/com/example/notimanager/domain/utils/IntentHelper.kt similarity index 94% rename from app/src/main/java/com/example/notimanager/data/utils/IntentHelper.kt rename to app/src/main/java/com/example/notimanager/domain/utils/IntentHelper.kt index c97c833..4df41ba 100644 --- a/app/src/main/java/com/example/notimanager/data/utils/IntentHelper.kt +++ b/app/src/main/java/com/example/notimanager/domain/utils/IntentHelper.kt @@ -1,4 +1,4 @@ -package com.example.notimanager.data.utils +package com.example.notimanager.domain.utils import android.content.Intent import android.os.Parcel diff --git a/app/src/main/java/com/example/notimanager/data/utils/NameGetter.kt b/app/src/main/java/com/example/notimanager/domain/utils/NameGetter.kt similarity index 93% rename from app/src/main/java/com/example/notimanager/data/utils/NameGetter.kt rename to app/src/main/java/com/example/notimanager/domain/utils/NameGetter.kt index 0d8d6da..d4e8ee7 100644 --- a/app/src/main/java/com/example/notimanager/data/utils/NameGetter.kt +++ b/app/src/main/java/com/example/notimanager/domain/utils/NameGetter.kt @@ -1,4 +1,4 @@ -package com.example.notimanager.data.utils +package com.example.notimanager.domain.utils import android.content.Context import android.content.pm.PackageManager From 5695d2fab82d2117b46577b636c285cfb7a79a90 Mon Sep 17 00:00:00 2001 From: Richter3766 <97567615+Richter3766@users.noreply.github.com> Date: Sat, 1 Mar 2025 09:36:20 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EC=95=B1=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20getter=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 패키지 이름으로 앱의 아이콘을 가져오는 getter 추가. --- .../{data => domain}/utils/AppIconGetter.kt | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) rename app/src/main/java/com/example/notimanager/{data => domain}/utils/AppIconGetter.kt (61%) diff --git a/app/src/main/java/com/example/notimanager/data/utils/AppIconGetter.kt b/app/src/main/java/com/example/notimanager/domain/utils/AppIconGetter.kt similarity index 61% rename from app/src/main/java/com/example/notimanager/data/utils/AppIconGetter.kt rename to app/src/main/java/com/example/notimanager/domain/utils/AppIconGetter.kt index 0a1d668..1c1f402 100644 --- a/app/src/main/java/com/example/notimanager/data/utils/AppIconGetter.kt +++ b/app/src/main/java/com/example/notimanager/domain/utils/AppIconGetter.kt @@ -1,21 +1,24 @@ -package com.example.notimanager.data.utils +package com.example.notimanager.domain.utils import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Paint import android.graphics.PorterDuff import android.graphics.PorterDuffColorFilter +import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import com.example.notimanager.common.objects.DateFormatter.toBitmap import java.io.ByteArrayOutputStream object AppIconGetter { - fun convertByteArrayWithColor(context: Context, icon: Icon?, color: Int): ByteArray{ - if (icon == null) return ByteArray(0) + fun convertByteArrayWithColor(icon: Drawable?, color: Int): ByteArray?{ + if (icon == null) return null // Icon을 Drawable로 변환 후 Bitmap으로 변환 - val bitmap = icon.loadDrawable(context)?.toBitmap() ?: return ByteArray(0) + val bitmap = icon.toBitmap() val bitmapConfig = bitmap.config ?: Bitmap.Config.ARGB_8888 // 색상 필터 적용 @@ -37,10 +40,21 @@ object AppIconGetter { return stream.toByteArray() } - fun convertByteArray(context: Context, icon: Icon?): ByteArray{ - val bitmap = icon?.loadDrawable(context)?.toBitmap() + fun convertByteArray(icon: Drawable?): ByteArray{ + val bitmap = icon?.toBitmap() val stream = ByteArrayOutputStream() bitmap?.compress(Bitmap.CompressFormat.PNG, 100, stream) return stream.toByteArray() } + + fun getAppIcon(context: Context, packageName: String): Drawable? { + return try { + val packageManager: PackageManager = context.packageManager + val applicationInfo: ApplicationInfo = packageManager.getApplicationInfo(packageName, 0) + packageManager.getApplicationIcon(applicationInfo) + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + null + } + } } \ No newline at end of file From 71789d6199b12c6894f989da8ad8a8839377bcc1 Mon Sep 17 00:00:00 2001 From: Richter3766 <97567615+Richter3766@users.noreply.github.com> Date: Sat, 1 Mar 2025 09:37:28 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 알림 모델에 subText 컬럼 추가, 알림 메타 모델에 isRead 컬럼 추가. 마이그레이션 진행. --- .../7.json | 20 +- .../8.json | 240 ++++++++++++++++++ .../data/model/NotificationMetaModel.kt | 3 +- .../data/model/NotificationModel.kt | 1 + .../source/local/database/MigrationObject.kt | 7 + .../local/database/NotiManagerDatabase.kt | 8 +- 6 files changed, 271 insertions(+), 8 deletions(-) create mode 100644 app/schemas/com.example.notimanager.data.source.local.database.NotiManagerDatabase/8.json diff --git a/app/schemas/com.example.notimanager.data.source.local.database.NotiManagerDatabase/7.json b/app/schemas/com.example.notimanager.data.source.local.database.NotiManagerDatabase/7.json index 9c6f87f..bece172 100644 --- a/app/schemas/com.example.notimanager.data.source.local.database.NotiManagerDatabase/7.json +++ b/app/schemas/com.example.notimanager.data.source.local.database.NotiManagerDatabase/7.json @@ -2,11 +2,11 @@ "formatVersion": 1, "database": { "version": 7, - "identityHash": "ddf91ad81bbb46713142177c854d85ad", + "identityHash": "0bb8836fe6777d5c9170a6881beae9fe", "entities": [ { "tableName": "notification", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `appName` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `timestamp` INTEGER NOT NULL)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `appName` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `subText` TEXT NOT NULL)", "fields": [ { "fieldPath": "id", @@ -37,6 +37,12 @@ "columnName": "timestamp", "affinity": "INTEGER", "notNull": true + }, + { + "fieldPath": "subText", + "columnName": "subText", + "affinity": "TEXT", + "notNull": true } ], "primaryKey": { @@ -62,7 +68,7 @@ }, { "tableName": "notification_meta", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`notificationId` INTEGER NOT NULL, `intentActive` INTEGER NOT NULL, `intentArray` BLOB NOT NULL, PRIMARY KEY(`notificationId`), FOREIGN KEY(`notificationId`) REFERENCES `notification`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`notificationId` INTEGER NOT NULL, `intentActive` INTEGER NOT NULL, `intentArray` BLOB NOT NULL, `isRead` INTEGER NOT NULL, PRIMARY KEY(`notificationId`), FOREIGN KEY(`notificationId`) REFERENCES `notification`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { "fieldPath": "notificationId", @@ -81,6 +87,12 @@ "columnName": "intentArray", "affinity": "BLOB", "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "isRead", + "affinity": "INTEGER", + "notNull": true } ], "primaryKey": { @@ -222,7 +234,7 @@ "views": [], "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, 'ddf91ad81bbb46713142177c854d85ad')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0bb8836fe6777d5c9170a6881beae9fe')" ] } } \ No newline at end of file diff --git a/app/schemas/com.example.notimanager.data.source.local.database.NotiManagerDatabase/8.json b/app/schemas/com.example.notimanager.data.source.local.database.NotiManagerDatabase/8.json new file mode 100644 index 0000000..7b13a36 --- /dev/null +++ b/app/schemas/com.example.notimanager.data.source.local.database.NotiManagerDatabase/8.json @@ -0,0 +1,240 @@ +{ + "formatVersion": 1, + "database": { + "version": 8, + "identityHash": "0bb8836fe6777d5c9170a6881beae9fe", + "entities": [ + { + "tableName": "notification", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `appName` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `subText` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "appName", + "columnName": "appName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subText", + "columnName": "subText", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_notification_appName_title_timestamp", + "unique": false, + "columnNames": [ + "appName", + "title", + "timestamp" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_notification_appName_title_timestamp` ON `${TABLE_NAME}` (`appName`, `title`, `timestamp`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "notification_meta", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`notificationId` INTEGER NOT NULL, `intentActive` INTEGER NOT NULL, `intentArray` BLOB NOT NULL, `isRead` INTEGER NOT NULL, PRIMARY KEY(`notificationId`), FOREIGN KEY(`notificationId`) REFERENCES `notification`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "intentActive", + "columnName": "intentActive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "intentArray", + "columnName": "intentArray", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "isRead", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "notificationId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "notification", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "notificationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "notification_icon", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`notificationId` INTEGER NOT NULL, `iconBytes` BLOB NOT NULL, `priorityActive` INTEGER NOT NULL DEFAULT 0, `priority` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`notificationId`), FOREIGN KEY(`notificationId`) REFERENCES `notification`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "iconBytes", + "columnName": "iconBytes", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "priorityActive", + "columnName": "priorityActive", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "notificationId" + ] + }, + "indices": [ + { + "name": "index_notification_icon_priorityActive_priority", + "unique": false, + "columnNames": [ + "priorityActive", + "priority" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_notification_icon_priorityActive_priority` ON `${TABLE_NAME}` (`priorityActive`, `priority`)" + } + ], + "foreignKeys": [ + { + "table": "notification", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "notificationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "app_icon", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`notiAppName` TEXT NOT NULL, `iconBytes` BLOB NOT NULL, `priorityActive` INTEGER NOT NULL DEFAULT 0, `priority` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`notiAppName`))", + "fields": [ + { + "fieldPath": "notiAppName", + "columnName": "notiAppName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconBytes", + "columnName": "iconBytes", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "priorityActive", + "columnName": "priorityActive", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "notiAppName" + ] + }, + "indices": [ + { + "name": "index_app_icon_priorityActive_priority", + "unique": false, + "columnNames": [ + "priorityActive", + "priority" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_app_icon_priorityActive_priority` ON `${TABLE_NAME}` (`priorityActive`, `priority`)" + } + ], + "foreignKeys": [] + } + ], + "views": [], + "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, '0bb8836fe6777d5c9170a6881beae9fe')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/notimanager/data/model/NotificationMetaModel.kt b/app/src/main/java/com/example/notimanager/data/model/NotificationMetaModel.kt index b4dd3cb..cd738e5 100644 --- a/app/src/main/java/com/example/notimanager/data/model/NotificationMetaModel.kt +++ b/app/src/main/java/com/example/notimanager/data/model/NotificationMetaModel.kt @@ -19,6 +19,7 @@ data class NotificationMetaModel( @PrimaryKey val notificationId: Long, val intentActive: Boolean = true, - val intentArray: ByteArray + val intentArray: ByteArray, + val isRead: Boolean = false, ) diff --git a/app/src/main/java/com/example/notimanager/data/model/NotificationModel.kt b/app/src/main/java/com/example/notimanager/data/model/NotificationModel.kt index 514c38a..c7115d2 100644 --- a/app/src/main/java/com/example/notimanager/data/model/NotificationModel.kt +++ b/app/src/main/java/com/example/notimanager/data/model/NotificationModel.kt @@ -15,4 +15,5 @@ data class NotificationModel( val title: String, val content: String, val timestamp: Long, + val subText: String, ) diff --git a/app/src/main/java/com/example/notimanager/data/source/local/database/MigrationObject.kt b/app/src/main/java/com/example/notimanager/data/source/local/database/MigrationObject.kt index e409fb3..653807b 100644 --- a/app/src/main/java/com/example/notimanager/data/source/local/database/MigrationObject.kt +++ b/app/src/main/java/com/example/notimanager/data/source/local/database/MigrationObject.kt @@ -21,4 +21,11 @@ object MigrationObject { db.execSQL("CREATE INDEX index_app_icon_priorityActive_priority ON app_icon (priorityActive, priority);") } } + + val MIGRATION_7_8 = object : Migration(7, 8) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE notification ADD COLUMN subText TEXT NOT NULL DEFAULT '';") + db.execSQL("ALTER TABLE notification_meta ADD COLUMN isRead INTEGER NOT NULL DEFAULT '0';") + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/notimanager/data/source/local/database/NotiManagerDatabase.kt b/app/src/main/java/com/example/notimanager/data/source/local/database/NotiManagerDatabase.kt index d16c3c3..cc34ac6 100644 --- a/app/src/main/java/com/example/notimanager/data/source/local/database/NotiManagerDatabase.kt +++ b/app/src/main/java/com/example/notimanager/data/source/local/database/NotiManagerDatabase.kt @@ -15,9 +15,10 @@ import com.example.notimanager.data.source.local.dao.NotificationIconDao import com.example.notimanager.data.source.local.dao.NotificationMetaDao import com.example.notimanager.data.source.local.database.MigrationObject.MIGRATION_3_4 import com.example.notimanager.data.source.local.database.MigrationObject.MIGRATION_6_7 +import com.example.notimanager.data.source.local.database.MigrationObject.MIGRATION_7_8 @Database( - version = 7, + version = 8, entities = [ NotificationModel::class, @@ -26,7 +27,7 @@ import com.example.notimanager.data.source.local.database.MigrationObject.MIGRAT AppIconModel::class ], // autoMigrations = [ -// AutoMigration (from = 6, to = 7) +// AutoMigration (from = 7, to = 8) // ] ) abstract class NotiManagerDatabase : RoomDatabase() { @@ -48,7 +49,8 @@ abstract class NotiManagerDatabase : RoomDatabase() { ) .addMigrations( MIGRATION_3_4, - MIGRATION_6_7 + MIGRATION_6_7, + MIGRATION_7_8 ) .fallbackToDestructiveMigration() .build() From 72aaa489e97b348ff357b0c544ee74f3995a4657 Mon Sep 17 00:00:00 2001 From: Richter3766 <97567615+Richter3766@users.noreply.github.com> Date: Sat, 1 Mar 2025 09:38:17 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20NotiService=20=EC=95=B1=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=B2=98=EB=A6=AC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 메인 앱 아이콘은 패키지로부터 가져오고, 제목 아이콘부터는 LargeIcon을 쓰되, null일 경우 smallIcon 활용. 데이터 모델 변경에 따른 변경 사항 적용. --- .../domain/service/NotiListenerService.kt | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/example/notimanager/domain/service/NotiListenerService.kt b/app/src/main/java/com/example/notimanager/domain/service/NotiListenerService.kt index 76e9653..46e3701 100644 --- a/app/src/main/java/com/example/notimanager/domain/service/NotiListenerService.kt +++ b/app/src/main/java/com/example/notimanager/domain/service/NotiListenerService.kt @@ -8,10 +8,11 @@ import com.example.notimanager.data.model.NotificationIconModel import com.example.notimanager.data.model.NotificationMetaModel import com.example.notimanager.data.model.NotificationModel import com.example.notimanager.domain.repository.NotificationRepositoryInterface -import com.example.notimanager.data.utils.AppIconGetter.convertByteArray -import com.example.notimanager.data.utils.AppIconGetter.convertByteArrayWithColor -import com.example.notimanager.data.utils.NameGetter -import com.example.notimanager.data.utils.IntentHelper +import com.example.notimanager.domain.utils.AppIconGetter.convertByteArray +import com.example.notimanager.domain.utils.AppIconGetter.convertByteArrayWithColor +import com.example.notimanager.domain.utils.AppIconGetter.getAppIcon +import com.example.notimanager.domain.utils.NameGetter +import com.example.notimanager.domain.utils.IntentHelper import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -34,14 +35,15 @@ class NotiListenerService: NotificationListenerService() { val title = notification.extras.getString("android.title") val content = notification.extras.getString("android.text") + val subText = notification.extras.getString("android.subText") ?: "" if (title != null && content != null){ CoroutineScope(Dispatchers.IO).launch { mutex.withLock { - val id = insertNotification(appName, title, content, postTime) + val id = insertNotification(appName, title, content, postTime, subText) insertNotificationMeta(id, it.packageName) - insertNotificationIcon(id, notification.getLargeIcon()) - insertAppIcon(appName, notification.smallIcon, notification.color) + insertNotificationIcon(id, notification.getLargeIcon(), notification.smallIcon, notification.color) + insertAppIcon(appName, it.packageName) } } @@ -52,13 +54,15 @@ class NotiListenerService: NotificationListenerService() { appName: String, title: String, content: String, - postTime: Long + postTime: Long, + subText: String ): Long{ val notificationModel = NotificationModel( appName = appName, title = title, content = content, - timestamp = postTime + timestamp = postTime, + subText = subText ) return notificationRepository.insertNotification(notificationModel) } @@ -78,8 +82,18 @@ class NotiListenerService: NotificationListenerService() { notificationRepository.insertNotificationMeta(notificationMetaModel) } - private suspend fun insertNotificationIcon(id: Long, icon: Icon?){ - val byteArray = convertByteArray(this@NotiListenerService, icon) + private suspend fun insertNotificationIcon( + id: Long, + iconLarge: Icon?, + iconSmall: Icon?, + color: Int + ){ + val byteArray = if (iconLarge != null) { + convertByteArray(iconLarge.loadDrawable(this@NotiListenerService)) + } else { + convertByteArrayWithColor(iconSmall?.loadDrawable(this@NotiListenerService), color) + ?: ByteArray(0) + } val notificationIconModel = NotificationIconModel( notificationId = id, @@ -90,10 +104,10 @@ class NotiListenerService: NotificationListenerService() { private suspend fun insertAppIcon( appName: String, - icon: Icon?, - color: Int + packageName: String ){ - val byteArray = convertByteArrayWithColor(this@NotiListenerService, icon, color) + val byteArray = convertByteArray(getAppIcon(this@NotiListenerService, packageName)) + val appIconModel = AppIconModel( notiAppName = appName, iconBytes = byteArray From 73f57bd2c679d433b3822f254b034c6f1224a14f Mon Sep 17 00:00:00 2001 From: Richter3766 <97567615+Richter3766@users.noreply.github.com> Date: Sat, 1 Mar 2025 09:58:41 +0900 Subject: [PATCH 6/6] =?UTF-8?q?test:=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/NotificationRepositoryTest.kt | 8 ++++---- .../test/resources/json/data/model/NotificationModel.json | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/test/java/com/example/notimanager/data/repository/NotificationRepositoryTest.kt b/app/src/test/java/com/example/notimanager/data/repository/NotificationRepositoryTest.kt index 9b32c50..5f3a594 100644 --- a/app/src/test/java/com/example/notimanager/data/repository/NotificationRepositoryTest.kt +++ b/app/src/test/java/com/example/notimanager/data/repository/NotificationRepositoryTest.kt @@ -16,10 +16,10 @@ import io.mockk.coEvery import io.mockk.mockk class NotificationRepositoryTest: BehaviorSpec({ - val notificationDao: NotificationDao = mockk() - val notificationMetaDao: NotificationMetaDao = mockk() - val notificationIconDao: NotificationIconDao = mockk() - val appIconDao: AppIconDao = mockk() + val notificationDao = mockk() + val notificationMetaDao = mockk() + val notificationIconDao = mockk() + val appIconDao = mockk() val repository = NotificationRepository( notificationDao, notificationMetaDao, diff --git a/app/src/test/resources/json/data/model/NotificationModel.json b/app/src/test/resources/json/data/model/NotificationModel.json index 34d75f1..634015c 100644 --- a/app/src/test/resources/json/data/model/NotificationModel.json +++ b/app/src/test/resources/json/data/model/NotificationModel.json @@ -3,5 +3,6 @@ "appName" : "Weather App", "title": "Weather Update", "content": "Rain is expected in your area today.", - "timestamp": 1672531299000 + "timestamp": 1672531299000, + "subText": "test" } \ No newline at end of file