Skip to content

Commit

Permalink
Merge pull request #274 from team-peekabook/feature/#273-feat-bookshe…
Browse files Browse the repository at this point in the history
…lf-version-update-notification

#273 [feat] 앱 버전이 맞지 않을 경우, 업데이트 화면 뜨게 하기
  • Loading branch information
stellar-halo authored Nov 6, 2023
2 parents 2ae61af + 35bb9fd commit 830ad95
Show file tree
Hide file tree
Showing 18 changed files with 330 additions and 2 deletions.
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
android:theme="@style/Theme.PeekabookAOS"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity android:name=".presentation.forceUpdate.ForceUpdateActivity"
android:exported="false"
android:screenOrientation="portrait"/>
<activity
android:name=".presentation.profileModify.ProfileModifyActivity"
android:exported="false"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.sopt.peekabookaos.data.entity.response

import com.sopt.peekabookaos.domain.entity.Version
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class VersionResponse(
val imageUrl: String,
val iosForceVersion: String,
val androidForceVersion: String,
@SerialName("text")
val versionText: String
) {
fun toVersion(): Version = Version(
imageUrl = this.imageUrl,
androidForceVersion = this.androidForceVersion,
versionText = this.versionText
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.sopt.peekabookaos.data.repository

import com.sopt.peekabookaos.data.source.remote.ForceUpdateDataSource
import com.sopt.peekabookaos.domain.entity.Version
import com.sopt.peekabookaos.domain.repository.ForceUpdateRepository
import javax.inject.Inject

class ForceUpdateRepositoryImpl @Inject constructor(
private val forceUpdateDataSource: ForceUpdateDataSource
) : ForceUpdateRepository {
override suspend fun getVersion(): Result<Version> =
kotlin.runCatching { forceUpdateDataSource.getVersion() }.map { response ->
requireNotNull(response.data).toVersion()
}
}
Original file line number Diff line number Diff line change
@@ -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<VersionResponse>
}
Original file line number Diff line number Diff line change
@@ -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<VersionResponse> =
forceUpdateService.getVersion()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -112,4 +114,10 @@ abstract class RepositoryModule {
abstract fun bindToMyPageRepository(
myPageRepositoryImpl: MyPageRepositoryImpl
): MyPageRepository

@Binds
@Singleton
abstract fun bindToForceUpdateRepository(
forceUpdateRepositoryImpl: ForceUpdateRepositoryImpl
): ForceUpdateRepository
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
11 changes: 11 additions & 0 deletions app/src/main/java/com/sopt/peekabookaos/domain/entity/Version.kt
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sopt.peekabookaos.domain.entity

data class VersionDetail(
val major: String,
val minor: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.sopt.peekabookaos.domain.entity

enum class VersionState {
LATEST, OUTDATED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.sopt.peekabookaos.domain.repository

import com.sopt.peekabookaos.domain.entity.Version

interface ForceUpdateRepository {
suspend fun getVersion(): Result<Version>
}
Original file line number Diff line number Diff line change
@@ -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()
}
Original file line number Diff line number Diff line change
@@ -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<ActivityForceUpdateBinding>(R.layout.activity_force_update) {
private lateinit var intentToPlayStore: Intent
private val viewModel by viewModels<ForceUpdateViewModel>()

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) }
}
}
Original file line number Diff line number Diff line change
@@ -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<Version> = MutableLiveData()
val latestVersion: LiveData<Version> = _latestVersion

fun getLatestVersion(version: Version) {
_latestVersion.value = version
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ 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
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
Expand All @@ -22,7 +26,28 @@ class SplashActivity : BindingActivity<ActivitySplashBinding>(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() {
Expand All @@ -36,5 +61,6 @@ class SplashActivity : BindingActivity<ActivitySplashBinding>(R.layout.activity_

companion object {
private const val DURATION: Long = 2000
const val LATEST_VERSION = "latest version"
}
}
Original file line number Diff line number Diff line change
@@ -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<Version> = MutableLiveData()
val latestVersion: LiveData<Version> = _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")
}
}
}
}
Loading

0 comments on commit 830ad95

Please sign in to comment.