From a617d4eab0cf526332af793ce7a98e04bf8dc659 Mon Sep 17 00:00:00 2001 From: Richter3766 <97567615+Richter3766@users.noreply.github.com> Date: Wed, 5 Mar 2025 10:25:48 +0900 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=EB=A7=8C=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 제목과 내용이 빈 알림은 받지 않도록 개선. --- .../example/notimanager/domain/service/NotiListenerService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 be61606..45a97b0 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 @@ -40,7 +40,7 @@ class NotiListenerService: NotificationListenerService() { val postTime = sbn.postTime CoroutineScope(Dispatchers.IO).launch { - if (title == "" && content == "" && subText == "") return@launch + if (title == "" && content == "null") return@launch val filteredList = filterRepository.getSpecificFilteredList(appName, title, subText) if (filteredList.isNotEmpty()) return@launch From da632c14f8005230e67ab2cfaf17969a68610c5c Mon Sep 17 00:00:00 2001 From: Richter3766 <97567615+Richter3766@users.noreply.github.com> Date: Wed, 5 Mar 2025 13:51:24 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=ED=97=88?= =?UTF-8?q?=EC=9A=A9=20=EC=9A=94=EC=B2=AD=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 1 + .../NotificationPermissionRepository.kt | 13 +++++++++- ...tificationPermissionRepositoryInterface.kt | 3 ++- .../usecase/NotificationPermissionUseCase.kt | 24 ++++++++++++++++++- .../NotificationPermissionViewModel.kt | 12 +++++----- .../presentation/ui/activity/MainActivity.kt | 14 +++++++---- 6 files changed, 54 insertions(+), 13 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f7d40df..6f088d8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + = Build.VERSION_CODES.TIRAMISU) { + appContext.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED + } else { + true + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/notimanager/domain/repository/NotificationPermissionRepositoryInterface.kt b/app/src/main/java/com/example/notimanager/domain/repository/NotificationPermissionRepositoryInterface.kt index e43f19c..3af3b01 100644 --- a/app/src/main/java/com/example/notimanager/domain/repository/NotificationPermissionRepositoryInterface.kt +++ b/app/src/main/java/com/example/notimanager/domain/repository/NotificationPermissionRepositoryInterface.kt @@ -2,5 +2,6 @@ package com.example.notimanager.domain.repository interface NotificationPermissionRepositoryInterface { fun isNotificationServiceEnabled(): Boolean - fun requestPermission() + fun requestNotificationListenerPermission() + fun isNotificationPermissionGranted(): Boolean } \ No newline at end of file diff --git a/app/src/main/java/com/example/notimanager/domain/usecase/NotificationPermissionUseCase.kt b/app/src/main/java/com/example/notimanager/domain/usecase/NotificationPermissionUseCase.kt index 7c3d2a2..733873f 100644 --- a/app/src/main/java/com/example/notimanager/domain/usecase/NotificationPermissionUseCase.kt +++ b/app/src/main/java/com/example/notimanager/domain/usecase/NotificationPermissionUseCase.kt @@ -1,5 +1,8 @@ package com.example.notimanager.domain.usecase +import android.Manifest +import android.app.Activity +import android.os.Build import com.example.notimanager.domain.repository.NotificationPermissionRepositoryInterface class NotificationPermissionUseCase( @@ -11,7 +14,26 @@ class NotificationPermissionUseCase( fun requestPermission() { if (!repository.isNotificationServiceEnabled()) { - repository.requestPermission() + repository.requestNotificationListenerPermission() } } + + fun isNotificationPermissionGranted(): Boolean{ + return repository.isNotificationPermissionGranted() + } + + fun requestPermission(activity: Activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + activity.requestPermissions( + arrayOf(Manifest.permission.POST_NOTIFICATIONS), + NOTIFICATION_PERMISSION_REQUEST_CODE + ) + } else { + // Android 12 이하에서는 별도 권한이 필요하지 않음 + } + } + + companion object { + private const val NOTIFICATION_PERMISSION_REQUEST_CODE = 1 + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/notimanager/presentation/stateholder/viewmodel/NotificationPermissionViewModel.kt b/app/src/main/java/com/example/notimanager/presentation/stateholder/viewmodel/NotificationPermissionViewModel.kt index 54950fd..2501f0b 100644 --- a/app/src/main/java/com/example/notimanager/presentation/stateholder/viewmodel/NotificationPermissionViewModel.kt +++ b/app/src/main/java/com/example/notimanager/presentation/stateholder/viewmodel/NotificationPermissionViewModel.kt @@ -1,5 +1,6 @@ package com.example.notimanager.presentation.stateholder.viewmodel +import android.app.Activity import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -12,19 +13,18 @@ class NotificationPermissionViewModel @Inject constructor( private val notificationPermissionUseCase: NotificationPermissionUseCase ) : ViewModel() { - private val _isPermissionGranted = MutableLiveData() - val isPermissionGranted: LiveData get() = _isPermissionGranted + private val _isNotificationPermissionGranted = MutableLiveData() + val isNotificationPermissionGranted: LiveData get() = _isNotificationPermissionGranted init { checkNotificationPermission() } fun checkNotificationPermission() { - _isPermissionGranted.value = notificationPermissionUseCase.isNotificationServiceEnabled() + _isNotificationPermissionGranted.value = notificationPermissionUseCase.isNotificationPermissionGranted() } - fun requestPermission() { - notificationPermissionUseCase.requestPermission() - checkNotificationPermission() + fun requestPermission(activity: Activity) { + notificationPermissionUseCase.requestPermission(activity) } } 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 7969b4c..55243fd 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 @@ -6,13 +6,14 @@ import androidx.activity.compose.setContent import androidx.activity.viewModels import androidx.navigation.compose.rememberNavController import com.example.notimanager.presentation.stateholder.viewmodel.NotificationPermissionViewModel +import com.example.notimanager.presentation.stateholder.viewmodel.NotificationServicePermissionViewModel import com.example.notimanager.presentation.ui.navigation.AppNavHost import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class MainActivity : ComponentActivity() { - - private val viewModel: NotificationPermissionViewModel by viewModels() + private val serviceViewModel: NotificationServicePermissionViewModel by viewModels() + private val notificationViewModel: NotificationPermissionViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -20,11 +21,16 @@ class MainActivity : ComponentActivity() { setContent { AppNavHost(navController = rememberNavController()) } + + notificationViewModel.isNotificationPermissionGranted.observe(this) { isGranted -> + if (!isGranted) { + notificationViewModel.requestPermission(this) + } + } } override fun onResume() { super.onResume() - viewModel.checkNotificationPermission() + serviceViewModel.checkNotificationServicePermission() } - } From 18e8a24bd4ad1f09ae166e148d6f42fa8eafe7c7 Mon Sep 17 00:00:00 2001 From: Richter3766 <97567615+Richter3766@users.noreply.github.com> Date: Wed, 5 Mar 2025 13:52:01 +0900 Subject: [PATCH 3/4] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EA=B6=8C=ED=95=9C=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 알림 허용과 구분하기 위해 이름을 바꿈. --- .../com/example/notimanager/di/AppModule.kt | 6 ++-- .../NotificationServicePermissionViewModel.kt | 30 +++++++++++++++++++ .../ui/component/PermissionDialog.kt | 7 +++-- .../repository/NotificationPermissionTest.kt | 2 +- .../NotificationPermissionUseCaseTest.kt | 6 ++-- 5 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/com/example/notimanager/presentation/stateholder/viewmodel/NotificationServicePermissionViewModel.kt diff --git a/app/src/main/java/com/example/notimanager/di/AppModule.kt b/app/src/main/java/com/example/notimanager/di/AppModule.kt index 261ecbf..6efe0ff 100644 --- a/app/src/main/java/com/example/notimanager/di/AppModule.kt +++ b/app/src/main/java/com/example/notimanager/di/AppModule.kt @@ -1,7 +1,7 @@ package com.example.notimanager.di import com.example.notimanager.domain.usecase.NotificationPermissionUseCase -import com.example.notimanager.presentation.stateholder.viewmodel.NotificationPermissionViewModel +import com.example.notimanager.presentation.stateholder.viewmodel.NotificationServicePermissionViewModel import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -15,7 +15,7 @@ class AppModule { @Singleton fun provideNotificationPermissionViewModel( notificationPermissionUseCase: NotificationPermissionUseCase - ): NotificationPermissionViewModel { - return NotificationPermissionViewModel(notificationPermissionUseCase) + ): NotificationServicePermissionViewModel { + return NotificationServicePermissionViewModel(notificationPermissionUseCase) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/notimanager/presentation/stateholder/viewmodel/NotificationServicePermissionViewModel.kt b/app/src/main/java/com/example/notimanager/presentation/stateholder/viewmodel/NotificationServicePermissionViewModel.kt new file mode 100644 index 0000000..f37db9a --- /dev/null +++ b/app/src/main/java/com/example/notimanager/presentation/stateholder/viewmodel/NotificationServicePermissionViewModel.kt @@ -0,0 +1,30 @@ +package com.example.notimanager.presentation.stateholder.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.example.notimanager.domain.usecase.NotificationPermissionUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class NotificationServicePermissionViewModel @Inject constructor( + private val notificationPermissionUseCase: NotificationPermissionUseCase +) : ViewModel() { + + private val _isNotificationServiceEnabled = MutableLiveData() + val isNotificationServiceEnabled: LiveData get() = _isNotificationServiceEnabled + + init { + checkNotificationServicePermission() + } + + fun checkNotificationServicePermission() { + _isNotificationServiceEnabled.value = notificationPermissionUseCase.isNotificationServiceEnabled() + } + + fun requestServicePermission() { + notificationPermissionUseCase.requestPermission() + checkNotificationServicePermission() + } +} diff --git a/app/src/main/java/com/example/notimanager/presentation/ui/component/PermissionDialog.kt b/app/src/main/java/com/example/notimanager/presentation/ui/component/PermissionDialog.kt index 5689caf..f7dd5ff 100644 --- a/app/src/main/java/com/example/notimanager/presentation/ui/component/PermissionDialog.kt +++ b/app/src/main/java/com/example/notimanager/presentation/ui/component/PermissionDialog.kt @@ -11,16 +11,17 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.hilt.navigation.compose.hiltViewModel import com.example.notimanager.presentation.stateholder.viewmodel.NotificationPermissionViewModel +import com.example.notimanager.presentation.stateholder.viewmodel.NotificationServicePermissionViewModel @Composable -fun PermissionCheck(viewModel: NotificationPermissionViewModel = hiltViewModel()){ - val isPermissionGranted by viewModel.isPermissionGranted.observeAsState() +fun PermissionCheck(viewModel: NotificationServicePermissionViewModel = hiltViewModel()){ + val isPermissionGranted by viewModel.isNotificationServiceEnabled.observeAsState() if (isPermissionGranted == false) { SimplePermissionDialog( title = "알림 권한 필요", message = "알림을 받기 위해 알림 권한이 필요합니다.", - onAllow = { viewModel.requestPermission() }, + onAllow = { viewModel.requestServicePermission() }, ) } } diff --git a/app/src/test/java/com/example/notimanager/data/repository/NotificationPermissionTest.kt b/app/src/test/java/com/example/notimanager/data/repository/NotificationPermissionTest.kt index 1504478..6d9b0d6 100644 --- a/app/src/test/java/com/example/notimanager/data/repository/NotificationPermissionTest.kt +++ b/app/src/test/java/com/example/notimanager/data/repository/NotificationPermissionTest.kt @@ -71,7 +71,7 @@ class NotificationPermissionRepositoryTest : BehaviorSpec({ val intentSlot = slot() When("requestPermission 호출") { - repository.requestPermission() + repository.requestNotificationListenerPermission() Then("알림 설정 화면을 여는 Intent가 시작되어야 한다") { verify { mockContext.startActivity(capture(intentSlot)) } diff --git a/app/src/test/java/com/example/notimanager/domain/usecase/NotificationPermissionUseCaseTest.kt b/app/src/test/java/com/example/notimanager/domain/usecase/NotificationPermissionUseCaseTest.kt index 29c25f6..c61b808 100644 --- a/app/src/test/java/com/example/notimanager/domain/usecase/NotificationPermissionUseCaseTest.kt +++ b/app/src/test/java/com/example/notimanager/domain/usecase/NotificationPermissionUseCaseTest.kt @@ -27,11 +27,11 @@ class NotificationPermissionUseCaseTest : BehaviorSpec({ When("알림 서비스가 비활성화되어 있다면") { every { repository.isNotificationServiceEnabled() } returns false - every { repository.requestPermission() } returns Unit + every { repository.requestNotificationListenerPermission() } returns Unit Then("권한 요청을 해야 한다") { useCase.requestPermission() - verify { repository.requestPermission() } + verify { repository.requestNotificationListenerPermission() } } } } @@ -50,7 +50,7 @@ class NotificationPermissionUseCaseTest : BehaviorSpec({ Then("권한 요청을 하지 않아야 한다") { useCase.requestPermission() - verify(exactly = 0) { repository.requestPermission() } + verify(exactly = 0) { repository.requestNotificationListenerPermission() } } } } From adff6287bbf2735ce4bc39edb05e71d2862611fb Mon Sep 17 00:00:00 2001 From: Richter3766 <97567615+Richter3766@users.noreply.github.com> Date: Wed, 5 Mar 2025 13:52:19 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20=EB=B9=88=20content=EC=97=90=20null?= =?UTF-8?q?=EB=A1=9C=20=EB=82=98=EC=98=A4=EB=8A=94=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 --- .../example/notimanager/domain/service/NotiListenerService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 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 45a97b0..5010f69 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 @@ -36,11 +36,11 @@ class NotiListenerService: NotificationListenerService() { val appName = NameGetter.getAppName(this, sbn) val title = notification.extras.getString("android.title") ?: "" val subText = notification.extras.getString("android.subText") ?: "" - val content = notification.extras.getCharSequence("android.text").toString() ?: "" + val content = notification.extras.getCharSequence("android.text")?.toString() ?: "" val postTime = sbn.postTime CoroutineScope(Dispatchers.IO).launch { - if (title == "" && content == "null") return@launch + if (title == "" && content == "") return@launch val filteredList = filterRepository.getSpecificFilteredList(appName, title, subText) if (filteredList.isNotEmpty()) return@launch