Skip to content

Commit 752f3a5

Browse files
committed
[REFACTOR/#479] 알림 받기 화면 compose 리팩토링
1 parent e83448e commit 752f3a5

4 files changed

Lines changed: 286 additions & 55 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.el.yello.presentation.getalarm.contract
2+
3+
import androidx.compose.runtime.Immutable
4+
5+
@Immutable
6+
data class GetAlarmState(
7+
val isLoading: Boolean = false,
8+
val isFromOnBoarding: Boolean = false,
9+
val isCodeTextEmpty: Boolean = false
10+
)
11+
12+
sealed interface GetAlarmSideEffect {
13+
data object NavigateToTutorial : GetAlarmSideEffect
14+
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package com.el.yello.presentation.getalarm.screen
2+
3+
import android.Manifest
4+
import android.content.pm.PackageManager
5+
import android.os.Build
6+
import androidx.activity.compose.rememberLauncherForActivityResult
7+
import androidx.activity.result.contract.ActivityResultContracts
8+
import androidx.compose.foundation.Image
9+
import androidx.compose.foundation.background
10+
import androidx.compose.foundation.layout.Arrangement
11+
import androidx.compose.foundation.layout.Column
12+
import androidx.compose.foundation.layout.PaddingValues
13+
import androidx.compose.foundation.layout.Row
14+
import androidx.compose.foundation.layout.Spacer
15+
import androidx.compose.foundation.layout.fillMaxSize
16+
import androidx.compose.foundation.layout.fillMaxWidth
17+
import androidx.compose.foundation.layout.height
18+
import androidx.compose.foundation.layout.padding
19+
import androidx.compose.foundation.layout.size
20+
import androidx.compose.foundation.shape.RoundedCornerShape
21+
import androidx.compose.material3.Button
22+
import androidx.compose.material3.ButtonDefaults
23+
import androidx.compose.material3.Scaffold
24+
import androidx.compose.material3.Text
25+
import androidx.compose.runtime.Composable
26+
import androidx.compose.runtime.LaunchedEffect
27+
import androidx.compose.ui.Alignment
28+
import androidx.compose.ui.Modifier
29+
import androidx.compose.ui.layout.ContentScale
30+
import androidx.compose.ui.platform.LocalContext
31+
import androidx.compose.ui.res.painterResource
32+
import androidx.compose.ui.res.stringResource
33+
import androidx.compose.ui.text.font.FontWeight
34+
import androidx.compose.ui.text.style.TextAlign
35+
import androidx.compose.ui.tooling.preview.Preview
36+
import androidx.compose.ui.unit.dp
37+
import androidx.compose.ui.unit.sp
38+
import androidx.core.content.ContextCompat
39+
import androidx.hilt.navigation.compose.hiltViewModel
40+
import com.el.yello.R
41+
import com.el.yello.presentation.getalarm.contract.GetAlarmSideEffect
42+
import com.el.yello.presentation.getalarm.viewmodel.GetAlarmViewModel
43+
import com.example.ui.compose.theme.Black
44+
import com.example.ui.compose.theme.Grayscale500
45+
import com.example.ui.compose.theme.PretendardFontFamily
46+
import com.example.ui.compose.theme.White
47+
import com.example.ui.compose.theme.YelloMain500
48+
import com.example.ui.compose.theme.YelloTheme
49+
import org.orbitmvi.orbit.compose.collectSideEffect
50+
51+
@Composable
52+
fun GetAlarmRoute(
53+
viewModel: GetAlarmViewModel = hiltViewModel(),
54+
isFromOnBoarding: Boolean,
55+
isCodeTextEmpty: Boolean,
56+
navigateToTutorial: () -> Unit
57+
) {
58+
val context = LocalContext.current
59+
60+
val requestPermissionLauncher = rememberLauncherForActivityResult(
61+
ActivityResultContracts.RequestPermission()
62+
) { isGranted ->
63+
viewModel.onPermissionResult(isGranted)
64+
}
65+
66+
LaunchedEffect(Unit) {
67+
viewModel.initGetAlarm(isFromOnBoarding, isCodeTextEmpty)
68+
}
69+
70+
viewModel.collectSideEffect {
71+
when (it) {
72+
GetAlarmSideEffect.NavigateToTutorial -> navigateToTutorial()
73+
}
74+
}
75+
76+
GetAlarmScreen(
77+
onStartYelloClick = {
78+
viewModel.onStartYelloClickEvent()
79+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
80+
if (ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
81+
) {
82+
viewModel.onPermissionResult(true)
83+
} else {
84+
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
85+
}
86+
} else {
87+
viewModel.onPermissionResult(true)
88+
}
89+
}
90+
)
91+
}
92+
93+
@Composable
94+
fun GetAlarmScreen(
95+
onStartYelloClick: () -> Unit = {}
96+
) {
97+
Scaffold(
98+
modifier = Modifier
99+
.fillMaxSize()
100+
.background(Black)
101+
) { paddingValues ->
102+
Column(
103+
modifier = Modifier
104+
.fillMaxSize()
105+
.background(Black)
106+
.padding(paddingValues)
107+
.padding(horizontal = 16.dp),
108+
horizontalAlignment = Alignment.CenterHorizontally
109+
) {
110+
Spacer(
111+
modifier = Modifier.height(104.dp)
112+
)
113+
114+
// 제목, 하트 아이콘
115+
Row(
116+
horizontalArrangement = Arrangement.Center,
117+
verticalAlignment = Alignment.CenterVertically
118+
) {
119+
Text(
120+
text = stringResource(id = R.string.onboarding_tv_startapp_title),
121+
fontSize = 24.sp,
122+
fontFamily = PretendardFontFamily,
123+
fontWeight = FontWeight.Bold,
124+
color = White,
125+
textAlign = TextAlign.Center
126+
)
127+
Image(
128+
modifier = Modifier
129+
.padding(start = 2.dp)
130+
.size(24.dp),
131+
painter = painterResource(id = R.drawable.ic_onboarding_startapp_heart),
132+
contentDescription = "Heart Icon"
133+
)
134+
}
135+
136+
Spacer(modifier = Modifier.height(8.dp))
137+
138+
// 부제목
139+
Text(
140+
text = stringResource(id = R.string.onboarding_tv_startapp_subtitle),
141+
fontSize = 14.sp,
142+
fontFamily = PretendardFontFamily,
143+
fontWeight = FontWeight.Medium,
144+
color = Grayscale500,
145+
textAlign = TextAlign.Center
146+
)
147+
148+
Spacer(modifier = Modifier.height(24.dp))
149+
150+
// 알림 아이콘 이미지
151+
Image(
152+
modifier = Modifier
153+
.fillMaxWidth()
154+
.height(340.dp),
155+
painter = painterResource(id = R.drawable.img_tutorial_startapp),
156+
contentDescription = "Start App Image",
157+
contentScale = ContentScale.Crop
158+
)
159+
160+
Spacer(modifier = Modifier.weight(1f))
161+
162+
// 쪽지 알림 받기 버튼
163+
Button(
164+
modifier = Modifier
165+
.fillMaxWidth()
166+
.padding(bottom = 34.dp),
167+
onClick = onStartYelloClick,
168+
colors = ButtonDefaults.buttonColors(
169+
containerColor = YelloMain500,
170+
contentColor = Black
171+
),
172+
shape = RoundedCornerShape(100.dp),
173+
contentPadding = PaddingValues(
174+
horizontal = 16.dp,
175+
vertical = 18.dp
176+
)
177+
) {
178+
Text(
179+
text = stringResource(id = R.string.onboarding_tv_start_yello),
180+
fontSize = 15.sp,
181+
fontFamily = PretendardFontFamily,
182+
fontWeight = FontWeight.Bold
183+
)
184+
}
185+
}
186+
}
187+
}
188+
189+
@Preview(showBackground = true)
190+
@Composable
191+
fun GetAlarmScreenPreview() {
192+
YelloTheme {
193+
GetAlarmScreen()
194+
}
195+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.el.yello.presentation.getalarm.viewmodel
2+
3+
import androidx.lifecycle.ViewModel
4+
import com.el.yello.presentation.getalarm.contract.GetAlarmSideEffect
5+
import com.el.yello.presentation.getalarm.contract.GetAlarmState
6+
import com.el.yello.util.manager.AmplitudeManager
7+
import dagger.hilt.android.lifecycle.HiltViewModel
8+
import org.orbitmvi.orbit.Container
9+
import org.orbitmvi.orbit.ContainerHost
10+
import org.orbitmvi.orbit.syntax.simple.intent
11+
import org.orbitmvi.orbit.syntax.simple.postSideEffect
12+
import org.orbitmvi.orbit.syntax.simple.reduce
13+
import org.orbitmvi.orbit.viewmodel.container
14+
import javax.inject.Inject
15+
16+
@HiltViewModel
17+
class GetAlarmViewModel @Inject constructor()
18+
: ContainerHost<GetAlarmState, GetAlarmSideEffect>, ViewModel() {
19+
override val container: Container<GetAlarmState, GetAlarmSideEffect> = container(GetAlarmState())
20+
21+
fun initGetAlarm(isFromOnBoarding: Boolean, isCodeTextEmpty: Boolean) = intent {
22+
reduce {
23+
state.copy(
24+
isFromOnBoarding = isFromOnBoarding,
25+
isCodeTextEmpty = isCodeTextEmpty
26+
)
27+
}
28+
}
29+
30+
fun onPermissionResult(isGranted: Boolean) = intent {
31+
if (isGranted) {
32+
AmplitudeManager.updateUserProperties(EVENT_PUSH_NOTIFICATION, VALUE_ENABLED)
33+
} else {
34+
AmplitudeManager.updateUserProperties(EVENT_PUSH_NOTIFICATION, VALUE_DISABLED)
35+
}
36+
postSideEffect(GetAlarmSideEffect.NavigateToTutorial)
37+
}
38+
39+
fun onStartYelloClickEvent() = intent {
40+
AmplitudeManager.trackEventWithProperties(EVENT_CLICK_ONBOARDING_NOTIFICATION)
41+
}
42+
43+
companion object {
44+
private const val EVENT_PUSH_NOTIFICATION = "user_pushnotification"
45+
private const val VALUE_ENABLED = "enabled"
46+
private const val VALUE_DISABLED = "disabled"
47+
private const val EVENT_CLICK_ONBOARDING_NOTIFICATION = "click_onboarding_notification"
48+
}
49+
}
Lines changed: 28 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,48 @@
11
package com.el.yello.presentation.onboarding.activity
22

3-
import android.Manifest
43
import android.content.Context
54
import android.content.Intent
6-
import android.content.pm.PackageManager
7-
import android.os.Build
85
import android.os.Bundle
9-
import androidx.activity.result.contract.ActivityResultContracts
10-
import androidx.core.content.ContextCompat
11-
import com.el.yello.R
12-
import com.el.yello.databinding.ActivityGetAlarmBinding
6+
import androidx.activity.ComponentActivity
7+
import androidx.activity.compose.setContent
8+
import androidx.compose.foundation.layout.fillMaxSize
9+
import androidx.compose.material3.MaterialTheme
10+
import androidx.compose.material3.Surface
11+
import androidx.compose.ui.Modifier
12+
import com.el.yello.presentation.getalarm.screen.GetAlarmRoute
1313
import com.el.yello.presentation.tutorial.TutorialActivity
14-
import com.el.yello.util.manager.AmplitudeManager
15-
import com.example.ui.base.BindingActivity
14+
import com.example.ui.compose.theme.YelloTheme
1615
import com.example.ui.extension.boolExtra
17-
import com.example.ui.extension.setOnSingleClickListener
16+
import dagger.hilt.android.AndroidEntryPoint
1817

19-
class GetAlarmActivity :
20-
BindingActivity<ActivityGetAlarmBinding>(R.layout.activity_get_alarm) {
18+
@AndroidEntryPoint
19+
class GetAlarmActivity : ComponentActivity() {
2120

2221
private val isFromOnBoarding by boolExtra()
22+
2323
override fun onCreate(savedInstanceState: Bundle?) {
2424
super.onCreate(savedInstanceState)
25-
askNotificationPermission()
26-
}
25+
val isCodeTextEmpty = intent.getBooleanExtra(OnBoardingActivity.EXTRA_CODE_TEXT_EMPTY, false)
2726

28-
private val requestPermissionLauncher =
29-
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
30-
if (isGranted) {
31-
AmplitudeManager.updateUserProperties(EVENT_PUSH_NOTIFICATION, VALUE_ENABLED)
32-
startTutorialActivity()
33-
} else {
34-
AmplitudeManager.updateUserProperties(EVENT_PUSH_NOTIFICATION, VALUE_DISABLED)
35-
startTutorialActivity()
27+
setContent {
28+
YelloTheme {
29+
Surface(
30+
modifier = Modifier.fillMaxSize(),
31+
color = MaterialTheme.colorScheme.background
32+
) {
33+
GetAlarmRoute(
34+
isFromOnBoarding = isFromOnBoarding,
35+
isCodeTextEmpty = isCodeTextEmpty,
36+
navigateToTutorial = ::startTutorialActivity
37+
)
38+
}
3639
}
3740
}
41+
}
3842

3943
private fun startTutorialActivity() {
40-
val isCodeTextEmpty =
41-
intent.getBooleanExtra(OnBoardingActivity.EXTRA_CODE_TEXT_EMPTY, false)
44+
val isCodeTextEmpty = intent.getBooleanExtra(OnBoardingActivity.EXTRA_CODE_TEXT_EMPTY, false)
45+
4246
val intent = TutorialActivity.newIntent(this, false).apply {
4347
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
4448
putExtra(OnBoardingActivity.EXTRA_CODE_TEXT_EMPTY, isCodeTextEmpty)
@@ -48,42 +52,11 @@ class GetAlarmActivity :
4852
finish()
4953
}
5054

51-
private fun askNotificationPermission() {
52-
binding.btnStartYello.setOnSingleClickListener {
53-
AmplitudeManager.trackEventWithProperties(EVENT_CLICK_ONBOARDING_NOTIFICATION)
54-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
55-
if (ContextCompat.checkSelfPermission(
56-
this,
57-
Manifest.permission.POST_NOTIFICATIONS,
58-
) == PackageManager.PERMISSION_GRANTED
59-
) {
60-
startTutorialActivity()
61-
} else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
62-
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
63-
} else {
64-
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
65-
}
66-
} else {
67-
startTutorialActivity()
68-
}
69-
}
70-
}
71-
72-
override fun onPause() {
73-
super.onPause()
74-
overridePendingTransition(NONE_ANIMATION, NONE_ANIMATION)
75-
}
76-
7755
companion object {
7856
@JvmStatic
7957
fun newIntent(context: Context, isFromOnBoarding: Boolean) =
8058
Intent(context, GetAlarmActivity::class.java).apply {
8159
putExtra("isFromOnBoarding", isFromOnBoarding)
8260
}
83-
private const val NONE_ANIMATION = 0
84-
private const val EVENT_PUSH_NOTIFICATION = "user_pushnotification"
85-
private const val VALUE_ENABLED = "enabled"
86-
private const val VALUE_DISABLED = "disabled"
87-
private const val EVENT_CLICK_ONBOARDING_NOTIFICATION = "click_onboarding_notification"
8861
}
8962
}

0 commit comments

Comments
 (0)