diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ce08a10e..552b58c7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -28,6 +28,9 @@
android:theme="@style/Theme.PeekabookAOS"
android:usesCleartextTraffic="true"
tools:targetApi="31">
+
=
+ kotlin.runCatching { forceUpdateDataSource.getVersion() }.map { response ->
+ requireNotNull(response.data).toVersion()
+ }
+}
diff --git a/app/src/main/java/com/sopt/peekabookaos/data/service/ForceUpdateService.kt b/app/src/main/java/com/sopt/peekabookaos/data/service/ForceUpdateService.kt
new file mode 100644
index 00000000..13b32c72
--- /dev/null
+++ b/app/src/main/java/com/sopt/peekabookaos/data/service/ForceUpdateService.kt
@@ -0,0 +1,10 @@
+package com.sopt.peekabookaos.data.service
+
+import com.sopt.peekabookaos.data.entity.BaseResponse
+import com.sopt.peekabookaos.data.entity.response.VersionResponse
+import retrofit2.http.GET
+
+interface ForceUpdateService {
+ @GET("user/v1/version")
+ suspend fun getVersion(): BaseResponse
+}
diff --git a/app/src/main/java/com/sopt/peekabookaos/data/source/remote/ForceUpdateDataSource.kt b/app/src/main/java/com/sopt/peekabookaos/data/source/remote/ForceUpdateDataSource.kt
new file mode 100644
index 00000000..c21ee921
--- /dev/null
+++ b/app/src/main/java/com/sopt/peekabookaos/data/source/remote/ForceUpdateDataSource.kt
@@ -0,0 +1,13 @@
+package com.sopt.peekabookaos.data.source.remote
+
+import com.sopt.peekabookaos.data.entity.BaseResponse
+import com.sopt.peekabookaos.data.entity.response.VersionResponse
+import com.sopt.peekabookaos.data.service.ForceUpdateService
+import javax.inject.Inject
+
+class ForceUpdateDataSource @Inject constructor(
+ private val forceUpdateService: ForceUpdateService
+) {
+ suspend fun getVersion(): BaseResponse =
+ forceUpdateService.getVersion()
+}
diff --git a/app/src/main/java/com/sopt/peekabookaos/di/RepositoryModule.kt b/app/src/main/java/com/sopt/peekabookaos/di/RepositoryModule.kt
index 78a3a0bc..0b8c200f 100644
--- a/app/src/main/java/com/sopt/peekabookaos/di/RepositoryModule.kt
+++ b/app/src/main/java/com/sopt/peekabookaos/di/RepositoryModule.kt
@@ -4,6 +4,7 @@ import com.sopt.peekabookaos.data.repository.AuthRepositoryImpl
import com.sopt.peekabookaos.data.repository.BlockRepositoryImpl
import com.sopt.peekabookaos.data.repository.BookRepositoryImpl
import com.sopt.peekabookaos.data.repository.DetailRepositoryImpl
+import com.sopt.peekabookaos.data.repository.ForceUpdateRepositoryImpl
import com.sopt.peekabookaos.data.repository.MyPageRepositoryImpl
import com.sopt.peekabookaos.data.repository.NaverRepositoryImpl
import com.sopt.peekabookaos.data.repository.NotificationRepositoryImpl
@@ -17,6 +18,7 @@ import com.sopt.peekabookaos.domain.repository.AuthRepository
import com.sopt.peekabookaos.domain.repository.BlockRepository
import com.sopt.peekabookaos.domain.repository.BookRepository
import com.sopt.peekabookaos.domain.repository.DetailRepository
+import com.sopt.peekabookaos.domain.repository.ForceUpdateRepository
import com.sopt.peekabookaos.domain.repository.MyPageRepository
import com.sopt.peekabookaos.domain.repository.NaverRepository
import com.sopt.peekabookaos.domain.repository.NotificationRepository
@@ -112,4 +114,10 @@ abstract class RepositoryModule {
abstract fun bindToMyPageRepository(
myPageRepositoryImpl: MyPageRepositoryImpl
): MyPageRepository
+
+ @Binds
+ @Singleton
+ abstract fun bindToForceUpdateRepository(
+ forceUpdateRepositoryImpl: ForceUpdateRepositoryImpl
+ ): ForceUpdateRepository
}
diff --git a/app/src/main/java/com/sopt/peekabookaos/di/RetrofitServiceModule.kt b/app/src/main/java/com/sopt/peekabookaos/di/RetrofitServiceModule.kt
index e3a5342a..22f4b3fc 100644
--- a/app/src/main/java/com/sopt/peekabookaos/di/RetrofitServiceModule.kt
+++ b/app/src/main/java/com/sopt/peekabookaos/di/RetrofitServiceModule.kt
@@ -4,6 +4,7 @@ import com.sopt.peekabookaos.data.service.AuthService
import com.sopt.peekabookaos.data.service.BlockService
import com.sopt.peekabookaos.data.service.BookService
import com.sopt.peekabookaos.data.service.DetailService
+import com.sopt.peekabookaos.data.service.ForceUpdateService
import com.sopt.peekabookaos.data.service.MyPageService
import com.sopt.peekabookaos.data.service.NaverService
import com.sopt.peekabookaos.data.service.NotificationService
@@ -21,6 +22,7 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
+import retrofit2.create
@Module
@InstallIn(SingletonComponent::class)
@@ -76,4 +78,8 @@ object RetrofitServiceModule {
@Provides
fun providesReportService(@PeekaType retrofit: Retrofit): ReportService =
retrofit.create(ReportService::class.java)
+
+ @Provides
+ fun providesForceUpdateService(@PeekaType retrofit: Retrofit): ForceUpdateService =
+ retrofit.create(ForceUpdateService::class.java)
}
diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/entity/Version.kt b/app/src/main/java/com/sopt/peekabookaos/domain/entity/Version.kt
new file mode 100644
index 00000000..c998453d
--- /dev/null
+++ b/app/src/main/java/com/sopt/peekabookaos/domain/entity/Version.kt
@@ -0,0 +1,11 @@
+package com.sopt.peekabookaos.domain.entity
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class Version(
+ val imageUrl: String = "",
+ val androidForceVersion: String = "",
+ val versionText: String = ""
+) : Parcelable
diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionDetail.kt b/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionDetail.kt
new file mode 100644
index 00000000..b904357a
--- /dev/null
+++ b/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionDetail.kt
@@ -0,0 +1,6 @@
+package com.sopt.peekabookaos.domain.entity
+
+data class VersionDetail(
+ val major: String,
+ val minor: String
+)
diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionState.kt b/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionState.kt
new file mode 100644
index 00000000..8bb1ca7b
--- /dev/null
+++ b/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionState.kt
@@ -0,0 +1,5 @@
+package com.sopt.peekabookaos.domain.entity
+
+enum class VersionState {
+ LATEST, OUTDATED
+}
diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/repository/ForceUpdateRepository.kt b/app/src/main/java/com/sopt/peekabookaos/domain/repository/ForceUpdateRepository.kt
new file mode 100644
index 00000000..6050968a
--- /dev/null
+++ b/app/src/main/java/com/sopt/peekabookaos/domain/repository/ForceUpdateRepository.kt
@@ -0,0 +1,7 @@
+package com.sopt.peekabookaos.domain.repository
+
+import com.sopt.peekabookaos.domain.entity.Version
+
+interface ForceUpdateRepository {
+ suspend fun getVersion(): Result
+}
diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetVersionUseCase.kt b/app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetVersionUseCase.kt
new file mode 100644
index 00000000..e4884600
--- /dev/null
+++ b/app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetVersionUseCase.kt
@@ -0,0 +1,10 @@
+package com.sopt.peekabookaos.domain.usecase
+
+import com.sopt.peekabookaos.domain.repository.ForceUpdateRepository
+import javax.inject.Inject
+
+class GetVersionUseCase @Inject constructor(
+ private val forceUpdateRepository: ForceUpdateRepository
+) {
+ suspend operator fun invoke() = forceUpdateRepository.getVersion()
+}
diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateActivity.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateActivity.kt
new file mode 100644
index 00000000..c9e3eae1
--- /dev/null
+++ b/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateActivity.kt
@@ -0,0 +1,40 @@
+package com.sopt.peekabookaos.presentation.forceUpdate
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import androidx.activity.viewModels
+import com.sopt.peekabookaos.R
+import com.sopt.peekabookaos.databinding.ActivityForceUpdateBinding
+import com.sopt.peekabookaos.domain.entity.Version
+import com.sopt.peekabookaos.presentation.splash.SplashActivity.Companion.LATEST_VERSION
+import com.sopt.peekabookaos.util.binding.BindingActivity
+import com.sopt.peekabookaos.util.extensions.getParcelable
+
+class ForceUpdateActivity :
+ BindingActivity(R.layout.activity_force_update) {
+ private lateinit var intentToPlayStore: Intent
+ private val viewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding.viewModel = viewModel
+ getLatestVersion()
+ initUpdateBtnClickListener()
+ }
+
+ private fun initUpdateBtnClickListener() {
+ binding.btnForceUpdate.setOnClickListener {
+ intentToPlayStore = Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse(getString(R.string.force_update_store_link))
+ )
+ startActivity(intentToPlayStore)
+ }
+ }
+
+ private fun getLatestVersion() {
+ intent.getParcelable(LATEST_VERSION, Version::class.java)
+ ?.let { viewModel.getLatestVersion(it) }
+ }
+}
diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateViewModel.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateViewModel.kt
new file mode 100644
index 00000000..992bb827
--- /dev/null
+++ b/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateViewModel.kt
@@ -0,0 +1,15 @@
+package com.sopt.peekabookaos.presentation.forceUpdate
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.sopt.peekabookaos.domain.entity.Version
+
+class ForceUpdateViewModel : ViewModel() {
+ private val _latestVersion: MutableLiveData = MutableLiveData()
+ val latestVersion: LiveData = _latestVersion
+
+ fun getLatestVersion(version: Version) {
+ _latestVersion.value = version
+ }
+}
diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashActivity.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashActivity.kt
index d829d028..e28ccd88 100644
--- a/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashActivity.kt
+++ b/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashActivity.kt
@@ -2,6 +2,8 @@ package com.sopt.peekabookaos.presentation.splash
import android.annotation.SuppressLint
import android.content.Intent
+import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -9,6 +11,8 @@ import androidx.activity.viewModels
import com.sopt.peekabookaos.R
import com.sopt.peekabookaos.databinding.ActivitySplashBinding
import com.sopt.peekabookaos.domain.entity.SplashState
+import com.sopt.peekabookaos.domain.entity.VersionState
+import com.sopt.peekabookaos.presentation.forceUpdate.ForceUpdateActivity
import com.sopt.peekabookaos.presentation.main.MainActivity
import com.sopt.peekabookaos.presentation.onboarding.OnboardingActivity
import com.sopt.peekabookaos.util.binding.BindingActivity
@@ -22,7 +26,28 @@ class SplashActivity : BindingActivity(R.layout.activity_
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding.lottieSplash.playAnimation()
- Handler(Looper.getMainLooper()).postDelayed({ checkSplashState() }, DURATION)
+ Handler(Looper.getMainLooper()).postDelayed({ initIsForceUpdateObserver() }, DURATION)
+ }
+
+ private fun initIsForceUpdateObserver() {
+ splashViewModel.latestVersion.observe(this) {
+ splashViewModel.checkUpdateVersion()
+ checkVersionUpdate()
+ }
+ }
+
+ private fun checkVersionUpdate() {
+ when (splashViewModel.checkUpdateVersion()) {
+ VersionState.LATEST -> checkSplashState()
+ VersionState.OUTDATED -> {
+ val intentToForceUpdate = Intent(this, ForceUpdateActivity::class.java).apply {
+ putExtra(LATEST_VERSION, splashViewModel.latestVersion.value)
+ addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK)
+ }
+ startActivity(Intent(intentToForceUpdate))
+ finish()
+ }
+ }
}
private fun checkSplashState() {
@@ -36,5 +61,6 @@ class SplashActivity : BindingActivity(R.layout.activity_
companion object {
private const val DURATION: Long = 2000
+ const val LATEST_VERSION = "latest version"
}
}
diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashViewModel.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashViewModel.kt
index faf1b808..268ae523 100644
--- a/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashViewModel.kt
+++ b/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashViewModel.kt
@@ -1,14 +1,65 @@
package com.sopt.peekabookaos.presentation.splash
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.sopt.peekabookaos.BuildConfig
import com.sopt.peekabookaos.domain.entity.SplashState
+import com.sopt.peekabookaos.domain.entity.Version
+import com.sopt.peekabookaos.domain.entity.VersionDetail
+import com.sopt.peekabookaos.domain.entity.VersionState
import com.sopt.peekabookaos.domain.usecase.GetSplashStateUseCase
+import com.sopt.peekabookaos.domain.usecase.GetVersionUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.launch
+import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class SplashViewModel @Inject constructor(
- private val getSplashStateUseCase: GetSplashStateUseCase
+ private val getSplashStateUseCase: GetSplashStateUseCase,
+ private val getVersionUseCase: GetVersionUseCase
) : ViewModel() {
+ private val _latestVersion: MutableLiveData = MutableLiveData()
+ val latestVersion: LiveData = _latestVersion
+ private lateinit var latestVersionDetail: VersionDetail
+ private lateinit var appVersionDetail: VersionDetail
+
+ init {
+ getVersion()
+ }
+
fun getSplashState(): SplashState = getSplashStateUseCase()
+
+ fun checkUpdateVersion(): VersionState {
+ latestVersionDetail =
+ spiltVersionToMajorMinor(requireNotNull(latestVersion.value?.androidForceVersion) { "version is null" })
+ appVersionDetail = spiltVersionToMajorMinor(BuildConfig.VERSION_NAME)
+ return if (appVersionDetail.major != latestVersionDetail.major || appVersionDetail.minor != latestVersionDetail.minor) VersionState.OUTDATED
+ else VersionState.LATEST
+ }
+
+ private fun spiltVersionToMajorMinor(versionName: String): VersionDetail {
+ val versionSpiltList = versionName.split(".")
+ val major = versionSpiltList[0]
+ val minor = versionSpiltList[1]
+ return VersionDetail(major, minor)
+ }
+
+ private fun getVersion() {
+ viewModelScope.launch {
+ getVersionUseCase()
+ .onSuccess { response ->
+ _latestVersion.value = Version(
+ response.imageUrl,
+ response.androidForceVersion,
+ response.versionText
+ )
+ }
+ .onFailure { throwable ->
+ Timber.e("$throwable")
+ }
+ }
+ }
}
diff --git a/app/src/main/res/layout/activity_force_update.xml b/app/src/main/res/layout/activity_force_update.xml
new file mode 100644
index 00000000..666f325e
--- /dev/null
+++ b/app/src/main/res/layout/activity_force_update.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4eaa93b8..ed824201 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -206,4 +206,9 @@
한 줄 소개는 40자까지 쓸 수 있어요!
차단된 사용자가 없어요.
yyyyMMdd_HHmmss
+
+
+ 앗! 피카북이 달라졌어요
+ 업데이트 하러 가기
+ https://play.google.com/store/apps/details?id=com.sopt.peekabookaos&hl=ko-KR
\ No newline at end of file