From 5416c0ca5bea8ab09659c438cf46d80bc55df996 Mon Sep 17 00:00:00 2001
From: Richter3766 <97567615+Richter3766@users.noreply.github.com>
Date: Thu, 6 Mar 2025 12:00:50 +0900
Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?=
=?UTF-8?q?=EC=9D=B4=EB=A6=84=20=ED=86=B5=EC=9D=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Notimanager로 이름 통일
---
app/src/main/res/values/strings.xml | 2 +-
app/src/main/res/values/themes.xml | 2 +-
settings.gradle.kts | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e89c2ae..15b4d85 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,3 @@
- NotiManger
+ NotiManager
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 98fbe13..4d04581 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 6371a51..c1fdb14 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -19,5 +19,5 @@ dependencyResolutionManagement {
}
}
-rootProject.name = "NotiManger"
+rootProject.name = "NotiManager"
include(":app")
From bd52bf368bf08bc71531ccccbfa29bd88da9a5a8 Mon Sep 17 00:00:00 2001
From: Richter3766 <97567615+Richter3766@users.noreply.github.com>
Date: Thu, 6 Mar 2025 12:02:26 +0900
Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=ED=8F=AC=EA=B7=B8=EB=9D=BC?=
=?UTF-8?q?=EC=9A=B4=EB=93=9C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B5=AC?=
=?UTF-8?q?=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
androidManifest에 포그라운드 서비스 등록
해당 서비스에서 받은 알림을 상태바에서 보여줌.
---
app/src/main/AndroidManifest.xml | 13 ++-
.../domain/service/ForegroundNotiService.kt | 93 +++++++++++++++++++
2 files changed, 103 insertions(+), 3 deletions(-)
create mode 100644 app/src/main/java/com/example/notimanager/domain/service/ForegroundNotiService.kt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6f088d8..83e1df2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,7 +1,10 @@
+
+
+ android:theme="@style/Theme.NotiManager">
@@ -34,7 +37,11 @@
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/example/notimanager/domain/service/ForegroundNotiService.kt b/app/src/main/java/com/example/notimanager/domain/service/ForegroundNotiService.kt
new file mode 100644
index 0000000..a35926f
--- /dev/null
+++ b/app/src/main/java/com/example/notimanager/domain/service/ForegroundNotiService.kt
@@ -0,0 +1,93 @@
+package com.example.notimanager.domain.service
+
+import android.Manifest
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.Service
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.IBinder
+import androidx.core.app.ActivityCompat
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import com.example.notimanager.R
+import com.example.notimanager.presentation.ui.activity.MainActivity
+
+class ForegroundNotiService: Service() {
+ private val channelId = "NotiManagerChannel"
+ private val groupId = "NotiManagerGroup"
+
+ override fun onCreate() {
+ super.onCreate()
+ createNotificationChannel()
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ val clearGroup = intent?.extras?.getBoolean("clearGroup") ?: false
+ if (clearGroup) clearGroupNotifications()
+
+ val appName = intent?.extras?.getString("appName") ?: ""
+ val content = intent?.extras?.getString("content") ?: ""
+ val isGroupSummary = intent?.extras?.getBoolean("isGroupSummary") ?: false
+ putNotification(appName, content, isGroupSummary)
+
+ return START_STICKY
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ return null
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ stopForeground(STOP_FOREGROUND_REMOVE)
+ }
+
+ private fun putNotification(
+ appName: String,
+ content: String,
+ isGroupSummary: Boolean) {
+ val notificationIntent = Intent(this, MainActivity::class.java).apply {
+ putExtra("appName", appName)
+ }
+ val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,
+ PendingIntent.FLAG_IMMUTABLE)
+
+ val notification = NotificationCompat.Builder(this, channelId)
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentTitle(appName)
+ .setContentText(content)
+ .setContentIntent(pendingIntent)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setGroup(groupId)
+ .setAutoCancel(true)
+ .setOngoing(true)
+ .setGroupSummary(isGroupSummary)
+ .build()
+
+ if (ActivityCompat.checkSelfPermission(
+ this,
+ Manifest.permission.POST_NOTIFICATIONS
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ return
+ }
+
+ NotificationManagerCompat.from(this).notify(appName.hashCode(), notification)
+ }
+
+ private fun createNotificationChannel() {
+ val channel = NotificationChannel(
+ channelId,
+ "NotiManager Channel",
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ val manager = getSystemService(NotificationManager::class.java)
+ manager.createNotificationChannel(channel)
+ }
+
+ private fun clearGroupNotifications() {
+ NotificationManagerCompat.from(this).cancelAll()
+ }
+}
\ No newline at end of file
From 6b442d8564d7a4bff393e0099ee4ccefe4b70eb9 Mon Sep 17 00:00:00 2001
From: Richter3766 <97567615+Richter3766@users.noreply.github.com>
Date: Thu, 6 Mar 2025 12:03:28 +0900
Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=ED=8F=AC=EA=B7=B8=EB=9D=BC?=
=?UTF-8?q?=EC=9A=B4=EB=93=9C=20=EC=84=9C=EB=B9=84=EC=8A=A4=EC=97=90=20?=
=?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EB=93=B1=EB=A1=9D=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
NotiListenerService에서 알림을 받으면
포그라운드 서비스에 해당 앱 알림을 띄우도록 함.
그리고 MainActivity 실행 시 포그라운드 서비스 실행.
---
.../data/repository/NotificationRepository.kt | 5 ++++
.../data/source/local/dao/NotificationDao.kt | 8 ++++++
.../NotificationRepositoryInterface.kt | 1 +
.../domain/service/NotiListenerService.kt | 16 ++++++++++-
.../presentation/ui/activity/MainActivity.kt | 27 ++++++++++++++++++-
5 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/com/example/notimanager/data/repository/NotificationRepository.kt b/app/src/main/java/com/example/notimanager/data/repository/NotificationRepository.kt
index 01e37b0..b51cae2 100644
--- a/app/src/main/java/com/example/notimanager/data/repository/NotificationRepository.kt
+++ b/app/src/main/java/com/example/notimanager/data/repository/NotificationRepository.kt
@@ -31,4 +31,9 @@ class NotificationRepository(
override suspend fun insertAppIcon(appIconModel: AppIconModel): Long {
return appIconDao.insert(appIconModel)
}
+
+ override suspend fun getPriorityNotificationCount(appName: String): Int{
+ return notificationDao.getPriorityNotificationCount(appName)
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/notimanager/data/source/local/dao/NotificationDao.kt b/app/src/main/java/com/example/notimanager/data/source/local/dao/NotificationDao.kt
index 26cceb6..d2a4203 100644
--- a/app/src/main/java/com/example/notimanager/data/source/local/dao/NotificationDao.kt
+++ b/app/src/main/java/com/example/notimanager/data/source/local/dao/NotificationDao.kt
@@ -132,4 +132,12 @@ interface NotificationDao {
@Query("UPDATE Notification SET isRead = 1 WHERE appName = :appName AND subText = :subText")
suspend fun updateSubTextAsRead(appName: String, subText: String): Int
+
+ @Query("""
+ SELECT COUNT(*)
+ FROM Notification AS n
+ INNER JOIN app_icon AS ai ON n.appName = ai.notiAppName AND ai.priorityActive = 1
+ WHERE n.appName = :appName AND n.isRead = 0
+ """)
+ suspend fun getPriorityNotificationCount(appName: String): Int
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/notimanager/domain/repository/NotificationRepositoryInterface.kt b/app/src/main/java/com/example/notimanager/domain/repository/NotificationRepositoryInterface.kt
index 93133bb..6756966 100644
--- a/app/src/main/java/com/example/notimanager/domain/repository/NotificationRepositoryInterface.kt
+++ b/app/src/main/java/com/example/notimanager/domain/repository/NotificationRepositoryInterface.kt
@@ -10,4 +10,5 @@ interface NotificationRepositoryInterface {
suspend fun insertNotificationMeta(metaModel: NotificationMetaModel): Long
suspend fun insertNotificationIcon(notificationIconModel: NotificationIconModel): Long
suspend fun insertAppIcon(appIconModel: AppIconModel): Long
+ suspend fun getPriorityNotificationCount(appName: String): Int
}
\ No newline at end of file
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 5010f69..2f04411 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
@@ -1,5 +1,6 @@
package com.example.notimanager.domain.service
+import android.content.Intent
import android.graphics.drawable.Icon
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
@@ -34,7 +35,7 @@ class NotiListenerService: NotificationListenerService() {
val notification = sbn.notification
val appName = NameGetter.getAppName(this, sbn)
- val title = notification.extras.getString("android.title") ?: ""
+ val title = notification.extras.getCharSequence("android.title")?.toString() ?: ""
val subText = notification.extras.getString("android.subText") ?: ""
val content = notification.extras.getCharSequence("android.text")?.toString() ?: ""
val postTime = sbn.postTime
@@ -50,6 +51,7 @@ class NotiListenerService: NotificationListenerService() {
insertNotificationMeta(id, sbn.packageName)
insertNotificationIcon(id, notification.getLargeIcon(), notification.smallIcon, notification.color)
insertAppIcon(appName, sbn.packageName)
+ putNotification(appName)
}
}
}
@@ -75,6 +77,7 @@ class NotiListenerService: NotificationListenerService() {
id: Long,
notificationPackage: String?,
){
+ // TODO: 원래 pendingIntent의 extras 추출해서 넣어보기
val intent = packageManager.getLaunchIntentForPackage(notificationPackage ?: "")
val intentArray = intent?.let { IntentHelper.saveIntent(it) }
@@ -118,5 +121,16 @@ class NotiListenerService: NotificationListenerService() {
)
notificationRepository.insertAppIcon(appIconModel)
}
+
+ private suspend fun putNotification(appName: String){
+ val count = notificationRepository.getPriorityNotificationCount(appName)
+ if (count != 0){
+ val serviceIntent = Intent(this, ForegroundNotiService::class.java).apply {
+ putExtra("appName", appName)
+ putExtra("content", count.toString())
+ }
+ startService(serviceIntent)
+ }
+ }
}
diff --git a/app/src/main/java/com/example/notimanager/presentation/ui/activity/MainActivity.kt b/app/src/main/java/com/example/notimanager/presentation/ui/activity/MainActivity.kt
index 55243fd..8e4f025 100644
--- a/app/src/main/java/com/example/notimanager/presentation/ui/activity/MainActivity.kt
+++ b/app/src/main/java/com/example/notimanager/presentation/ui/activity/MainActivity.kt
@@ -1,10 +1,15 @@
package com.example.notimanager.presentation.ui.activity
+import android.content.Intent
import android.os.Bundle
+import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
+import androidx.navigation.NavController
+import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
+import com.example.notimanager.domain.service.ForegroundNotiService
import com.example.notimanager.presentation.stateholder.viewmodel.NotificationPermissionViewModel
import com.example.notimanager.presentation.stateholder.viewmodel.NotificationServicePermissionViewModel
import com.example.notimanager.presentation.ui.navigation.AppNavHost
@@ -14,12 +19,14 @@ import dagger.hilt.android.AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val serviceViewModel: NotificationServicePermissionViewModel by viewModels()
private val notificationViewModel: NotificationPermissionViewModel by viewModels()
+ private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
- AppNavHost(navController = rememberNavController())
+ navController = rememberNavController()
+ AppNavHost(navController = navController as NavHostController)
}
notificationViewModel.isNotificationPermissionGranted.observe(this) { isGranted ->
@@ -27,10 +34,28 @@ class MainActivity : ComponentActivity() {
notificationViewModel.requestPermission(this)
}
}
+ val serviceIntent = Intent(this, ForegroundNotiService::class.java).apply {
+ putExtra("clearGroup", true)
+ putExtra("appName", "NotiManager")
+ putExtra("content", "실행 중입니다.")
+ putExtra("isGroupSummary", true)
+ }
+ startService(serviceIntent)
}
override fun onResume() {
super.onResume()
serviceViewModel.checkNotificationServicePermission()
}
+
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+ val appName = intent.extras?.getString("appName") ?: ""
+ Log.i("앱 이름", appName)
+ if (appName == ""){
+ navController.navigate("mainScreen")
+ }
+
+ navController.navigate("titleScreen/$appName")
+ }
}
From 556f5d7793c73064d58e137a9b036665fd28c967 Mon Sep 17 00:00:00 2001
From: Richter3766 <97567615+Richter3766@users.noreply.github.com>
Date: Thu, 6 Mar 2025 13:18:12 +0900
Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=ED=81=B4?=
=?UTF-8?q?=EB=A6=AD=20=EC=8B=9C=20Intent=20=EB=8F=99=EC=9E=91=20=EA=B5=AC?=
=?UTF-8?q?=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
중요 알림을 클릭하면 해당 앱 제목 페이지로 이동하도록 함.
---
.../notimanager/domain/service/ForegroundNotiService.kt | 7 +++++--
.../notimanager/presentation/ui/activity/MainActivity.kt | 8 +++-----
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/com/example/notimanager/domain/service/ForegroundNotiService.kt b/app/src/main/java/com/example/notimanager/domain/service/ForegroundNotiService.kt
index a35926f..14b52ce 100644
--- a/app/src/main/java/com/example/notimanager/domain/service/ForegroundNotiService.kt
+++ b/app/src/main/java/com/example/notimanager/domain/service/ForegroundNotiService.kt
@@ -51,8 +51,11 @@ class ForegroundNotiService: Service() {
val notificationIntent = Intent(this, MainActivity::class.java).apply {
putExtra("appName", appName)
}
- val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,
- PendingIntent.FLAG_IMMUTABLE)
+ val pendingIntent = PendingIntent
+ .getActivity(this,
+ 0,
+ notificationIntent,
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
val notification = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
diff --git a/app/src/main/java/com/example/notimanager/presentation/ui/activity/MainActivity.kt b/app/src/main/java/com/example/notimanager/presentation/ui/activity/MainActivity.kt
index 8e4f025..9dd14d4 100644
--- a/app/src/main/java/com/example/notimanager/presentation/ui/activity/MainActivity.kt
+++ b/app/src/main/java/com/example/notimanager/presentation/ui/activity/MainActivity.kt
@@ -24,9 +24,11 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ val appName = intent.extras?.getString("appName") ?: ""
setContent {
navController = rememberNavController()
AppNavHost(navController = navController as NavHostController)
+ if (appName != "" && appName != "NotiManager") navController.navigate("titleScreen/$appName")
}
notificationViewModel.isNotificationPermissionGranted.observe(this) { isGranted ->
@@ -51,11 +53,7 @@ class MainActivity : ComponentActivity() {
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
val appName = intent.extras?.getString("appName") ?: ""
- Log.i("앱 이름", appName)
- if (appName == ""){
- navController.navigate("mainScreen")
- }
+ if (appName != "" && appName != "NotiManager") navController.navigate("titleScreen/$appName")
- navController.navigate("titleScreen/$appName")
}
}