Skip to content
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,12 @@

<br>

## ANDROID ARTICLE
🔗 [TERNING TISTORY](https://terning.tistory.com/category/Android)

## DESIGN SYSTEM
🔗 [TERNING DESIGN SYSTEM](https://teamterning.github.io/Terning-Android/index.html)


## KANBAN BOARD
🔗 [TERNING PROJECT](https://github.com/orgs/teamterning/projects/1)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ interface TerningDataStore {
var userId: Long
var alarmAvailable: Boolean
var hasRequestedPermission: Boolean
var serverNoticeTimestamp: Long
fun clearInfo()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class TerningDataStoreImpl @Inject constructor(
get() = dataStore.getBoolean(PERMISSION_REQUESTED, false)
set(value) = dataStore.edit { putBoolean(PERMISSION_REQUESTED, value) }

override var serverNoticeTimestamp: Long
get() = dataStore.getLong(LAST_NOTICE_TIME, 0L)
set(value) = dataStore.edit { putLong(LAST_NOTICE_TIME, value) }

override fun clearInfo() {
dataStore.edit().clear().apply()
}
Expand All @@ -42,5 +46,6 @@ class TerningDataStoreImpl @Inject constructor(
private const val USER_ID = "USER_ID"
private const val ALARM = "ALARM"
private const val PERMISSION_REQUESTED = "PERMISSION_REQUESTED"
private const val LAST_NOTICE_TIME = "LAST_NOTICE_TIME"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,24 @@ class UserRepositoryImpl @Inject constructor(
override fun clearInfo() {
terningDataStore.clearInfo()
}
}

override fun hasNoticeCooldownPassed(): Boolean {
val lastShownTimestamp = terningDataStore.serverNoticeTimestamp

if (lastShownTimestamp == 0L) return true

val currentTime = System.currentTimeMillis()

val elapsedTime = currentTime - lastShownTimestamp

return elapsedTime > THREE_HOURS_MS
}

override fun setNoticeTimestampToNow() {
terningDataStore.serverNoticeTimestamp = System.currentTimeMillis()
}

companion object {
private const val THREE_HOURS_MS = 3 * 60 * 60 * 1000L
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ sealed class UpdateState {
data object NoUpdateAvailable : UpdateState()
data class MajorUpdateAvailable(val title: String, val content: String) : UpdateState()
data class PatchUpdateAvailable(val title: String, val content: String) : UpdateState()
}
data object ServerNoticeAvailable: UpdateState()
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,7 @@ interface UserRepository {
fun getPermissionRequested(): Boolean

fun clearInfo()

fun hasNoticeCooldownPassed(): Boolean
fun setNoticeTimestampToNow()
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.terning.feature.splash

import android.content.Context
import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
Expand All @@ -15,6 +17,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.net.toUri
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
Expand All @@ -29,9 +32,9 @@ import com.terning.core.designsystem.theme.TerningMain
import com.terning.core.designsystem.theme.TerningPointTheme
import com.terning.core.designsystem.theme.White
import com.terning.core.designsystem.type.DeeplinkType
import com.terning.feature.splash.SplashUiState
import com.terning.feature.splash.component.TerningMajorUpdateDialog
import com.terning.feature.splash.component.TerningPatchUpdateDialog
import com.terning.feature.splash.component.TerningServerNoticeDialog
import kotlinx.coroutines.launch

@Composable
Expand Down Expand Up @@ -106,7 +109,11 @@ internal fun SplashRoute(
SplashScreen(
splashUiState = updateState.toUi(),
onUpdateButtonClick = context::launchPlayStore,
onUpdateSkipButtonClick = viewModel::checkIfAccessTokenAvailable
onUpdateSkipButtonClick = viewModel::checkIfAccessTokenAvailable,
onDetailButtonClick = {
navigateToServerWebView(context)
viewModel.checkIfAccessTokenAvailable()
}
)
}

Expand All @@ -115,6 +122,7 @@ private fun SplashScreen(
splashUiState: SplashUiState,
onUpdateButtonClick: () -> Unit,
onUpdateSkipButtonClick: () -> Unit,
onDetailButtonClick: () -> Unit,
) {
when (splashUiState) {
is SplashUiState.MajorUpdateAvailable -> {
Expand All @@ -138,6 +146,13 @@ private fun SplashScreen(
}
}

is SplashUiState.ServerNoticeAvailable -> {
TerningServerNoticeDialog(
onDismissButtonClick = onUpdateSkipButtonClick,
onDetailButtonClick = onDetailButtonClick,
)
}

else -> {}
}

Expand All @@ -156,6 +171,13 @@ private fun SplashScreen(
}
}

private fun navigateToServerWebView(context: Context) {
CustomTabsIntent.Builder().build().launchUrl(context, SERVER_URL.toUri())
}

private const val SERVER_URL =
"https://abundant-quiver-13f.notion.site/2a22867b52c180649a5bfdf1704820a3?pvs=73"

@Preview(showBackground = true)
@Composable
private fun SplashScreenPreview() {
Expand All @@ -164,6 +186,7 @@ private fun SplashScreenPreview() {
splashUiState = SplashUiState.NoUpdateAvailable,
onUpdateButtonClick = {},
onUpdateSkipButtonClick = {},
onDetailButtonClick = {},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ sealed class SplashUiState {
data object NoUpdateAvailable : SplashUiState()
data class MajorUpdateAvailable(val title: String, val content: String) : SplashUiState()
data class PatchUpdateAvailable(val title: String, val content: String) : SplashUiState()
data object ServerNoticeAvailable : SplashUiState()
}

fun UpdateState.toUi(): SplashUiState = when (this) {
UpdateState.InitialState -> SplashUiState.InitialState
UpdateState.NoUpdateAvailable -> SplashUiState.NoUpdateAvailable
is UpdateState.MajorUpdateAvailable -> SplashUiState.MajorUpdateAvailable(title, content)
is UpdateState.PatchUpdateAvailable -> SplashUiState.PatchUpdateAvailable(title, content)
is UpdateState.ServerNoticeAvailable -> SplashUiState.ServerNoticeAvailable
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import javax.inject.Inject
@HiltViewModel
class SplashViewModel @Inject constructor(
private val userRepository: UserRepository,
private val getLatestVersionUseCase: GetUpdateStateUseCase
private val getLatestVersionUseCase: GetUpdateStateUseCase,
) : ViewModel() {

private val _sideEffects = MutableSharedFlow<SplashSideEffect>()
Expand Down Expand Up @@ -47,6 +47,15 @@ class SplashViewModel @Inject constructor(

private fun checkIfUpdateNotAvailable(updateState: UpdateState) {
if (updateState is UpdateState.NoUpdateAvailable) {
checkServerNotice()
}
}

private fun checkServerNotice() = viewModelScope.launch {
if (userRepository.hasNoticeCooldownPassed()) {
_updateState.value = UpdateState.ServerNoticeAvailable
userRepository.setNoticeTimestampToNow()
} else {
checkIfAccessTokenAvailable()
}
}
Expand All @@ -58,4 +67,4 @@ class SplashViewModel @Inject constructor(
companion object {
private const val DELAY_TIME = 500L
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.terning.feature.splash.component

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.terning.core.designsystem.component.dialog.NoticeDialogButton
import com.terning.core.designsystem.theme.Back
import com.terning.core.designsystem.theme.Black
import com.terning.core.designsystem.theme.Grey150
import com.terning.core.designsystem.theme.Grey200
import com.terning.core.designsystem.theme.Grey350
import com.terning.core.designsystem.theme.Grey400
import com.terning.core.designsystem.theme.Grey500
import com.terning.core.designsystem.theme.TerningMain
import com.terning.core.designsystem.theme.TerningMain2
import com.terning.core.designsystem.theme.TerningPointTheme
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.designsystem.theme.White
import com.terning.feature.splash.R

@Composable
internal fun TerningServerNoticeDialog(
onDismissButtonClick: () -> Unit,
onDetailButtonClick: () -> Unit,
) {
Dialog(
onDismissRequest = { },
properties = DialogProperties(
dismissOnBackPress = false,
dismissOnClickOutside = false,
usePlatformDefaultWidth = false,
)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(horizontal = 28.dp)
.background(color = White, shape = RoundedCornerShape(20.dp))
.padding(12.dp)
) {
Text(
text = stringResource(R.string.dialog_server_title),
style = TerningTheme.typography.title2,
color = Grey500,
modifier = Modifier.padding(top = 20.dp)
)
Spacer(modifier = Modifier.height(20.dp))
Text(
text = stringResource(R.string.dialog_bodytitle),
style = TerningTheme.typography.body4.copy(textAlign = TextAlign.Center),
overflow = TextOverflow.Clip,
color = Grey400,
)
Spacer(modifier = Modifier.height(26.dp))
Column(
modifier = Modifier
.clip(RoundedCornerShape(5.dp))
.background(Back)
.padding(
vertical = 18.dp,
horizontal = 58.dp
)
) {
Text(
text = stringResource(R.string.dialog_server_over_title),
style = TerningTheme.typography.body6,
color = Black,
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.dialog_server_over_day),
style = TerningTheme.typography.detail4,
color = Grey500
)
}
Spacer(modifier = Modifier.height(26.dp))
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
NoticeDialogButton(
text = stringResource(R.string.dialog_dismiss),
contentColor = Grey350,
pressedContainerColor = Grey200,
containerColor = Grey150,
onClick = onDismissButtonClick,
modifier = Modifier.weight(1f)
)
NoticeDialogButton(
text = stringResource(R.string.dialog_detail),
contentColor = White,
pressedContainerColor = TerningMain2,
containerColor = TerningMain,
onClick = onDetailButtonClick,
modifier = Modifier.weight(1f)
)
}
}
}
}

@Preview(showBackground = true, widthDp = 360, heightDp = 780)
@Composable
private fun TerningPatchUpdateDialogPreview() {
TerningPointTheme {
TerningServerNoticeDialog(
onDismissButtonClick = {},
onDetailButtonClick = {},
)
}
}
12 changes: 11 additions & 1 deletion feature/splash/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,14 @@
<string name="update_dialog_patch_button_dismiss">다음에 하기</string>
<string name="update_dialog_patch_button_update">업데이트 하기</string>
<string name="update_dialog_major_button_update">업데이트 하러 가기</string>
</resources>

<string name="dialog_server_title">터닝 서비스 종료 안내</string>
<string name="dialog_bodytitle">그동안 터닝을 사랑해주신 모든 분들께\n
진심으로 감사의 말씀을 드립니다.\n
‘터닝’은 11월 25일부로 서비스가 종료될 예정이며,\n
자세한 사항은 공지 내용을 확인해주세요.</string>
Comment on lines +10 to +14
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥹

<string name="dialog_server_over_title">서비스 종료 예정일</string>
<string name="dialog_server_over_day">2025년 11월 25일</string>
<string name="dialog_dismiss">닫기</string>
<string name="dialog_detail">자세히 보기</string>
</resources>
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
compileSdk = "35"
minSdk = "28"
targetSdk = "35"
versionName = "1.3.8"
versionCode = "103080"
versionName = "1.4.0"
versionCode = "104000"
jvmTarget = "1.8"

## Android gradle plugin
Expand Down