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/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/ForegroundNotiService.kt b/app/src/main/java/com/example/notimanager/domain/service/ForegroundNotiService.kt new file mode 100644 index 0000000..14b52ce --- /dev/null +++ b/app/src/main/java/com/example/notimanager/domain/service/ForegroundNotiService.kt @@ -0,0 +1,96 @@ +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 or PendingIntent.FLAG_UPDATE_CURRENT) + + 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 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..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 @@ -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,16 @@ 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) + val appName = intent.extras?.getString("appName") ?: "" setContent { - AppNavHost(navController = rememberNavController()) + navController = rememberNavController() + AppNavHost(navController = navController as NavHostController) + if (appName != "" && appName != "NotiManager") navController.navigate("titleScreen/$appName") } notificationViewModel.isNotificationPermissionGranted.observe(this) { isGranted -> @@ -27,10 +36,24 @@ 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") ?: "" + if (appName != "" && appName != "NotiManager") navController.navigate("titleScreen/$appName") + + } } 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 @@ -