From 32074899855c1a5092c0e0e35cc7c0a42bb1c7aa Mon Sep 17 00:00:00 2001
From: Jiwon Ryu <39649594+akuby21@users.noreply.github.com>
Date: Wed, 31 Jul 2024 01:04:41 +0900
Subject: [PATCH] =?UTF-8?q?[=EA=B2=BD=EB=B6=81=EB=8C=80=20Android=20?=
=?UTF-8?q?=EB=A5=98=EC=A7=80=EC=9B=90]=206=EC=A3=BC=EC=B0=A8=20=EA=B3=BC?=
=?UTF-8?q?=EC=A0=9C=5FSTEP1=20(#16)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* init: load previous codes
이전 코드 불러오기
* refactor: implement databinding on ErrorActivity
전주 피드백 사항인 ErrorActivity databinding 추가 적용
* docs: edit README
* feat: implement SplashView
splashview 구현
* feat: implement loading serviceConfig in layer
mvvm 패턴에 알맞게 데이터 레이어에 serviceConfig 불러오도록 구현
* init: load previous codes
이전 코드 불러오기
* refactor: implement databinding on ErrorActivity
전주 피드백 사항인 ErrorActivity databinding 추가 적용
* feat: implement SplashView
splashview 구현
* feat: implement loading serviceConfig in layer
mvvm 패턴에 알맞게 데이터 레이어에 serviceConfig 불러오도록 구현
* refactor: add coroutines for async
비동기 작업을 위해 코루틴 추가
---
README.md | 6 +--
app/build.gradle.kts | 6 +++
app/src/main/AndroidManifest.xml | 13 +++---
.../tech/kakao/map/base/BindingAdapter.kt | 6 +++
.../kakao/map/data/ConfigRepositoryImpl.kt | 13 ++++++
.../datasource/Local/Entity/ConfigEntity.kt | 23 ++++++++++
.../data/datasource/Remote/ConfigService.kt | 33 ++++++++++++++
.../tech/kakao/map/data/di/ConfigModule.kt | 32 +++++++++++++
.../tech/kakao/map/domain/ConfigRepository.kt | 7 +++
.../campus/tech/kakao/map/domain/vo/Config.kt | 6 +++
.../tech/kakao/map/domain/vo/ServiceState.kt | 5 +++
.../map/presenter/view/SplashActivity.kt | 45 +++++++++++++++++++
.../presenter/viewModel/SplashViewModel.kt | 29 ++++++++++++
app/src/main/res/layout/activity_splash.xml | 38 ++++++++++++++++
.../main/res/xml/remote_config_defaults.xml | 11 +++++
build.gradle.kts | 1 +
16 files changed, 266 insertions(+), 8 deletions(-)
create mode 100644 app/src/main/java/campus/tech/kakao/map/data/ConfigRepositoryImpl.kt
create mode 100644 app/src/main/java/campus/tech/kakao/map/data/datasource/Local/Entity/ConfigEntity.kt
create mode 100644 app/src/main/java/campus/tech/kakao/map/data/datasource/Remote/ConfigService.kt
create mode 100644 app/src/main/java/campus/tech/kakao/map/data/di/ConfigModule.kt
create mode 100644 app/src/main/java/campus/tech/kakao/map/domain/ConfigRepository.kt
create mode 100644 app/src/main/java/campus/tech/kakao/map/domain/vo/Config.kt
create mode 100644 app/src/main/java/campus/tech/kakao/map/domain/vo/ServiceState.kt
create mode 100644 app/src/main/java/campus/tech/kakao/map/presenter/view/SplashActivity.kt
create mode 100644 app/src/main/java/campus/tech/kakao/map/presenter/viewModel/SplashViewModel.kt
create mode 100644 app/src/main/res/layout/activity_splash.xml
create mode 100644 app/src/main/res/xml/remote_config_defaults.xml
diff --git a/README.md b/README.md
index fa8c53e5..00b79e51 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,6 @@
- 코드 컨벤션을 준수하며 프로그래밍한다.
## 구현할 기능 목록
### 1단계 기능
-- [ ] 스플래시 화면 구현
-- [ ] Remote Config 값 불러와 비교하기
-- [ ] 에러시 serviceMessage 출력
+- [x] 스플래시 화면 구현
+- [x] Remote Config 값 불러와 비교하기
+- [x] 에러시 serviceMessage 출력
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 5f1d9f42..82e4e908 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -5,6 +5,7 @@ plugins {
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
id("com.google.dagger.hilt.android")
+ id("com.google.gms.google-services")
}
android {
@@ -68,6 +69,9 @@ dependencies {
implementation("androidx.test:core-ktx:1.5.0")
implementation("androidx.activity:activity-ktx:1.9.0")
implementation("androidx.test.espresso:espresso-contrib:3.6.1")
+ implementation("androidx.activity:activity:1.8.0")
+ implementation("com.google.firebase:firebase-config-ktx:22.0.0")
+ implementation("com.google.firebase:firebase-common-ktx:21.0.0")
androidTestImplementation("androidx.test:core:1.5.0")
testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk-android:1.13.11")
@@ -91,6 +95,8 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3")
+ implementation(platform("com.google.firebase:firebase-bom:33.1.2"))
+
}
kapt {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1c5e89db..5bcf1ad6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,10 +19,7 @@
android:usesCleartextTraffic="true"
tools:targetApi="31">
-
@@ -30,9 +27,15 @@
+
+
+ android:exported="false" />
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/base/BindingAdapter.kt b/app/src/main/java/campus/tech/kakao/map/base/BindingAdapter.kt
index 7ec97de3..02b11fee 100644
--- a/app/src/main/java/campus/tech/kakao/map/base/BindingAdapter.kt
+++ b/app/src/main/java/campus/tech/kakao/map/base/BindingAdapter.kt
@@ -5,6 +5,7 @@ import android.view.View
import android.widget.TextView
import androidx.databinding.BindingAdapter
import campus.tech.kakao.map.R
+import campus.tech.kakao.map.domain.vo.ServiceState
import campus.tech.kakao.map.presenter.view.MapActivity
@BindingAdapter("visibleGone")
@@ -19,3 +20,8 @@ fun setErrorTest(view: TextView, type: ErrorEnum){
else -> view.resources.getString(R.string.else_error)
}
}
+
+@BindingAdapter("splashErrorVisible")
+fun isError(view: TextView, serviceState: ServiceState){
+ view.visibility = if(serviceState == ServiceState.ON_SERVICE) View.GONE else View.VISIBLE
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/data/ConfigRepositoryImpl.kt b/app/src/main/java/campus/tech/kakao/map/data/ConfigRepositoryImpl.kt
new file mode 100644
index 00000000..1b5ebe55
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/data/ConfigRepositoryImpl.kt
@@ -0,0 +1,13 @@
+package campus.tech.kakao.map.data
+
+import campus.tech.kakao.map.data.datasource.Local.Entity.toVO
+import campus.tech.kakao.map.data.datasource.Remote.ConfigService
+import campus.tech.kakao.map.domain.ConfigRepository
+import campus.tech.kakao.map.domain.vo.Config
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+
+class ConfigRepositoryImpl @Inject constructor(private val configService: ConfigService) : ConfigRepository{
+ override suspend fun getConfig(): Config = withContext(Dispatchers.IO){ configService.getConfig().toVO() }
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/data/datasource/Local/Entity/ConfigEntity.kt b/app/src/main/java/campus/tech/kakao/map/data/datasource/Local/Entity/ConfigEntity.kt
new file mode 100644
index 00000000..cb7102fc
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/data/datasource/Local/Entity/ConfigEntity.kt
@@ -0,0 +1,23 @@
+package campus.tech.kakao.map.data.datasource.Local.Entity
+
+import campus.tech.kakao.map.domain.vo.Config
+import campus.tech.kakao.map.domain.vo.ServiceState
+import com.google.firebase.remoteconfig.FirebaseRemoteConfig.DEFAULT_VALUE_FOR_STRING
+
+data class ConfigEntity(
+ val serviceState : String,
+ val serviceMessage : String
+)
+
+fun ConfigEntity.toVO() : Config =
+ Config(
+ mapServiceState(serviceState),
+ if(serviceMessage != DEFAULT_VALUE_FOR_STRING) serviceMessage else "unknown"
+ )
+
+fun mapServiceState(serviceState: String): ServiceState =
+ when(serviceState) {
+ "ON_SERVICE" -> ServiceState.ON_SERVICE
+ "OUT_OF_ORDER" -> ServiceState.OUT_OF_ORDER
+ else -> ServiceState.ELSE
+ }
diff --git a/app/src/main/java/campus/tech/kakao/map/data/datasource/Remote/ConfigService.kt b/app/src/main/java/campus/tech/kakao/map/data/datasource/Remote/ConfigService.kt
new file mode 100644
index 00000000..ed03f65f
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/data/datasource/Remote/ConfigService.kt
@@ -0,0 +1,33 @@
+package campus.tech.kakao.map.data.datasource.Remote
+
+import campus.tech.kakao.map.R
+import campus.tech.kakao.map.data.datasource.Local.Entity.ConfigEntity
+import campus.tech.kakao.map.domain.vo.ServiceState
+import com.google.firebase.Firebase
+import com.google.firebase.remoteconfig.FirebaseRemoteConfig
+import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
+import com.google.firebase.remoteconfig.remoteConfig
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.tasks.asDeferred
+import kotlinx.coroutines.tasks.await
+import kotlinx.coroutines.withContext
+
+class ConfigService {
+ private val remoteConfig: FirebaseRemoteConfig = Firebase.remoteConfig
+ private val configSettings = remoteConfigSettings {
+ minimumFetchIntervalInSeconds = 0
+ }
+
+ init{
+ remoteConfig.setConfigSettingsAsync(configSettings)
+ }
+
+ suspend fun getConfig() : ConfigEntity = withContext(Dispatchers.IO){
+ remoteConfig.fetchAndActivate().await()
+ ConfigEntity(
+ remoteConfig.getString("serviceState"),
+ remoteConfig.getString("serviceMessage")
+ )
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/data/di/ConfigModule.kt b/app/src/main/java/campus/tech/kakao/map/data/di/ConfigModule.kt
new file mode 100644
index 00000000..9a5d9472
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/data/di/ConfigModule.kt
@@ -0,0 +1,32 @@
+package campus.tech.kakao.map.data.di
+
+import android.content.SharedPreferences
+import campus.tech.kakao.map.data.ConfigRepositoryImpl
+import campus.tech.kakao.map.data.PlaceRepositoryImpl
+import campus.tech.kakao.map.data.datasource.Local.DB.RoomDB
+import campus.tech.kakao.map.data.datasource.Remote.ConfigService
+import campus.tech.kakao.map.data.datasource.Remote.RemoteService
+import campus.tech.kakao.map.data.datasource.Remote.RetrofitService
+import campus.tech.kakao.map.domain.ConfigRepository
+import campus.tech.kakao.map.domain.PlaceRepository
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object ConfigModule {
+
+ @Provides
+ @Singleton
+ fun provideConfigRepository(
+ configService: ConfigService
+ ) : ConfigRepository =
+ ConfigRepositoryImpl(configService)
+
+ @Provides
+ @Singleton
+ fun provideConfigService() : ConfigService = ConfigService()
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/domain/ConfigRepository.kt b/app/src/main/java/campus/tech/kakao/map/domain/ConfigRepository.kt
new file mode 100644
index 00000000..2b7f62db
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/domain/ConfigRepository.kt
@@ -0,0 +1,7 @@
+package campus.tech.kakao.map.domain
+
+import campus.tech.kakao.map.domain.vo.Config
+
+interface ConfigRepository {
+ suspend fun getConfig() : Config
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/domain/vo/Config.kt b/app/src/main/java/campus/tech/kakao/map/domain/vo/Config.kt
new file mode 100644
index 00000000..c781091a
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/domain/vo/Config.kt
@@ -0,0 +1,6 @@
+package campus.tech.kakao.map.domain.vo
+
+data class Config(
+ val serviceState : ServiceState,
+ val serviceMessage : String
+)
diff --git a/app/src/main/java/campus/tech/kakao/map/domain/vo/ServiceState.kt b/app/src/main/java/campus/tech/kakao/map/domain/vo/ServiceState.kt
new file mode 100644
index 00000000..c387afd5
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/domain/vo/ServiceState.kt
@@ -0,0 +1,5 @@
+package campus.tech.kakao.map.domain.vo
+
+enum class ServiceState {
+ ON_SERVICE,LOADING,OUT_OF_ORDER,ELSE
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/presenter/view/SplashActivity.kt b/app/src/main/java/campus/tech/kakao/map/presenter/view/SplashActivity.kt
new file mode 100644
index 00000000..a1bf6e9e
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/presenter/view/SplashActivity.kt
@@ -0,0 +1,45 @@
+package campus.tech.kakao.map.presenter.view
+
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.databinding.DataBindingUtil
+import campus.tech.kakao.map.R
+import campus.tech.kakao.map.databinding.ActivitySplashBinding
+import campus.tech.kakao.map.domain.vo.ServiceState
+import campus.tech.kakao.map.presenter.viewModel.MapViewModel
+import campus.tech.kakao.map.presenter.viewModel.SplashViewModel
+import com.google.firebase.Firebase
+import com.google.firebase.remoteconfig.FirebaseRemoteConfig
+import com.google.firebase.remoteconfig.get
+import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
+import com.google.firebase.remoteconfig.remoteConfig
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class SplashActivity : AppCompatActivity() {
+ private val viewModel: SplashViewModel by viewModels()
+ private lateinit var binding: ActivitySplashBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_splash)
+
+ binding = DataBindingUtil.setContentView(this, R.layout.activity_splash)
+ binding.lifecycleOwner = this
+ binding.viewModel = viewModel
+
+ viewModel.config.observe(this){
+ if(it.serviceState == ServiceState.ON_SERVICE){
+ startActivity(Intent(this,MapActivity::class.java))
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/presenter/viewModel/SplashViewModel.kt b/app/src/main/java/campus/tech/kakao/map/presenter/viewModel/SplashViewModel.kt
new file mode 100644
index 00000000..07dbb582
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/presenter/viewModel/SplashViewModel.kt
@@ -0,0 +1,29 @@
+package campus.tech.kakao.map.presenter.viewModel
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import campus.tech.kakao.map.domain.ConfigRepository
+import campus.tech.kakao.map.domain.vo.Config
+import campus.tech.kakao.map.domain.vo.ServiceState
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class SplashViewModel @Inject constructor(
+ private val configRepository: ConfigRepository
+) : ViewModel() {
+ private val _config: MutableLiveData = MutableLiveData(
+ Config(serviceState = ServiceState.LOADING, serviceMessage = "Loading...")
+ )
+ val config: LiveData = _config
+
+ init {
+ viewModelScope.launch {
+ _config.postValue(configRepository.getConfig())
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml
new file mode 100644
index 00000000..6c011d75
--- /dev/null
+++ b/app/src/main/res/layout/activity_splash.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/remote_config_defaults.xml b/app/src/main/res/xml/remote_config_defaults.xml
new file mode 100644
index 00000000..26fbfebd
--- /dev/null
+++ b/app/src/main/res/xml/remote_config_defaults.xml
@@ -0,0 +1,11 @@
+
+
+
+ serviceMessage
+ 서버 점검 중입니다. 나중에 다시 시도해 주세요.
+
+
+ serviceState
+ ON_SERVICE
+
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 7d703bf2..4da8acbe 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,6 +4,7 @@ plugins {
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
id("org.jlleitschuh.gradle.ktlint") version "12.1.0" apply false
id("com.google.dagger.hilt.android") version "2.48.1" apply false
+ id("com.google.gms.google-services") version "4.4.2" apply false
}
allprojects {