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 {