11package com.unifest.android.feature.booth
22
3+ import android.Manifest
4+ import android.content.Intent
5+ import android.net.Uri
6+ import android.os.Build
7+ import android.provider.Settings
38import android.widget.Toast
9+ import androidx.activity.compose.rememberLauncherForActivityResult
10+ import androidx.activity.result.contract.ActivityResultContracts
411import androidx.compose.foundation.background
512import androidx.compose.foundation.isSystemInDarkTheme
613import androidx.compose.foundation.layout.Box
@@ -20,9 +27,13 @@ import androidx.compose.material3.SnackbarHostState
2027import androidx.compose.material3.Text
2128import androidx.compose.runtime.Composable
2229import androidx.compose.runtime.DisposableEffect
30+ import androidx.compose.runtime.LaunchedEffect
2331import androidx.compose.runtime.getValue
32+ import androidx.compose.runtime.mutableStateOf
2433import androidx.compose.runtime.remember
2534import androidx.compose.runtime.rememberCoroutineScope
35+ import androidx.compose.runtime.setValue
36+ import androidx.compose.runtime.snapshotFlow
2637import androidx.compose.ui.Alignment
2738import androidx.compose.ui.Modifier
2839import androidx.compose.ui.graphics.Color
@@ -35,9 +46,9 @@ import androidx.compose.ui.unit.dp
3546import androidx.hilt.navigation.compose.hiltViewModel
3647import androidx.lifecycle.compose.collectAsStateWithLifecycle
3748import com.unifest.android.core.common.ObserveAsEvents
49+ import com.unifest.android.core.common.PermissionDialogButtonType
50+ import com.unifest.android.core.common.extension.checkNotificationPermission
3851import com.unifest.android.core.common.extension.findActivity
39- import com.unifest.android.core.common.extension.navigateToAppSetting
40- import com.unifest.android.core.designsystem.R as designR
4152import com.unifest.android.core.designsystem.component.LoadingWheel
4253import com.unifest.android.core.designsystem.component.NetworkErrorDialog
4354import com.unifest.android.core.designsystem.component.NetworkImage
@@ -51,6 +62,8 @@ import com.unifest.android.core.designsystem.theme.Title2
5162import com.unifest.android.core.designsystem.theme.UnifestTheme
5263import com.unifest.android.core.ui.DevicePreview
5364import com.unifest.android.core.ui.component.NoShowAlertDialog
65+ import com.unifest.android.core.ui.component.NotificationPermissionTextProvider
66+ import com.unifest.android.core.ui.component.PermissionDialog
5467import com.unifest.android.core.ui.component.WaitingConfirmDialog
5568import com.unifest.android.core.ui.component.WaitingDialog
5669import com.unifest.android.core.ui.component.WaitingPinDialog
@@ -65,8 +78,10 @@ import com.unifest.android.feature.booth.viewmodel.BoothViewModel
6578import com.unifest.android.feature.booth.viewmodel.ErrorType
6679import kotlinx.collections.immutable.toImmutableList
6780import kotlinx.coroutines.delay
81+ import kotlinx.coroutines.flow.distinctUntilChanged
6882import kotlinx.coroutines.launch
6983import tech.thdev.compose.exteions.system.ui.controller.rememberExSystemUiController
84+ import com.unifest.android.core.designsystem.R as designR
7085
7186private const val SnackBarDuration = 1000L
7287
@@ -87,6 +102,8 @@ internal fun BoothDetailRoute(
87102 val uriHandler = LocalUriHandler .current
88103 val activity = context.findActivity()
89104
105+ var isNotificationPermissionGranted by remember { mutableStateOf(activity.checkNotificationPermission()) }
106+
90107 DisposableEffect (systemUiController) {
91108 systemUiController.setStatusBarColor(
92109 color = Color .Transparent ,
@@ -100,13 +117,60 @@ internal fun BoothDetailRoute(
100117 }
101118 }
102119
120+ LaunchedEffect (Unit ) {
121+ snapshotFlow { activity.checkNotificationPermission() }
122+ .distinctUntilChanged()
123+ .collect { isGranted ->
124+ isNotificationPermissionGranted = isGranted
125+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
126+ viewModel.onPermissionResult(
127+ permission = Manifest .permission.POST_NOTIFICATIONS ,
128+ isGranted = isGranted,
129+ )
130+ }
131+ }
132+ }
133+
134+ val notificationPermissionLauncher = rememberLauncherForActivityResult(
135+ contract = ActivityResultContracts .RequestPermission (),
136+ onResult = { isGranted ->
137+ isNotificationPermissionGranted = isGranted
138+
139+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
140+ viewModel.onPermissionResult(Manifest .permission.POST_NOTIFICATIONS , isGranted)
141+ }
142+ },
143+ )
144+
145+ val settingsLauncher = rememberLauncherForActivityResult(
146+ contract = ActivityResultContracts .StartActivityForResult (),
147+ onResult = {
148+ // 설정에서 돌아왔을 때 권한 상태를 다시 확인
149+ isNotificationPermissionGranted = activity.checkNotificationPermission()
150+
151+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
152+ viewModel.onPermissionResult(
153+ permission = Manifest .permission.POST_NOTIFICATIONS ,
154+ isGranted = isNotificationPermissionGranted,
155+ )
156+ }
157+ },
158+ )
159+
103160 ObserveAsEvents (flow = viewModel.uiEvent) { event ->
104161 when (event) {
105162 is BoothUiEvent .NavigateBack -> popBackStack()
106163 is BoothUiEvent .NavigateToBoothLocation -> navigateToBoothLocation()
107164 is BoothUiEvent .NavigateToWaiting -> navigateToWaiting()
108165 is BoothUiEvent .NavigateToPrivatePolicy -> uriHandler.openUri(BuildConfig .UNIFEST_PRIVATE_POLICY_URL )
109166 is BoothUiEvent .NavigateToThirdPartyPolicy -> uriHandler.openUri(BuildConfig .UNIFEST_THIRD_PARTY_POLICY_URL )
167+ is BoothUiEvent .NavigateToAppSetting -> {
168+ val intent = Intent (Settings .ACTION_APPLICATION_DETAILS_SETTINGS ).apply {
169+ data = Uri .fromParts(" package" , activity.packageName, null )
170+ }
171+ settingsLauncher.launch(intent)
172+ }
173+
110174 is BoothUiEvent .ShowSnackBar -> {
111175 scope.launch {
112176 val job = launch {
@@ -121,10 +185,49 @@ internal fun BoothDetailRoute(
121185 }
122186
123187 is BoothUiEvent .ShowToast -> Toast .makeText(context, event.message.asString(context), Toast .LENGTH_SHORT ).show()
124- is BoothUiEvent .NavigateToAppSetting -> activity.navigateToAppSetting()
188+ is BoothUiEvent .RequestPermission -> {
189+ when (event.permission) {
190+ Manifest .permission.POST_NOTIFICATIONS -> {
191+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
192+ notificationPermissionLauncher.launch(Manifest .permission.POST_NOTIFICATIONS )
193+ }
194+ }
195+ }
196+ }
125197 }
126198 }
127199
200+ if (uiState.isNotificationPermissionDialogVisible && Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU && ! isNotificationPermissionGranted) {
201+ PermissionDialog (
202+ permissionTextProvider = NotificationPermissionTextProvider (),
203+ isPermanentlyDeclined = ! activity.shouldShowRequestPermissionRationale(Manifest .permission.POST_NOTIFICATIONS ),
204+ onDismiss = {
205+ viewModel.onAction(
206+ BoothUiAction .OnPermissionDialogButtonClick (
207+ buttonType = PermissionDialogButtonType .DISMISS ,
208+ permission = Manifest .permission.POST_NOTIFICATIONS ,
209+ ),
210+ )
211+ },
212+ navigateToAppSetting = {
213+ viewModel.onAction(
214+ BoothUiAction .OnPermissionDialogButtonClick (
215+ buttonType = PermissionDialogButtonType .NAVIGATE_TO_APP_SETTING ,
216+ permission = Manifest .permission.POST_NOTIFICATIONS ,
217+ ),
218+ )
219+ },
220+ onConfirm = {
221+ viewModel.onAction(
222+ BoothUiAction .OnPermissionDialogButtonClick (
223+ buttonType = PermissionDialogButtonType .CONFIRM ,
224+ permission = Manifest .permission.POST_NOTIFICATIONS ,
225+ ),
226+ )
227+ },
228+ )
229+ }
230+
128231 BoothDetailScreen (
129232 padding = padding,
130233 uiState = uiState,
0 commit comments