Skip to content

Commit 04bb03b

Browse files
authored
Merge pull request #322 from Project-Unifest/feat/check-permission-enhancement
[refactor] 권한 허용 관련 로직 개선 및 스탬프 API 변경사항 반영
2 parents c3b5a47 + ed95d6f commit 04bb03b

File tree

22 files changed

+482
-165
lines changed

22 files changed

+482
-165
lines changed

core/common/src/main/kotlin/com/unifest/android/core/common/extension/Activity.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import android.Manifest
44
import android.app.Activity
55
import android.content.Intent
66
import android.content.pm.PackageManager
7-
import android.net.Uri
87
import android.os.Build
9-
import android.provider.Settings
108

119
inline fun <reified T : Activity> Activity.startActivityWithAnimation(
1210
withFinish: Boolean,
@@ -26,14 +24,15 @@ inline fun <reified T : Activity> Activity.startActivityWithAnimation(
2624
if (withFinish) finish()
2725
}
2826

29-
fun Activity.navigateToAppSetting() {
30-
Intent(
31-
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
32-
Uri.fromParts("package", packageName, null),
33-
).also(::startActivity)
34-
}
35-
3627
fun Activity.checkLocationPermission(): Boolean {
3728
return checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
3829
checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
3930
}
31+
32+
fun Activity.checkNotificationPermission(): Boolean {
33+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
34+
checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
35+
} else {
36+
true
37+
}
38+
}

core/data/impl/src/main/kotlin/com/unifest/android/core/data/mapper/StampMapper.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ internal fun StampRecord.toModel(): StampRecordModel {
3737
internal fun StampFestival.toModel(): StampFestivalModel {
3838
return StampFestivalModel(
3939
festivalId = festivalId,
40-
name = name,
40+
schoolName = schoolName,
4141
defaultImgUrl = defaultImgUrl,
4242
usedImgUrl = usedImgUrl,
4343
)

core/model/src/main/kotlin/com/unifest/android/core/model/StampFestivalModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.unifest.android.core.model
22

33
data class StampFestivalModel(
44
val festivalId: Long,
5-
val name: String,
5+
val schoolName: String,
66
val defaultImgUrl: String,
77
val usedImgUrl: String,
88
)

core/network/src/main/kotlin/com/unifest/android/core/network/response/stamp/StampFestivalsResponse.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ data class StampFestivalsResponse(
1717
data class StampFestival(
1818
@SerialName("festivalId")
1919
val festivalId: Long,
20-
@SerialName("name")
21-
val name: String,
20+
@SerialName("schoolName")
21+
val schoolName: String,
2222
@SerialName("defaultImgUrl")
2323
val defaultImgUrl: String,
2424
@SerialName("usedImgUrl")

feature/booth/src/main/kotlin/com/unifest/android/feature/booth/BoothDetailScreen.kt

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
package 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
38
import android.widget.Toast
9+
import androidx.activity.compose.rememberLauncherForActivityResult
10+
import androidx.activity.result.contract.ActivityResultContracts
411
import androidx.compose.foundation.background
512
import androidx.compose.foundation.isSystemInDarkTheme
613
import androidx.compose.foundation.layout.Box
@@ -20,9 +27,13 @@ import androidx.compose.material3.SnackbarHostState
2027
import androidx.compose.material3.Text
2128
import androidx.compose.runtime.Composable
2229
import androidx.compose.runtime.DisposableEffect
30+
import androidx.compose.runtime.LaunchedEffect
2331
import androidx.compose.runtime.getValue
32+
import androidx.compose.runtime.mutableStateOf
2433
import androidx.compose.runtime.remember
2534
import androidx.compose.runtime.rememberCoroutineScope
35+
import androidx.compose.runtime.setValue
36+
import androidx.compose.runtime.snapshotFlow
2637
import androidx.compose.ui.Alignment
2738
import androidx.compose.ui.Modifier
2839
import androidx.compose.ui.graphics.Color
@@ -35,9 +46,9 @@ import androidx.compose.ui.unit.dp
3546
import androidx.hilt.navigation.compose.hiltViewModel
3647
import androidx.lifecycle.compose.collectAsStateWithLifecycle
3748
import com.unifest.android.core.common.ObserveAsEvents
49+
import com.unifest.android.core.common.PermissionDialogButtonType
50+
import com.unifest.android.core.common.extension.checkNotificationPermission
3851
import 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
4152
import com.unifest.android.core.designsystem.component.LoadingWheel
4253
import com.unifest.android.core.designsystem.component.NetworkErrorDialog
4354
import com.unifest.android.core.designsystem.component.NetworkImage
@@ -51,6 +62,8 @@ import com.unifest.android.core.designsystem.theme.Title2
5162
import com.unifest.android.core.designsystem.theme.UnifestTheme
5263
import com.unifest.android.core.ui.DevicePreview
5364
import 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
5467
import com.unifest.android.core.ui.component.WaitingConfirmDialog
5568
import com.unifest.android.core.ui.component.WaitingDialog
5669
import com.unifest.android.core.ui.component.WaitingPinDialog
@@ -65,8 +78,10 @@ import com.unifest.android.feature.booth.viewmodel.BoothViewModel
6578
import com.unifest.android.feature.booth.viewmodel.ErrorType
6679
import kotlinx.collections.immutable.toImmutableList
6780
import kotlinx.coroutines.delay
81+
import kotlinx.coroutines.flow.distinctUntilChanged
6882
import kotlinx.coroutines.launch
6983
import tech.thdev.compose.exteions.system.ui.controller.rememberExSystemUiController
84+
import com.unifest.android.core.designsystem.R as designR
7085

7186
private 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

Comments
 (0)