11package com.ninecraft.booket.feature.settings.notification
22
3+ import android.content.Context
34import android.content.Intent
4- import android.net.Uri
5+ import android.content.pm.PackageManager
6+ import android.os.Build
57import android.provider.Settings
68import androidx.activity.compose.rememberLauncherForActivityResult
79import androidx.activity.result.contract.ActivityResultContracts
10+ import androidx.annotation.RequiresApi
811import androidx.compose.foundation.background
912import androidx.compose.foundation.layout.Arrangement
1013import androidx.compose.foundation.layout.Column
@@ -18,12 +21,19 @@ import androidx.compose.foundation.shape.RoundedCornerShape
1821import androidx.compose.material3.Icon
1922import androidx.compose.material3.Text
2023import androidx.compose.runtime.Composable
24+ import androidx.compose.runtime.LaunchedEffect
25+ import androidx.compose.runtime.getValue
26+ import androidx.compose.runtime.produceState
2127import androidx.compose.ui.Alignment
2228import androidx.compose.ui.Modifier
2329import androidx.compose.ui.graphics.vector.ImageVector
2430import androidx.compose.ui.platform.LocalContext
2531import androidx.compose.ui.res.stringResource
2632import androidx.compose.ui.res.vectorResource
33+ import androidx.core.content.ContextCompat
34+ import androidx.lifecycle.Lifecycle
35+ import androidx.lifecycle.LifecycleEventObserver
36+ import androidx.lifecycle.compose.LocalLifecycleOwner
2737import com.ninecraft.booket.core.common.extensions.noRippleClickable
2838import com.ninecraft.booket.core.designsystem.DevicePreview
2939import com.ninecraft.booket.core.designsystem.theme.ReedTheme
@@ -43,6 +53,33 @@ internal fun NotificationUi(
4353 state : NotificationUiState ,
4454 modifier : Modifier = Modifier ,
4555) {
56+ val context = LocalContext .current
57+ val lifecycleOwner = LocalLifecycleOwner .current
58+
59+ val isGranted by produceState(
60+ initialValue = checkNotificationPermission(context),
61+ key1 = lifecycleOwner,
62+ ) {
63+ // 포그라운드 복귀 시 OS 권한 동기화
64+ val observer = LifecycleEventObserver { _, event ->
65+ if (event == Lifecycle .Event .ON_RESUME ) {
66+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
67+ value = checkNotificationPermission(context)
68+ }
69+ }
70+ }
71+ lifecycleOwner.lifecycle.addObserver(observer)
72+ awaitDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
73+ }
74+
75+ val settingsLauncher = rememberLauncherForActivityResult(
76+ contract = ActivityResultContracts .StartActivityForResult (),
77+ ) { _ -> }
78+
79+ val intent = Intent (Settings .ACTION_APP_NOTIFICATION_SETTINGS ).apply {
80+ putExtra(Settings .EXTRA_APP_PACKAGE , context.packageName)
81+ }
82+
4683 ReedScaffold (
4784 modifier = modifier.fillMaxSize(),
4885 containerColor = White ,
@@ -59,42 +96,47 @@ internal fun NotificationUi(
5996 state.eventSink(NotificationUiEvent .OnBackClick )
6097 },
6198 )
99+ if (! isGranted) {
100+ NotificationGuideItem (
101+ onClick = {
102+ settingsLauncher.launch(intent)
103+ },
104+ )
105+ }
62106 Spacer (modifier = Modifier .height(ReedTheme .spacing.spacing2))
63- NotificationGuideItem ()
64- Spacer (modifier = Modifier .height(ReedTheme .spacing.spacing4))
65107 ToggleItem (
66108 title = stringResource(R .string.notification_toggle_title),
67109 description = stringResource(R .string.notification_toggle_description),
68- isChecked = state.isNotificationEnabled,
110+ isChecked = isGranted && state.isNotificationEnabled,
69111 onCheckedChange = { enabled ->
70- state.eventSink(NotificationUiEvent .OnNotificationToggle (enabled))
112+ if (isGranted) {
113+ state.eventSink(NotificationUiEvent .OnNotificationToggle (enabled))
114+ } else {
115+ settingsLauncher.launch(intent)
116+ }
71117 },
72118 )
73119 }
74120 }
75121}
76122
77123@Composable
78- internal fun NotificationGuideItem () {
79- val context = LocalContext .current
80- val settingsLauncher = rememberLauncherForActivityResult(
81- contract = ActivityResultContracts .StartActivityForResult (),
82- ) { _ -> }
83-
124+ internal fun NotificationGuideItem (
125+ onClick : () -> Unit ,
126+ modifier : Modifier = Modifier ,
127+ ) {
84128 Row (
85- modifier = Modifier
86- .padding(horizontal = ReedTheme .spacing.spacing5)
129+ modifier = modifier
130+ .padding(
131+ vertical = ReedTheme .spacing.spacing2,
132+ horizontal = ReedTheme .spacing.spacing5,
133+ )
87134 .fillMaxWidth()
88135 .background(
89136 color = ReedTheme .colors.baseSecondary,
90137 shape = RoundedCornerShape (ReedTheme .radius.md),
91138 )
92- .noRippleClickable {
93- val intent = Intent (Settings .ACTION_APPLICATION_DETAILS_SETTINGS ).apply {
94- data = Uri .fromParts(" package" , context.packageName, null )
95- }
96- settingsLauncher.launch(intent)
97- }
139+ .noRippleClickable { onClick() }
98140 .padding(
99141 vertical = ReedTheme .spacing.spacing6,
100142 horizontal = ReedTheme .spacing.spacing5,
@@ -122,6 +164,15 @@ internal fun NotificationGuideItem() {
122164 }
123165}
124166
167+ private fun checkNotificationPermission (context : Context ): Boolean {
168+ return if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
169+ ContextCompat .checkSelfPermission(context, android.Manifest .permission.POST_NOTIFICATIONS ) == PackageManager .PERMISSION_GRANTED
170+ } else {
171+ true
172+ }
173+ }
174+
175+ @RequiresApi(Build .VERSION_CODES .TIRAMISU )
125176@DevicePreview
126177@Composable
127178private fun NotificationUiPreview () {
0 commit comments