diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 806f17f3..397d12dc 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -20,8 +20,8 @@ # hide the original source file name. #-renamesourcefileattribute SourceFile - -keep class com.unifest.android.feature.map.model.** { *; } - +-keep class com.unifest.android.feature.map.model.** { *; } +-keep class *.** { *; } -dontwarn com.unifest.android.core.common.ErrorHandlerActions -dontwarn com.unifest.android.core.common.HandleExceptionKt -dontwarn com.unifest.android.core.common.ObserveEventKt @@ -35,6 +35,9 @@ -dontwarn com.unifest.android.core.data.datasource.RemoteConfigDataSourceImpl -dontwarn com.unifest.android.core.data.di.FirebaseModule_ProvideMessagingFactory -dontwarn com.unifest.android.core.data.di.FirebaseModule_ProvideRemoteConfigFactory +-dontwarn com.unifest.android.core.data.di.HiltWrapper_DataSourceModule +-dontwarn com.unifest.android.core.data.di.HiltWrapper_FirebaseModule +-dontwarn com.unifest.android.core.data.di.HiltWrapper_RepositoryModule -dontwarn com.unifest.android.core.data.repository.BoothRepository -dontwarn com.unifest.android.core.data.repository.BoothRepositoryImpl -dontwarn com.unifest.android.core.data.repository.FestivalRepository @@ -49,22 +52,31 @@ -dontwarn com.unifest.android.core.data.repository.OnboardingRepositoryImpl -dontwarn com.unifest.android.core.data.repository.RemoteConfigRepository -dontwarn com.unifest.android.core.data.repository.RemoteConfigRepositoryImpl +-dontwarn com.unifest.android.core.data.repository.SettingRepository +-dontwarn com.unifest.android.core.data.repository.SettingRepositoryImpl -dontwarn com.unifest.android.core.data.repository.WaitingRepository -dontwarn com.unifest.android.core.data.repository.WaitingRepositoryImpl -dontwarn com.unifest.android.core.database.LikedBoothDao -dontwarn com.unifest.android.core.database.LikedFestivalDao +-dontwarn com.unifest.android.core.database.di.DaoModule -dontwarn com.unifest.android.core.database.di.DaoModule_ProvideLikedBoothDaoFactory -dontwarn com.unifest.android.core.database.di.DaoModule_ProvideLikedFestivalDaoFactory +-dontwarn com.unifest.android.core.database.di.DatabaseModule -dontwarn com.unifest.android.core.database.di.DatabaseModule_ProvideLikedBoothDatabaseFactory -dontwarn com.unifest.android.core.database.di.DatabaseModule_ProvideLikedFestivalDatabaseFactory -dontwarn com.unifest.android.core.datastore.OnboardingDataSource -dontwarn com.unifest.android.core.datastore.OnboardingDataSourceImpl -dontwarn com.unifest.android.core.datastore.RecentLikedFestivalDataSource -dontwarn com.unifest.android.core.datastore.RecentLikedFestivalDataSourceImpl +-dontwarn com.unifest.android.core.datastore.SettingDataSource +-dontwarn com.unifest.android.core.datastore.SettingDataSourceImpl -dontwarn com.unifest.android.core.datastore.TokenDataSource -dontwarn com.unifest.android.core.datastore.TokenDataSourceImpl +-dontwarn com.unifest.android.core.datastore.di.DataSourceModule -dontwarn com.unifest.android.core.datastore.di.DataStoreModule_ProvideOnboardingDataStore$datastore_releaseFactory -dontwarn com.unifest.android.core.datastore.di.DataStoreModule_ProvideRecentFestivalDataStore$datastore_releaseFactory +-dontwarn com.unifest.android.core.datastore.di.HiltWrapper_DataStoreModule +-dontwarn com.unifest.android.core.designsystem.ComponentPreview -dontwarn com.unifest.android.core.designsystem.component.ButtonKt -dontwarn com.unifest.android.core.designsystem.component.DialogKt -dontwarn com.unifest.android.core.designsystem.component.LoadingWheelKt @@ -82,36 +94,70 @@ -dontwarn com.unifest.android.core.navigation.MainTabRoute$Waiting -dontwarn com.unifest.android.core.navigation.MainTabRoute -dontwarn com.unifest.android.core.navigation.Route +-dontwarn com.unifest.android.core.network.di.ApiModule -dontwarn com.unifest.android.core.network.di.ApiModule_ProvideUnifestService$network_releaseFactory +-dontwarn com.unifest.android.core.network.di.HiltWrapper_NetworkModule -dontwarn com.unifest.android.core.network.di.NetworkModule_ProvideHttpLoggingInterceptor$network_releaseFactory -dontwarn com.unifest.android.core.network.di.NetworkModule_ProvideUnifestApiRetrofit$network_releaseFactory -dontwarn com.unifest.android.core.network.di.NetworkModule_ProvideUnifestOkHttpClient$network_releaseFactory -dontwarn com.unifest.android.core.network.service.UnifestService +-dontwarn com.unifest.android.core.ui.DevicePreview -dontwarn com.unifest.android.core.ui.component.CameraPermissionTextProvider -dontwarn com.unifest.android.core.ui.component.LikedFestivalGridKt -dontwarn com.unifest.android.core.ui.component.PermissionDialogKt -dontwarn com.unifest.android.core.ui.component.PermissionTextProvider -dontwarn com.unifest.android.feature.booth.navigation.BoothNavigationKt -dontwarn com.unifest.android.feature.booth.viewmodel.BoothViewModel +-dontwarn com.unifest.android.feature.booth.viewmodel.BoothViewModel_HiltModules$BindsModule -dontwarn com.unifest.android.feature.booth.viewmodel.BoothViewModel_HiltModules$KeyModule -dontwarn com.unifest.android.feature.festival.viewmodel.FestivalViewModel +-dontwarn com.unifest.android.feature.festival.viewmodel.FestivalViewModel_HiltModules$BindsModule -dontwarn com.unifest.android.feature.festival.viewmodel.FestivalViewModel_HiltModules$KeyModule -dontwarn com.unifest.android.feature.home.navigation.HomeNavigationKt -dontwarn com.unifest.android.feature.home.viewmodel.HomeViewModel +-dontwarn com.unifest.android.feature.home.viewmodel.HomeViewModel_HiltModules$BindsModule -dontwarn com.unifest.android.feature.home.viewmodel.HomeViewModel_HiltModules$KeyModule -dontwarn com.unifest.android.feature.liked_booth.navigation.LikedBoothNavigationKt -dontwarn com.unifest.android.feature.liked_booth.viewmodel.LikedBoothViewModel +-dontwarn com.unifest.android.feature.liked_booth.viewmodel.LikedBoothViewModel_HiltModules$BindsModule -dontwarn com.unifest.android.feature.liked_booth.viewmodel.LikedBoothViewModel_HiltModules$KeyModule -dontwarn com.unifest.android.feature.map.navigation.MapNavigationKt -dontwarn com.unifest.android.feature.map.viewmodel.MapViewModel +-dontwarn com.unifest.android.feature.map.viewmodel.MapViewModel_HiltModules$BindsModule -dontwarn com.unifest.android.feature.map.viewmodel.MapViewModel_HiltModules$KeyModule -dontwarn com.unifest.android.feature.menu.navigation.MenuNavigationKt -dontwarn com.unifest.android.feature.menu.viewmodel.MenuViewModel +-dontwarn com.unifest.android.feature.menu.viewmodel.MenuViewModel_HiltModules$BindsModule -dontwarn com.unifest.android.feature.menu.viewmodel.MenuViewModel_HiltModules$KeyModule -dontwarn com.unifest.android.feature.navigator.IntroNavigator -dontwarn com.unifest.android.feature.navigator.MainNavigator -dontwarn com.unifest.android.feature.navigator.Navigator$DefaultImpls -dontwarn com.unifest.android.feature.navigator.Navigator +-dontwarn com.unifest.android.feature.navigator.SplashNavigator -dontwarn com.unifest.android.feature.waiting.navigation.WaitingNavigationKt -dontwarn com.unifest.android.feature.waiting.viewmodel.WaitingViewModel +-dontwarn com.unifest.android.feature.waiting.viewmodel.WaitingViewModel_HiltModules$BindsModule -dontwarn com.unifest.android.feature.waiting.viewmodel.WaitingViewModel_HiltModules$KeyModule +-dontwarn hilt_aggregated_deps._com_unifest_android_core_data_di_HiltWrapper_DataSourceModule +-dontwarn hilt_aggregated_deps._com_unifest_android_core_data_di_HiltWrapper_FirebaseModule +-dontwarn hilt_aggregated_deps._com_unifest_android_core_data_di_HiltWrapper_RepositoryModule +-dontwarn hilt_aggregated_deps._com_unifest_android_core_database_di_DaoModule +-dontwarn hilt_aggregated_deps._com_unifest_android_core_database_di_DatabaseModule +-dontwarn hilt_aggregated_deps._com_unifest_android_core_datastore_di_DataSourceModule +-dontwarn hilt_aggregated_deps._com_unifest_android_core_datastore_di_HiltWrapper_DataStoreModule +-dontwarn hilt_aggregated_deps._com_unifest_android_core_network_di_ApiModule +-dontwarn hilt_aggregated_deps._com_unifest_android_core_network_di_HiltWrapper_NetworkModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_booth_viewmodel_BoothViewModel_HiltModules_BindsModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_booth_viewmodel_BoothViewModel_HiltModules_KeyModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_festival_viewmodel_FestivalViewModel_HiltModules_BindsModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_festival_viewmodel_FestivalViewModel_HiltModules_KeyModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_home_viewmodel_HomeViewModel_HiltModules_BindsModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_home_viewmodel_HomeViewModel_HiltModules_KeyModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_liked_booth_viewmodel_LikedBoothViewModel_HiltModules_BindsModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_liked_booth_viewmodel_LikedBoothViewModel_HiltModules_KeyModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_map_viewmodel_MapViewModel_HiltModules_BindsModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_map_viewmodel_MapViewModel_HiltModules_KeyModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_menu_viewmodel_MenuViewModel_HiltModules_BindsModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_menu_viewmodel_MenuViewModel_HiltModules_KeyModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_waiting_viewmodel_WaitingViewModel_HiltModules_BindsModule +-dontwarn hilt_aggregated_deps._com_unifest_android_feature_waiting_viewmodel_WaitingViewModel_HiltModules_KeyModule diff --git a/app/src/main/kotlin/com/unifest/android/initializer/FirebaseMessagingInitializer.kt b/app/src/main/kotlin/com/unifest/android/initializer/FirebaseMessagingInitializer.kt index 130dbbc3..fc2cb1c6 100644 --- a/app/src/main/kotlin/com/unifest/android/initializer/FirebaseMessagingInitializer.kt +++ b/app/src/main/kotlin/com/unifest/android/initializer/FirebaseMessagingInitializer.kt @@ -3,6 +3,7 @@ package com.unifest.android.initializer import android.content.Context import androidx.startup.Initializer import com.google.firebase.messaging.FirebaseMessaging +import com.unifest.android.BuildConfig import timber.log.Timber class FirebaseMessagingInitializer : Initializer { @@ -15,6 +16,17 @@ class FirebaseMessagingInitializer : Initializer { Timber.e("Failed to subscribe to topic") } } + + if (BuildConfig.DEBUG) { + FirebaseMessaging.getInstance().subscribeToTopic("test") + .addOnCompleteListener { task -> + if (task.isSuccessful) { + Timber.d("Subscribed to topic successfully") + } else { + Timber.e("Failed to subscribe to topic") + } + } + } } override fun dependencies(): List>> { diff --git a/app/src/main/kotlin/com/unifest/android/service/UnifestFirebaseMessagingService.kt b/app/src/main/kotlin/com/unifest/android/service/UnifestFirebaseMessagingService.kt index 07f3f3df..7d66a622 100644 --- a/app/src/main/kotlin/com/unifest/android/service/UnifestFirebaseMessagingService.kt +++ b/app/src/main/kotlin/com/unifest/android/service/UnifestFirebaseMessagingService.kt @@ -16,6 +16,13 @@ import timber.log.Timber class UnifestFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) + Timber.d("$remoteMessage") + Timber.d("${remoteMessage.notification?.title}") + Timber.d("${remoteMessage.notification?.body}") + Timber.d("${remoteMessage.data.size}") + Timber.d("${remoteMessage.data.keys}") + Timber.d("${remoteMessage.data.entries}") + if (remoteMessage.notification != null) { sendNotification(remoteMessage) } @@ -24,18 +31,18 @@ class UnifestFirebaseMessagingService : FirebaseMessagingService() { private fun sendNotification(remoteMessage: RemoteMessage) { val requestCode = System.currentTimeMillis().toInt() + // Activity 의 onNewIntent 가 호출되기 위해선 해당 flag 들이 필요함 val intent = Intent(this, MainActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).also { - if (remoteMessage.data["boothId"] != null) { - if (remoteMessage.data["waitingId"] != null) { - Timber.tag("UnifestFirebaseMessagingService").d("waitingId: ${remoteMessage.data["waitingId"]}") - putExtra("navigate_to_waiting", true) - putExtra("waitingId", remoteMessage.data["waitingId"]) - } else { - Timber.tag("UnifestFirebaseMessagingService").d("boothId: ${remoteMessage.data["boothId"]}") - putExtra("navigate_to_booth", true) - putExtra("boothId", remoteMessage.data["boothId"]) - } + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + } + + for (key in remoteMessage.data.keys) { + val value = remoteMessage.data[key] + when { + value == null -> Timber.d("Null value for key: $key") + else -> { + intent.putExtra(key, value) + Timber.d("data -> key $key -> $value") } } } diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index edac0e96..5c719fe1 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -47,7 +47,7 @@ dependencies { compileOnly(libs.gradle.android) compileOnly(libs.gradle.kotlin) compileOnly(libs.gradle.androidx.room) - compileOnly(libs.compose.compiler.extension) + // compileOnly(libs.compose.compiler.extension) compileOnly(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) } diff --git a/build-logic/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt b/build-logic/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt index 8937b66e..4444fb47 100644 --- a/build-logic/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt +++ b/build-logic/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt @@ -6,7 +6,8 @@ import org.gradle.kotlin.dsl.configure internal class AndroidApplicationComposeConventionPlugin : BuildLogicConventionPlugin( { - applyPlugins(Plugins.ANDROID_APPLICATION, Plugins.COMPOSE_COMPILER) + // applyPlugins(Plugins.ANDROID_APPLICATION, Plugins.COMPOSE_COMPILER) + applyPlugins(Plugins.ANDROID_APPLICATION) extensions.configure { configureCompose(this) diff --git a/build-logic/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt b/build-logic/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt index cf0b5b87..7066953f 100644 --- a/build-logic/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt +++ b/build-logic/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt @@ -6,7 +6,8 @@ import org.gradle.kotlin.dsl.configure internal class AndroidLibraryComposeConventionPlugin : BuildLogicConventionPlugin( { - applyPlugins(Plugins.ANDROID_LIBRARY, Plugins.COMPOSE_COMPILER) + // applyPlugins(Plugins.ANDROID_LIBRARY, Plugins.COMPOSE_COMPILER) + applyPlugins(Plugins.ANDROID_LIBRARY) extensions.configure { configureCompose(this) diff --git a/build-logic/src/main/kotlin/com/unifest/android/Android.kt b/build-logic/src/main/kotlin/com/unifest/android/Android.kt index 6252fcd7..ddd66968 100644 --- a/build-logic/src/main/kotlin/com/unifest/android/Android.kt +++ b/build-logic/src/main/kotlin/com/unifest/android/Android.kt @@ -7,7 +7,7 @@ import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension -internal fun Project.configureAndroid(extension: CommonExtension<*, *, *, *, *, *>) { +internal fun Project.configureAndroid(extension: CommonExtension<*, *, *, *, *>) { extension.apply { compileSdk = libs.versions.compileSdk.get().toInt() diff --git a/build-logic/src/main/kotlin/com/unifest/android/Compose.kt b/build-logic/src/main/kotlin/com/unifest/android/Compose.kt index b4587b1d..abe9e7bb 100644 --- a/build-logic/src/main/kotlin/com/unifest/android/Compose.kt +++ b/build-logic/src/main/kotlin/com/unifest/android/Compose.kt @@ -2,25 +2,60 @@ package com.unifest.android import com.android.build.api.dsl.CommonExtension import org.gradle.api.Project -import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies -import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -internal fun Project.configureCompose(extension: CommonExtension<*, *, *, *, *, *>) { +//internal fun Project.configureCompose(extension: CommonExtension<*, *, *, *, *, *>) { +// extension.apply { +// dependencies { +// implementation(libs.bundles.androidx.compose) +// debugImplementation(libs.androidx.compose.ui.tooling) +// } +// +// configure { +// enableStrongSkippingMode.set(true) +// includeSourceInformation.set(true) +// +// metricsDestination.file("build/composeMetrics") +// reportsDestination.file("build/composeReports") +// +// stabilityConfigurationFile.set(project.rootDir.resolve("stability.config.conf")) +// } +// } +//} + +internal fun Project.configureCompose(extension: CommonExtension<*, *, *, *, *>) { extension.apply { - dependencies { - implementation(libs.bundles.androidx.compose) - debugImplementation(libs.androidx.compose.ui.tooling) + buildFeatures { + compose = true } - configure { - enableStrongSkippingMode.set(true) - includeSourceInformation.set(true) + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() + } - metricsDestination.file("build/composeMetrics") - reportsDestination.file("build/composeReports") + dependencies { + implementation(libs.androidx.compose.bom) + androidTestImplementation(libs.androidx.compose.bom) + implementation(libs.androidx.compose.material.iconsExtended) + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.ui.tooling.preview) + debugImplementation(libs.androidx.compose.ui.tooling) + } + } - stabilityConfigurationFile.set(project.rootDir.resolve("stability.config.conf")) + tasks.withType().configureEach { + kotlinOptions { + freeCompilerArgs = freeCompilerArgs + listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=$rootDir/report/compose-metrics", + ) + freeCompilerArgs = freeCompilerArgs + listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=$rootDir/report/compose-reports", + ) } } } diff --git a/build-logic/src/main/kotlin/com/unifest/android/Extensions.kt b/build-logic/src/main/kotlin/com/unifest/android/Extensions.kt index 9eb35d79..5a919522 100644 --- a/build-logic/src/main/kotlin/com/unifest/android/Extensions.kt +++ b/build-logic/src/main/kotlin/com/unifest/android/Extensions.kt @@ -25,7 +25,7 @@ internal val Project.isAndroidProject: Boolean get() = pluginManager.hasPlugin(Plugins.ANDROID_APPLICATION) || pluginManager.hasPlugin(Plugins.ANDROID_LIBRARY) -internal val Project.androidExtensions: CommonExtension<*, *, *, *, *, *> +internal val Project.androidExtensions: CommonExtension<*, *, *, *, *> get() { return if (pluginManager.hasPlugin(Plugins.ANDROID_APPLICATION)) { extensions.getByType() diff --git a/build-logic/src/main/kotlin/com/unifest/android/Plugins.kt b/build-logic/src/main/kotlin/com/unifest/android/Plugins.kt index 27a9de1d..14a2e397 100644 --- a/build-logic/src/main/kotlin/com/unifest/android/Plugins.kt +++ b/build-logic/src/main/kotlin/com/unifest/android/Plugins.kt @@ -9,7 +9,7 @@ internal object Plugins { const val ANDROID_APPLICATION = "com.android.application" const val ANDROID_LIBRARY = "com.android.library" - const val COMPOSE_COMPILER = "org.jetbrains.kotlin.plugin.compose" + // const val COMPOSE_COMPILER = "org.jetbrains.kotlin.plugin.compose" const val ANDROIDX_ROOM = "androidx.room" diff --git a/build.gradle.kts b/build.gradle.kts index 7a371da9..56afad3b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,12 +11,12 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.android.library) apply false alias(libs.plugins.androidx.room) apply false - alias(libs.plugins.compose.compiler) apply false + // alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.hilt) apply false alias(libs.plugins.google.service) apply false alias(libs.plugins.firebase.crashlytics) apply false alias(libs.plugins.ksp) apply false - alias(libs.plugins.compose.investigator) apply false + // alias(libs.plugins.compose.investigator) apply false } buildscript { diff --git a/core/data/src/main/kotlin/com/unifest/android/core/data/di/RepositoryModule.kt b/core/data/src/main/kotlin/com/unifest/android/core/data/di/RepositoryModule.kt index b77e3eef..91ba9eab 100644 --- a/core/data/src/main/kotlin/com/unifest/android/core/data/di/RepositoryModule.kt +++ b/core/data/src/main/kotlin/com/unifest/android/core/data/di/RepositoryModule.kt @@ -14,6 +14,8 @@ import com.unifest.android.core.data.repository.OnboardingRepository import com.unifest.android.core.data.repository.OnboardingRepositoryImpl import com.unifest.android.core.data.repository.RemoteConfigRepository import com.unifest.android.core.data.repository.RemoteConfigRepositoryImpl +import com.unifest.android.core.data.repository.SettingRepository +import com.unifest.android.core.data.repository.SettingRepositoryImpl import com.unifest.android.core.data.repository.WaitingRepository import com.unifest.android.core.data.repository.WaitingRepositoryImpl import dagger.Binds @@ -56,4 +58,8 @@ internal abstract class RepositoryModule { @Binds @Singleton abstract fun bindMessagingRepository(messagingRepositoryImpl: MessagingRepositoryImpl): MessagingRepository + + @Binds + @Singleton + abstract fun bindSettingRepository(settingRepositoryImpl: SettingRepositoryImpl): SettingRepository } diff --git a/core/data/src/main/kotlin/com/unifest/android/core/data/repository/LikedFestivalRepositoryImpl.kt b/core/data/src/main/kotlin/com/unifest/android/core/data/repository/LikedFestivalRepositoryImpl.kt index 7d472735..bb2cb47a 100644 --- a/core/data/src/main/kotlin/com/unifest/android/core/data/repository/LikedFestivalRepositoryImpl.kt +++ b/core/data/src/main/kotlin/com/unifest/android/core/data/repository/LikedFestivalRepositoryImpl.kt @@ -58,13 +58,13 @@ internal class LikedFestivalRepositoryImpl @Inject constructor( override suspend fun registerLikedFestival() = runSuspendCatching { val festivalId = recentLikedFestivalDataSource.getRecentLikedFestivalId() - val fcmToken = tokenDataSource.getFCMToken() ?: "" + val fcmToken = tokenDataSource.getFCMToken() service.registerLikedFestival(LikedFestivalRequest(festivalId, fcmToken)) } override suspend fun unregisterLikedFestival() = runSuspendCatching { val festivalId = recentLikedFestivalDataSource.getRecentLikedFestivalId() - val fcmToken = tokenDataSource.getFCMToken() ?: "" + val fcmToken = tokenDataSource.getFCMToken() service.unregisterLikedFestival(LikedFestivalRequest(festivalId, fcmToken)) } } diff --git a/core/data/src/main/kotlin/com/unifest/android/core/data/repository/SettingRepository.kt b/core/data/src/main/kotlin/com/unifest/android/core/data/repository/SettingRepository.kt new file mode 100644 index 00000000..b4d352db --- /dev/null +++ b/core/data/src/main/kotlin/com/unifest/android/core/data/repository/SettingRepository.kt @@ -0,0 +1,8 @@ +package com.unifest.android.core.data.repository + +import kotlinx.coroutines.flow.Flow + +interface SettingRepository { + fun flowIsClusteringEnabled(): Flow + suspend fun updateIsClusteringEnabled(isClusteringEnabled: Boolean) +} diff --git a/core/data/src/main/kotlin/com/unifest/android/core/data/repository/SettingRepositoryImpl.kt b/core/data/src/main/kotlin/com/unifest/android/core/data/repository/SettingRepositoryImpl.kt new file mode 100644 index 00000000..8f9120e7 --- /dev/null +++ b/core/data/src/main/kotlin/com/unifest/android/core/data/repository/SettingRepositoryImpl.kt @@ -0,0 +1,17 @@ +package com.unifest.android.core.data.repository + +import com.unifest.android.core.datastore.SettingDataSource +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +internal class SettingRepositoryImpl @Inject constructor( + private val settingDataSource: SettingDataSource, +) : SettingRepository { + override fun flowIsClusteringEnabled(): Flow = + settingDataSource.settingsData.map { settingsData -> settingsData.isClusteringEnabled } + + override suspend fun updateIsClusteringEnabled(isClusteringEnabled: Boolean) { + settingDataSource.updateIsClusteringEnabled(isClusteringEnabled) + } +} diff --git a/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/SettingDataSource.kt b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/SettingDataSource.kt new file mode 100644 index 00000000..4608748d --- /dev/null +++ b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/SettingDataSource.kt @@ -0,0 +1,9 @@ +package com.unifest.android.core.datastore + +import com.unifest.android.core.datastore.model.SettingsData +import kotlinx.coroutines.flow.Flow + +interface SettingDataSource { + val settingsData: Flow + suspend fun updateIsClusteringEnabled(isClusteringEnabled: Boolean) +} diff --git a/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/SettingDataSourceImpl.kt b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/SettingDataSourceImpl.kt new file mode 100644 index 00000000..d79ce102 --- /dev/null +++ b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/SettingDataSourceImpl.kt @@ -0,0 +1,30 @@ +package com.unifest.android.core.datastore + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import com.unifest.android.core.datastore.di.OnboardingDataStore +import com.unifest.android.core.datastore.model.SettingsData +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +class SettingDataSourceImpl @Inject constructor( + @OnboardingDataStore private val dataStore: DataStore, +) : SettingDataSource { + private companion object PreferencesKey { + private val KEY_IS_CLUSTERING_ENABLED = booleanPreferencesKey("is_clustering_enabled") + } + + override val settingsData = dataStore.data.map { preferences -> + SettingsData( + isClusteringEnabled = preferences[KEY_IS_CLUSTERING_ENABLED] ?: true, + ) + } + + override suspend fun updateIsClusteringEnabled(isClusteringEnabled: Boolean) { + dataStore.edit { preferences -> + preferences[KEY_IS_CLUSTERING_ENABLED] = isClusteringEnabled + } + } +} diff --git a/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataSourceModule.kt b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataSourceModule.kt index bc45ff86..f3373384 100644 --- a/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataSourceModule.kt +++ b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataSourceModule.kt @@ -4,6 +4,8 @@ import com.unifest.android.core.datastore.OnboardingDataSource import com.unifest.android.core.datastore.OnboardingDataSourceImpl import com.unifest.android.core.datastore.RecentLikedFestivalDataSource import com.unifest.android.core.datastore.RecentLikedFestivalDataSourceImpl +import com.unifest.android.core.datastore.SettingDataSource +import com.unifest.android.core.datastore.SettingDataSourceImpl import com.unifest.android.core.datastore.TokenDataSource import com.unifest.android.core.datastore.TokenDataSourceImpl import dagger.Binds @@ -26,4 +28,8 @@ abstract class DataSourceModule { @Binds @Singleton abstract fun bindTokenDataSource(tokenDataSourceImpl: TokenDataSourceImpl): TokenDataSource + + @Binds + @Singleton + abstract fun bindSettingDataSource(settingDataSourceImpl: SettingDataSourceImpl): SettingDataSource } diff --git a/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataStoreModule.kt b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataStoreModule.kt index 43248534..120f2db2 100644 --- a/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataStoreModule.kt +++ b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataStoreModule.kt @@ -17,9 +17,12 @@ private val Context.onboardingDataStore: DataStore by preferencesDa private const val RECENT_LIKED_FESTIVAL_DATASTORE = "recent_liked_festival_datastore" private val Context.recentLikedFestivalDataStore: DataStore by preferencesDataStore(name = RECENT_LIKED_FESTIVAL_DATASTORE) -private const val TOKEN_DATASTORE = "onboarding_datastore" +private const val TOKEN_DATASTORE = "token_datastore" private val Context.tokenDataStore: DataStore by preferencesDataStore(name = TOKEN_DATASTORE) +private const val SETTING_DATASTORE = "setting_datastore" +private val Context.settingDataStore: DataStore by preferencesDataStore(name = SETTING_DATASTORE) + @Module @InstallIn(SingletonComponent::class) internal object DataStoreModule { @@ -38,4 +41,9 @@ internal object DataStoreModule { @Singleton @Provides internal fun provideTokenDataStore(@ApplicationContext context: Context) = context.tokenDataStore + + @TokenDataStore + @Singleton + @Provides + internal fun provideSettingDataStore(@ApplicationContext context: Context) = context.settingDataStore } diff --git a/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataStoreQualifier.kt b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataStoreQualifier.kt index 56a40594..7bb3fc06 100644 --- a/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataStoreQualifier.kt +++ b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/di/DataStoreQualifier.kt @@ -13,3 +13,7 @@ annotation class RecentLikedFestivalDataStore @Qualifier @Retention(AnnotationRetention.BINARY) annotation class TokenDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class SettingDataStore diff --git a/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/model/SettingsData.kt b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/model/SettingsData.kt new file mode 100644 index 00000000..cd005fa8 --- /dev/null +++ b/core/datastore/src/main/kotlin/com/unifest/android/core/datastore/model/SettingsData.kt @@ -0,0 +1,5 @@ +package com.unifest.android.core.datastore.model + +data class SettingsData( + val isClusteringEnabled: Boolean, +) diff --git a/core/designsystem/src/main/res/drawable/ic_cluster.xml b/core/designsystem/src/main/res/drawable/ic_cluster.xml new file mode 100644 index 00000000..3f26b0f4 --- /dev/null +++ b/core/designsystem/src/main/res/drawable/ic_cluster.xml @@ -0,0 +1,11 @@ + + + diff --git a/core/designsystem/src/main/res/drawable/ic_cluster_icon.xml b/core/designsystem/src/main/res/drawable/ic_cluster_icon.xml deleted file mode 100644 index 4e026e3e..00000000 --- a/core/designsystem/src/main/res/drawable/ic_cluster_icon.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/core/navigation/src/main/kotlin/com/unifest/android/core/navigation/RouteModel.kt b/core/navigation/src/main/kotlin/com/unifest/android/core/navigation/RouteModel.kt index a845b0a4..6692c5d7 100644 --- a/core/navigation/src/main/kotlin/com/unifest/android/core/navigation/RouteModel.kt +++ b/core/navigation/src/main/kotlin/com/unifest/android/core/navigation/RouteModel.kt @@ -1,34 +1,32 @@ package com.unifest.android.core.navigation -import kotlinx.serialization.Serializable - -sealed interface MainTabRoute : Route { - @Serializable - data object Home : MainTabRoute - - @Serializable - data object Waiting : MainTabRoute - - @Serializable - data object Map : MainTabRoute - - @Serializable - data object Stamp : MainTabRoute - - @Serializable - data object Menu : MainTabRoute -} - -sealed interface Route { - @Serializable - data object Booth { - @Serializable - data class BoothDetail(val boothId: Long) : Route - - @Serializable - data object BoothLocation : Route - } - - @Serializable - data object LikeBooth : Route -} +// sealed interface MainTabRoute : Route { +// @Serializable +// data object Home : MainTabRoute +// +// @Serializable +// data object Waiting : MainTabRoute +// +// @Serializable +// data object Map : MainTabRoute +// +// @Serializable +// data object Stamp : MainTabRoute +// +// @Serializable +// data object Menu : MainTabRoute +// } +// +// sealed interface Route { +// @Serializable +// data object Booth { +// @Serializable +// data class BoothDetail(val boothId: Long) : Route +// +// @Serializable +// data object BoothLocation : Route +// } +// +// @Serializable +// data object LikeBooth : Route +// } diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 7a266a00..b3ae2e67 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -47,5 +47,5 @@ secrets { } fun getServerBaseUrl(propertyKey: String): String { - return gradleLocalProperties(rootDir, providers).getProperty(propertyKey) + return gradleLocalProperties(rootDir).getProperty(propertyKey) } diff --git a/feature/booth/src/main/kotlin/com/unifest/android/feature/booth/navigation/BoothNavigation.kt b/feature/booth/src/main/kotlin/com/unifest/android/feature/booth/navigation/BoothNavigation.kt index e8b2389a..838e6b45 100644 --- a/feature/booth/src/main/kotlin/com/unifest/android/feature/booth/navigation/BoothNavigation.kt +++ b/feature/booth/src/main/kotlin/com/unifest/android/feature/booth/navigation/BoothNavigation.kt @@ -4,22 +4,30 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController +import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.compose.navigation +import androidx.navigation.navArgument import com.unifest.android.core.common.extension.sharedViewModel -import com.unifest.android.core.navigation.Route import com.unifest.android.feature.booth.BoothDetailRoute import com.unifest.android.feature.booth.BoothLocationRoute import com.unifest.android.feature.booth.viewmodel.BoothViewModel +const val BOOTH_ID = "booth_id" +const val BOOTH_ROUTE = "booth_route/{$BOOTH_ID}" +const val BOOTH_DETAIL_ROUTE = "booth_detail_route" +const val BOOTH_LOCATION_ROUTE = "booth_location_route" + fun NavController.navigateToBoothDetail( boothId: Long, ) { - navigate(Route.Booth.BoothDetail(boothId)) + // navigate(Route.Booth.BoothDetail(boothId)) + navigate("booth_route/$boothId") } fun NavController.navigateToBoothLocation() { - navigate(Route.Booth.BoothLocation) + // navigate(Route.Booth.BoothLocation) + navigate(BOOTH_LOCATION_ROUTE) } fun NavGraphBuilder.boothNavGraph( @@ -28,10 +36,20 @@ fun NavGraphBuilder.boothNavGraph( popBackStack: () -> Unit, navigateToBoothLocation: () -> Unit, ) { - navigation( - startDestination = Route.Booth.BoothDetail::class, +// navigation( +// startDestination = Route.Booth.BoothDetail::class, +// ) { + navigation( + startDestination = BOOTH_DETAIL_ROUTE, + route = BOOTH_ROUTE, + arguments = listOf( + navArgument(BOOTH_ID) { + type = NavType.LongType + }, + ), ) { - composable { navBackStackEntry -> + // composable { navBackStackEntry -> + composable(route = BOOTH_DETAIL_ROUTE) { navBackStackEntry -> val viewModel = navBackStackEntry.sharedViewModel(navController) BoothDetailRoute( padding = padding, @@ -40,7 +58,8 @@ fun NavGraphBuilder.boothNavGraph( viewModel = viewModel, ) } - composable { navBackStackEntry -> + // composable { navBackStackEntry -> + composable(route = BOOTH_LOCATION_ROUTE) { navBackStackEntry -> val viewModel = navBackStackEntry.sharedViewModel(navController) BoothLocationRoute( onBackClick = popBackStack, diff --git a/feature/booth/src/main/kotlin/com/unifest/android/feature/booth/viewmodel/BoothViewModel.kt b/feature/booth/src/main/kotlin/com/unifest/android/feature/booth/viewmodel/BoothViewModel.kt index a00c6c22..e14b33fb 100644 --- a/feature/booth/src/main/kotlin/com/unifest/android/feature/booth/viewmodel/BoothViewModel.kt +++ b/feature/booth/src/main/kotlin/com/unifest/android/feature/booth/viewmodel/BoothViewModel.kt @@ -12,6 +12,7 @@ import com.unifest.android.core.data.repository.LikedFestivalRepository import com.unifest.android.core.data.repository.WaitingRepository import com.unifest.android.core.model.MenuModel import com.unifest.android.feature.booth.R +import com.unifest.android.feature.booth.navigation.BOOTH_ID import com.unifest.android.core.designsystem.R as designR import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toImmutableList @@ -34,9 +35,9 @@ class BoothViewModel @Inject constructor( private val waitingRepository: WaitingRepository, savedStateHandle: SavedStateHandle, ) : ViewModel(), ErrorHandlerActions { - companion object { - private const val BOOTH_ID = "boothId" - } +// companion object { +// private const val BOOTH_ID = "boothId" +// } private val boothId: Long = requireNotNull(savedStateHandle.get(BOOTH_ID)) { "boothId is required." } diff --git a/feature/home/src/main/kotlin/com/unifest/android/feature/home/navigation/HomeNavigation.kt b/feature/home/src/main/kotlin/com/unifest/android/feature/home/navigation/HomeNavigation.kt index 2e85ed0d..1d0ae3d7 100644 --- a/feature/home/src/main/kotlin/com/unifest/android/feature/home/navigation/HomeNavigation.kt +++ b/feature/home/src/main/kotlin/com/unifest/android/feature/home/navigation/HomeNavigation.kt @@ -6,11 +6,13 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.unifest.android.core.common.UiText -import com.unifest.android.core.navigation.MainTabRoute import com.unifest.android.feature.home.HomeRoute +const val HOME_ROUTE = "home_route" + fun NavController.navigateToHome(navOptions: NavOptions) { - navigate(MainTabRoute.Home, navOptions) + // navigate(MainTabRoute.Home, navOptions) + navigate(HOME_ROUTE, navOptions) } fun NavGraphBuilder.homeNavGraph( @@ -18,7 +20,8 @@ fun NavGraphBuilder.homeNavGraph( popBackStack: () -> Unit, onShowSnackBar: (UiText) -> Unit, ) { - composable { + // composable { + composable(route = HOME_ROUTE) { HomeRoute( padding = padding, popBackStack = popBackStack, diff --git a/feature/intro/src/main/kotlin/com/unifest/android/feature/intro/component/AllFestivalsRow.kt b/feature/intro/src/main/kotlin/com/unifest/android/feature/intro/component/AllFestivalsRow.kt index 20b9fdc2..46065b74 100644 --- a/feature/intro/src/main/kotlin/com/unifest/android/feature/intro/component/AllFestivalsRow.kt +++ b/feature/intro/src/main/kotlin/com/unifest/android/feature/intro/component/AllFestivalsRow.kt @@ -2,6 +2,7 @@ package com.unifest.android.feature.intro.component import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -45,6 +46,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.launch +@OptIn(ExperimentalFoundationApi::class) @Composable fun AllFestivalsTabRow( festivals: ImmutableList, diff --git a/feature/intro/src/main/kotlin/com/unifest/android/feature/intro/di/IntroNavigator.kt b/feature/intro/src/main/kotlin/com/unifest/android/feature/intro/di/IntroNavigatorModule.kt similarity index 100% rename from feature/intro/src/main/kotlin/com/unifest/android/feature/intro/di/IntroNavigator.kt rename to feature/intro/src/main/kotlin/com/unifest/android/feature/intro/di/IntroNavigatorModule.kt diff --git a/feature/liked-booth/src/main/kotlin/com/unifest/android/feature/liked_booth/navigation/LikedBoothNavigation.kt b/feature/liked-booth/src/main/kotlin/com/unifest/android/feature/liked_booth/navigation/LikedBoothNavigation.kt index 5bfcba25..87fd9dd2 100644 --- a/feature/liked-booth/src/main/kotlin/com/unifest/android/feature/liked_booth/navigation/LikedBoothNavigation.kt +++ b/feature/liked-booth/src/main/kotlin/com/unifest/android/feature/liked_booth/navigation/LikedBoothNavigation.kt @@ -5,11 +5,13 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.unifest.android.core.common.UiText -import com.unifest.android.core.navigation.Route import com.unifest.android.feature.liked_booth.LikedBoothRoute +const val LIKE_BOOTH_ROUTE = "like_booth_route" + fun NavController.navigateToLikedBooth() { - navigate(Route.LikeBooth) + // navigate(Route.LikeBooth) + navigate(LIKE_BOOTH_ROUTE) } fun NavGraphBuilder.likedBoothNavGraph( @@ -18,7 +20,8 @@ fun NavGraphBuilder.likedBoothNavGraph( navigateToBoothDetail: (Long) -> Unit, onShowSnackBar: (UiText) -> Unit, ) { - composable { + // composable { + composable(route = LIKE_BOOTH_ROUTE) { LikedBoothRoute( padding = padding, popBackStack = popBackStack, diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts index 429c1e6b..22e3b382 100644 --- a/feature/main/build.gradle.kts +++ b/feature/main/build.gradle.kts @@ -27,7 +27,6 @@ dependencies { projects.feature.festival, projects.feature.home, projects.feature.likedBooth, - projects.feature.intro, projects.feature.map, projects.feature.menu, projects.feature.navigator, diff --git a/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainActivity.kt b/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainActivity.kt index 1644a71c..a1646f78 100644 --- a/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainActivity.kt +++ b/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainActivity.kt @@ -11,17 +11,12 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.graphics.Color import com.unifest.android.core.designsystem.theme.DarkGrey100 import com.unifest.android.core.designsystem.theme.UnifestTheme -import com.unifest.android.feature.navigator.IntroNavigator import dagger.hilt.android.AndroidEntryPoint import tech.thdev.compose.exteions.system.ui.controller.rememberExSystemUiController import timber.log.Timber -import javax.inject.Inject @AndroidEntryPoint class MainActivity : ComponentActivity() { - @Inject - lateinit var introNavigator: IntroNavigator - private val viewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { @@ -53,15 +48,19 @@ class MainActivity : ComponentActivity() { override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) + setIntent(intent) + handleIntent(intent) + } - if (intent.getBooleanExtra("navigate_to_waiting", false)) { + private fun handleIntent(intent: Intent) { + if (intent.getStringExtra("waitingId") != null) { val waitingId = intent.getStringExtra("waitingId") Timber.d("navigate_to_waiting -> waitingId: $waitingId") if (waitingId != null) { viewModel.setWaitingId(waitingId.toLong()) } - } else if (intent.getBooleanExtra("navigate_to_booth", false)) { + } else if (intent.getStringExtra("boothId") != null) { val boothId = intent.getStringExtra("boothId") Timber.d("navigate_to_booth -> boothId: $boothId") if (boothId != null) { diff --git a/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainNavController.kt b/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainNavController.kt index 1e3178f8..1f794c8c 100644 --- a/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainNavController.kt +++ b/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainNavController.kt @@ -3,17 +3,15 @@ package com.unifest.android.feature.main import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.navigation.NavDestination -import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController import androidx.navigation.NavOptions import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.navOptions -import com.unifest.android.core.navigation.MainTabRoute -import com.unifest.android.core.navigation.Route import com.unifest.android.feature.booth.navigation.navigateToBoothDetail import com.unifest.android.feature.booth.navigation.navigateToBoothLocation +import com.unifest.android.feature.home.navigation.HOME_ROUTE import com.unifest.android.feature.home.navigation.navigateToHome import com.unifest.android.feature.liked_booth.navigation.navigateToLikedBooth import com.unifest.android.feature.map.navigation.navigateToMap @@ -29,10 +27,14 @@ internal class MainNavController( val startDestination = MainTab.MAP.route + // val currentTab: MainTab? +// @Composable get() = MainTab.find { tab -> +// currentDestination?.hasRoute(tab::class) == true +// } val currentTab: MainTab? - @Composable get() = MainTab.find { tab -> - currentDestination?.hasRoute(tab::class) == true - } + @Composable get() = currentDestination + ?.route + ?.let(MainTab.Companion::find) fun navigate(tab: MainTab) { val navOptions = navOptions { @@ -68,9 +70,15 @@ internal class MainNavController( navController.popBackStack() } +// // https://github.com/droidknights/DroidKnights2023_App/pull/243/commits/4bfb6d13908eaaab38ab3a59747d628efa3893cb +// fun popBackStackIfNotMap() { +// if (!isSameCurrentDestination()) { +// popBackStack() +// } +// } // https://github.com/droidknights/DroidKnights2023_App/pull/243/commits/4bfb6d13908eaaab38ab3a59747d628efa3893cb fun popBackStackIfNotMap() { - if (!isSameCurrentDestination()) { + if (!isSameCurrentDestination(HOME_ROUTE)) { popBackStack() } } @@ -82,13 +90,21 @@ internal class MainNavController( navController.navigate(startDestination, options) } - private inline fun isSameCurrentDestination(): Boolean { - return navController.currentDestination?.hasRoute() == true - } +// private inline fun isSameCurrentDestination(): Boolean { +// return navController.currentDestination?.hasRoute() == true +// } + private fun isSameCurrentDestination(route: String) = + navController.currentDestination?.route == route + +// @Composable +// fun shouldShowBottomBar() = MainTab.contains { +// currentDestination?.hasRoute(it::class) == true +// } @Composable - fun shouldShowBottomBar() = MainTab.contains { - currentDestination?.hasRoute(it::class) == true + fun shouldShowBottomBar(): Boolean { + val currentRoute = currentDestination?.route ?: return false + return currentRoute in MainTab } } diff --git a/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainTab.kt b/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainTab.kt index d772aeec..50c239f7 100644 --- a/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainTab.kt +++ b/feature/main/src/main/kotlin/com/unifest/android/feature/main/MainTab.kt @@ -1,63 +1,74 @@ package com.unifest.android.feature.main -import androidx.compose.runtime.Composable -import com.unifest.android.core.navigation.MainTabRoute -import com.unifest.android.core.navigation.Route - internal enum class MainTab( val iconResId: Int, val selectedIconResId: Int, internal val contentDescription: String, val label: String, - val route: MainTabRoute, + // val route: MainTabRoute, + val route: String, ) { HOME( iconResId = R.drawable.ic_home, selectedIconResId = R.drawable.ic_selected_home, contentDescription = "Home Icon", label = "홈", - route = MainTabRoute.Home, + // route = MainTabRoute.Home, + route = "home_route", ), MAP( iconResId = R.drawable.ic_map, selectedIconResId = R.drawable.ic_selected_map, contentDescription = "Map Icon", label = "지도", - route = MainTabRoute.Map, + // route = MainTabRoute.Map, + route = "map_route", ), WAITING( iconResId = R.drawable.ic_waiting, selectedIconResId = R.drawable.ic_selected_waiting, contentDescription = "Waiting Icon", label = "웨이팅", - route = MainTabRoute.Waiting, + // route = MainTabRoute.Waiting, + route = "waiting_route", ), - -// STAMP( + // STAMP( // iconResId = R.drawable.ic_stamp, // selectedIconResId = R.drawable.ic_selected_stamp, // contentDescription = "Stamp Icon", // label = "스탬프", // route = MainTabRoute.Stamp, +// route = "stamp_route", // ), + MENU( iconResId = R.drawable.ic_menu, selectedIconResId = R.drawable.ic_selected_menu, contentDescription = "Menu Icon", label = "메뉴", - route = MainTabRoute.Menu, + // route = MainTabRoute.Menu, + route = "stamp_route", ), ; +// companion object { +// @Composable +// fun find(predicate: @Composable (MainTabRoute) -> Boolean): MainTab? { +// return entries.find { predicate(it.route) } +// } +// +// @Composable +// fun contains(predicate: @Composable (Route) -> Boolean): Boolean { +// return entries.map { it.route }.any { predicate(it) } +// } +// } companion object { - @Composable - fun find(predicate: @Composable (MainTabRoute) -> Boolean): MainTab? { - return entries.find { predicate(it.route) } + operator fun contains(route: String): Boolean { + return entries.map { it.route }.contains(route) } - @Composable - fun contains(predicate: @Composable (Route) -> Boolean): Boolean { - return entries.map { it.route }.any { predicate(it) } + fun find(route: String): MainTab? { + return entries.find { it.route == route } } } } diff --git a/feature/map/src/main/kotlin/com/unifest/android/feature/map/MapScreen.kt b/feature/map/src/main/kotlin/com/unifest/android/feature/map/MapScreen.kt index 53b34bba..03a6b429 100644 --- a/feature/map/src/main/kotlin/com/unifest/android/feature/map/MapScreen.kt +++ b/feature/map/src/main/kotlin/com/unifest/android/feature/map/MapScreen.kt @@ -12,6 +12,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -68,11 +69,13 @@ import com.naver.maps.map.compose.ExperimentalNaverMapApi import com.naver.maps.map.compose.LocationTrackingMode import com.naver.maps.map.compose.MapProperties import com.naver.maps.map.compose.MapUiSettings +import com.naver.maps.map.compose.MarkerState import com.naver.maps.map.compose.NaverMap import com.naver.maps.map.compose.PolygonOverlay import com.naver.maps.map.compose.rememberCameraPositionState import com.naver.maps.map.compose.rememberFusedLocationSource import com.naver.maps.map.overlay.Align +import com.naver.maps.map.compose.Marker as ComposeMarker import com.naver.maps.map.overlay.Overlay import com.naver.maps.map.overlay.OverlayImage import com.unifest.android.core.common.ObserveAsEvents @@ -129,6 +132,7 @@ internal fun MapRoute( ) { val mapUiState by mapViewModel.uiState.collectAsStateWithLifecycle() val festivalUiState by festivalViewModel.uiState.collectAsStateWithLifecycle() + val isClusteringEnabled by mapViewModel.isClusteringEnabled.collectAsStateWithLifecycle(true) val context = LocalContext.current val activity = context.findActivity() val dialogQueue = mapViewModel.permissionDialogQueue @@ -291,6 +295,7 @@ internal fun MapRoute( festivalUiState = festivalUiState, onMapUiAction = mapViewModel::onMapUiAction, onFestivalUiAction = festivalViewModel::onFestivalUiAction, + isClusteringEnabled = isClusteringEnabled, ) } @@ -299,6 +304,7 @@ private fun checkLocationPermission(activity: Activity): Boolean { activity.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED } +@OptIn(ExperimentalFoundationApi::class) @Composable internal fun MapScreen( padding: PaddingValues, @@ -306,6 +312,7 @@ internal fun MapScreen( festivalUiState: FestivalUiState, onMapUiAction: (MapUiAction) -> Unit, onFestivalUiAction: (FestivalUiAction) -> Unit, + isClusteringEnabled: Boolean, ) { val cameraPositionState = rememberCameraPositionState { position = CameraPosition(LatLng(37.0122749, 127.2635972), 15.8) @@ -327,6 +334,7 @@ internal fun MapScreen( pagerState = pagerState, onMapUiAction = onMapUiAction, onFestivalUiAction = onFestivalUiAction, + isClusteringEnabled = isClusteringEnabled, ) if (mapUiState.isServerErrorDialogVisible) { @@ -356,7 +364,7 @@ internal fun MapScreen( } } -@OptIn(ExperimentalNaverMapApi::class) +@OptIn(ExperimentalNaverMapApi::class, ExperimentalFoundationApi::class) @Composable fun MapContent( uiState: MapUiState, @@ -365,8 +373,10 @@ fun MapContent( pagerState: PagerState, onMapUiAction: (MapUiAction) -> Unit, onFestivalUiAction: (FestivalUiAction) -> Unit, + isClusteringEnabled: Boolean, ) { - // val context = LocalContext.current + val context = LocalContext.current + Box { NaverMap( cameraPositionState = cameraPositionState, @@ -388,113 +398,116 @@ fun MapContent( outlineWidth = 1.dp, holes = persistentListOf(uiState.innerHole), ) -// uiState.filteredBoothList.forEach { booth -> -// Marker( -// state = MarkerState(position = booth.position), -// icon = MarkerCategory.fromString(booth.category).getMarkerIcon(booth.isSelected), -// onClick = { -// onMapUiAction(MapUiAction.OnBoothMarkerClick(booth)) -// true -// }, -// ) -// } - var clusterManager by remember { mutableStateOf?>(null) } - DisposableMapEffect(uiState.filteredBoothList) { map -> - if (clusterManager == null) { - clusterManager = Clusterer.ComplexBuilder() - .minClusteringZoom(9) - .maxClusteringZoom(16) - .maxScreenDistance(200.0) - .thresholdStrategy { zoom -> - if (zoom <= 11) { - 0.0 - } else { - 70.0 + if (isClusteringEnabled) { + var clusterManager by remember { mutableStateOf?>(null) } + DisposableMapEffect(uiState.filteredBoothList) { map -> + if (clusterManager == null) { + clusterManager = Clusterer.ComplexBuilder() + .minClusteringZoom(9) + .maxClusteringZoom(16) + .maxScreenDistance(200.0) + .thresholdStrategy { zoom -> + if (zoom <= 11) { + 0.0 + } else { + 70.0 + } } - } - .distanceStrategy(object : DistanceStrategy { - private val defaultDistanceStrategy = DefaultDistanceStrategy() + .distanceStrategy(object : DistanceStrategy { + private val defaultDistanceStrategy = DefaultDistanceStrategy() - override fun getDistance(zoom: Int, node1: Node, node2: Node): Double { - return if (zoom <= 9) { - -1.0 - } else if ((node1.tag as ItemData).category == (node2.tag as ItemData).category) { - if (zoom <= 11) { + override fun getDistance(zoom: Int, node1: Node, node2: Node): Double { + return if (zoom <= 9) { -1.0 + } else if ((node1.tag as ItemData).category == (node2.tag as ItemData).category) { + if (zoom <= 11) { + -1.0 + } else { + defaultDistanceStrategy.getDistance(zoom, node1, node2) + } } else { - defaultDistanceStrategy.getDistance(zoom, node1, node2) + 10000.0 } + } + }) + .tagMergeStrategy { cluster -> + if (cluster.maxZoom <= 9) { + null } else { - 10000.0 + ItemData("", (cluster.children.first().tag as ItemData).category) } } - }) - .tagMergeStrategy { cluster -> - if (cluster.maxZoom <= 9) { - null - } else { - ItemData("", (cluster.children.first().tag as ItemData).category) - } - } - .markerManager(object : DefaultMarkerManager() { - override fun createMarker() = super.createMarker().apply { - subCaptionTextSize = 10f - subCaptionColor = android.graphics.Color.WHITE - subCaptionHaloColor = android.graphics.Color.TRANSPARENT + .markerManager(object : DefaultMarkerManager() { + override fun createMarker() = super.createMarker().apply { + subCaptionTextSize = 10f + subCaptionColor = android.graphics.Color.WHITE + subCaptionHaloColor = android.graphics.Color.TRANSPARENT + } + }) + .clusterMarkerUpdater { info, marker -> + val size = info.size + marker.icon = OverlayImage.fromResource(designR.drawable.ic_cluster) + marker.captionText = size.toString() + marker.setCaptionAligns(Align.Center) + marker.captionColor = android.graphics.Color.WHITE + marker.captionHaloColor = android.graphics.Color.TRANSPARENT + marker.onClickListener = DefaultClusterOnClickListener(info) } - }) - .clusterMarkerUpdater { info, marker -> - val size = info.size - marker.icon = OverlayImage.fromResource(designR.drawable.ic_cluster_icon) - marker.captionText = size.toString() - marker.setCaptionAligns(Align.Center) - marker.captionColor = android.graphics.Color.WHITE - marker.captionHaloColor = android.graphics.Color.TRANSPARENT - marker.onClickListener = DefaultClusterOnClickListener(info) - } - .leafMarkerUpdater { info, marker -> - marker.icon = MarkerCategory.fromString((info.key as BoothMapModel).category) - .getMarkerIcon((info.key as BoothMapModel).isSelected) - marker.onClickListener = Overlay.OnClickListener { - onMapUiAction(MapUiAction.OnBoothMarkerClick(listOf(info.key as BoothMapModel))) - true + .leafMarkerUpdater { info, marker -> + marker.icon = MarkerCategory.fromString((info.key as BoothMapModel).category) + .getMarkerIcon((info.key as BoothMapModel).isSelected) + marker.onClickListener = Overlay.OnClickListener { + onMapUiAction(MapUiAction.OnBoothMarkerClick(listOf(info.key as BoothMapModel))) + true + } } - } - .build() - .apply { this.map = map } - } - val boothListMap = uiState.filteredBoothList.associateWith { booth -> ItemData(booth.name, booth.category) } - clusterManager?.addAll(boothListMap) - onDispose { - clusterManager?.clear() + .build() + .apply { this.map = map } + } + val boothListMap = uiState.filteredBoothList.associateWith { booth -> ItemData(booth.name, booth.category) } + clusterManager?.addAll(boothListMap) + onDispose { + clusterManager?.clear() + } } - } -// var clusterManager by remember { mutableStateOf?>(null) } -// DisposableMapEffect(uiState.filteredBoothsList) { map -> -// if (clusterManager == null) { -// clusterManager = TedNaverClustering.with(context, map) -// .customMarker { -// Marker().apply { -// icon = MarkerCategory.fromString(it.category).getMarkerIcon(it.isSelected) +// var clusterManager by remember { mutableStateOf?>(null) } +// DisposableMapEffect(uiState.filteredBoothList) { map -> +// if (clusterManager == null) { +// clusterManager = TedNaverClustering.with(context, map) +// .clusterClickListener { booths -> +// onMapUiAction(MapUiAction.OnBoothMarkerClick(booths.items.toList())) // } -// } -// .markerClickListener { booth -> -// onMapUiAction(MapUiAction.OnBoothMarkerClick(listOf(booth))) -// } -// .clusterClickListener { booths -> -// onMapUiAction(MapUiAction.OnBoothMarkerClick(booths.items.toList())) -// } -// // 마커를 클릭 했을 경우 마커의 위치로 카메라 이동 비활성화 -// .clickToCenter(false) -// .make() -// } -// clusterManager?.addItems(uiState.filteredBoothsList) -// onDispose { -// clusterManager?.clearItems() +// .customMarker { +// Marker().apply { +// icon = MarkerCategory.fromString(it.category).getMarkerIcon(it.isSelected) +// } +// } +// .markerClickListener { booth -> +// onMapUiAction(MapUiAction.OnBoothMarkerClick(listOf(booth))) +// } +// // 마커를 클릭 했을 경우 마커의 위치로 카메라 이동 비활성화 +// .clickToCenter(false) +// .make() +// } +// clusterManager?.addItems(uiState.filteredBoothList) +// onDispose { +// clusterManager?.clearItems() +// } // } -// } + } else { + uiState.filteredBoothList.forEach { booth -> + ComposeMarker( + state = MarkerState(position = LatLng(booth.latitude, booth.longitude)), + icon = MarkerCategory.fromString(booth.category).getMarkerIcon(booth.isSelected), + onClick = { + onMapUiAction(MapUiAction.OnSingleBoothMarkerClick(booth)) + true + }, + ) + } + } } MapTopAppBar( title = uiState.festivalInfo.schoolName, @@ -583,6 +596,7 @@ private fun MapScreenPreview( festivalUiState = FestivalUiState(), onMapUiAction = {}, onFestivalUiAction = {}, + isClusteringEnabled = true, ) } } diff --git a/feature/map/src/main/kotlin/com/unifest/android/feature/map/navigation/MapNavigation.kt b/feature/map/src/main/kotlin/com/unifest/android/feature/map/navigation/MapNavigation.kt index 1bc7e579..6fb921e9 100644 --- a/feature/map/src/main/kotlin/com/unifest/android/feature/map/navigation/MapNavigation.kt +++ b/feature/map/src/main/kotlin/com/unifest/android/feature/map/navigation/MapNavigation.kt @@ -6,11 +6,13 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.unifest.android.core.common.UiText -import com.unifest.android.core.navigation.MainTabRoute import com.unifest.android.feature.map.MapRoute +const val MAP_ROUTE = "map_route" + fun NavController.navigateToMap(navOptions: NavOptions) { - navigate(MainTabRoute.Map, navOptions) + // navigate(MainTabRoute.Map, navOptions) + navigate(MAP_ROUTE, navOptions) } fun NavGraphBuilder.mapNavGraph( @@ -18,7 +20,8 @@ fun NavGraphBuilder.mapNavGraph( navigateToBoothDetail: (Long) -> Unit, onShowSnackBar: (UiText) -> Unit, ) { - composable { + // composable { + composable(route = MAP_ROUTE) { MapRoute( padding = padding, navigateToBoothDetail = navigateToBoothDetail, diff --git a/feature/map/src/main/kotlin/com/unifest/android/feature/map/viewmodel/MapUiAction.kt b/feature/map/src/main/kotlin/com/unifest/android/feature/map/viewmodel/MapUiAction.kt index 4164e6e2..57b0ad1a 100644 --- a/feature/map/src/main/kotlin/com/unifest/android/feature/map/viewmodel/MapUiAction.kt +++ b/feature/map/src/main/kotlin/com/unifest/android/feature/map/viewmodel/MapUiAction.kt @@ -11,7 +11,7 @@ sealed interface MapUiAction { data class OnSearch(val searchText: TextFieldValue) : MapUiAction data class OnBoothMarkerClick(val booths: List) : MapUiAction - // data class OnBoothMarkerClick(val booth: BoothMapModel) : MapUiAction + data class OnSingleBoothMarkerClick(val booth: BoothMapModel) : MapUiAction data object OnTogglePopularBooth : MapUiAction data class OnBoothItemClick(val boothId: Long) : MapUiAction data class OnRetryClick(val error: ErrorType) : MapUiAction diff --git a/feature/map/src/main/kotlin/com/unifest/android/feature/map/viewmodel/MapViewModel.kt b/feature/map/src/main/kotlin/com/unifest/android/feature/map/viewmodel/MapViewModel.kt index 07106d33..af1d37bf 100644 --- a/feature/map/src/main/kotlin/com/unifest/android/feature/map/viewmodel/MapViewModel.kt +++ b/feature/map/src/main/kotlin/com/unifest/android/feature/map/viewmodel/MapViewModel.kt @@ -12,6 +12,7 @@ import com.unifest.android.core.data.repository.BoothRepository import com.unifest.android.core.data.repository.FestivalRepository import com.unifest.android.core.data.repository.LikedFestivalRepository import com.unifest.android.core.data.repository.OnboardingRepository +import com.unifest.android.core.data.repository.SettingRepository import com.unifest.android.core.model.FestivalModel import com.unifest.android.feature.map.R import com.unifest.android.feature.map.mapper.toMapModel @@ -36,6 +37,7 @@ class MapViewModel @Inject constructor( private val festivalRepository: FestivalRepository, private val boothRepository: BoothRepository, private val likedFestivalRepository: LikedFestivalRepository, + private val settingRepository: SettingRepository, ) : ViewModel(), ErrorHandlerActions { private val _uiState = MutableStateFlow(MapUiState()) val uiState: StateFlow = _uiState.asStateFlow() @@ -43,16 +45,14 @@ class MapViewModel @Inject constructor( private val _uiEvent = Channel() val uiEvent: Flow = _uiEvent.receiveAsFlow() + val isClusteringEnabled = settingRepository.flowIsClusteringEnabled() + val permissionDialogQueue = mutableStateListOf() fun onPermissionResult( permission: String, isGranted: Boolean, ) { -// if (isGranted && permissionDialogQueue.isEmpty()) { -// refreshFCMToken() -// } - if (!isGranted && !permissionDialogQueue.contains(permission)) { permissionDialogQueue.add(permission) } @@ -78,7 +78,7 @@ class MapViewModel @Inject constructor( is MapUiAction.OnSearch -> searchBooth() is MapUiAction.OnTooltipClick -> completeMapOnboarding() is MapUiAction.OnBoothMarkerClick -> updateSelectedBoothList(action.booths) - // is MapUiAction.OnBoothMarkerClick -> updateSelectedBooth(action.booth) + is MapUiAction.OnSingleBoothMarkerClick -> updateSelectedSingleBooth(action.booth) is MapUiAction.OnTogglePopularBooth -> setEnablePopularMode() is MapUiAction.OnBoothItemClick -> navigateToBoothDetail(action.boothId) is MapUiAction.OnRetryClick -> refresh(action.error) @@ -314,40 +314,40 @@ class MapViewModel @Inject constructor( } } -// private fun updateSelectedBooth(booth: BoothMapModel) { -// if (_uiState.value.isPopularMode) { -// viewModelScope.launch { -// _uiState.update { -// it.copy(isPopularMode = false) -// } -// delay(500) -// _uiState.update { -// it.copy( -// isBoothSelectionMode = true, -// selectedBoothList = listOf(booth).toImmutableList(), -// ) -// } -// } -// } else { -// _uiState.update { -// it.copy( -// isBoothSelectionMode = true, -// selectedBoothList = listOf(booth).toImmutableList(), -// ) -// } -// } -// _uiState.update { -// it.copy( -// filteredBoothList = it.filteredBoothList.map { boothMapModel -> -// if (boothMapModel.id == booth.id) { -// boothMapModel.copy(isSelected = true) -// } else { -// boothMapModel.copy(isSelected = false) -// } -// }.toImmutableList(), -// ) -// } -// } + private fun updateSelectedSingleBooth(booth: BoothMapModel) { + if (_uiState.value.isPopularMode) { + viewModelScope.launch { + _uiState.update { + it.copy(isPopularMode = false) + } + delay(500) + _uiState.update { + it.copy( + isBoothSelectionMode = true, + selectedBoothList = listOf(booth).toImmutableList(), + ) + } + } + } else { + _uiState.update { + it.copy( + isBoothSelectionMode = true, + selectedBoothList = listOf(booth).toImmutableList(), + ) + } + } + _uiState.update { + it.copy( + filteredBoothList = it.filteredBoothList.map { boothMapModel -> + if (boothMapModel.id == booth.id) { + boothMapModel.copy(isSelected = true) + } else { + boothMapModel.copy(isSelected = false) + } + }.toImmutableList(), + ) + } + } private fun updateSelectedBoothList(booths: List) { Timber.d("booths.size: ${booths.size} updateSelectedBoothList: $booths") diff --git a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/MenuScreen.kt b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/MenuScreen.kt index a44c94b9..50b3f0b1 100644 --- a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/MenuScreen.kt +++ b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/MenuScreen.kt @@ -4,6 +4,7 @@ import android.content.pm.PackageManager import android.widget.Toast import androidx.compose.animation.core.LinearOutSlowInEasing import androidx.compose.animation.core.tween +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -83,6 +84,7 @@ internal fun MenuRoute( ) { val menuUiState by menuViewModel.uiState.collectAsStateWithLifecycle() val festivalUiState by festivalViewModel.uiState.collectAsStateWithLifecycle() + val isClusteringEnabled by menuViewModel.isClusteringEnabled.collectAsStateWithLifecycle(true) val context = LocalContext.current val uriHandler = LocalUriHandler.current val appVersion = remember { @@ -125,9 +127,11 @@ internal fun MenuRoute( appVersion = appVersion, onMenuUiAction = menuViewModel::onMenuUiAction, onFestivalUiAction = festivalViewModel::onFestivalUiAction, + isClusteringEnabled = isClusteringEnabled, ) } +@OptIn(ExperimentalFoundationApi::class) @Composable fun MenuScreen( padding: PaddingValues, @@ -136,6 +140,7 @@ fun MenuScreen( appVersion: String, onMenuUiAction: (MenuUiAction) -> Unit, onFestivalUiAction: (FestivalUiAction) -> Unit, + isClusteringEnabled: Boolean, ) { Box( modifier = Modifier @@ -260,10 +265,16 @@ fun MenuScreen( index = index, totalCount = menuUiState.likedBooths.size, deleteLikedBooth = { onMenuUiAction(MenuUiAction.OnToggleBookmark(booth)) }, - modifier = Modifier.animateItem( - fadeInSpec = null, - fadeOutSpec = null, - placementSpec = tween( +// modifier = Modifier.animateItem( +// fadeInSpec = null, +// fadeOutSpec = null, +// placementSpec = tween( +// durationMillis = 500, +// easing = LinearOutSlowInEasing, +// ), +// ), + modifier = Modifier.animateItemPlacement( + animationSpec = tween( durationMillis = 500, easing = LinearOutSlowInEasing, ), @@ -277,6 +288,24 @@ fun MenuScreen( color = MaterialTheme.colorScheme.outline, ) } + item { + MenuItem( + icon = ImageVector.vectorResource(R.drawable.ic_clustering), + title = stringResource(R.string.clustering), + onClick = {}, + isToggleMenuItem = true, + checked = isClusteringEnabled, + onCheckedChange = { isChecked -> + onMenuUiAction(MenuUiAction.OnToggleClustering(isChecked)) + }, + ) + } + item { + HorizontalDivider( + thickness = 1.dp, + color = MaterialTheme.colorScheme.outline, + ) + } item { MenuItem( icon = ImageVector.vectorResource(R.drawable.ic_inquiry), @@ -362,6 +391,7 @@ fun MenuScreenPreview( appVersion = "1.0.0", onMenuUiAction = {}, onFestivalUiAction = {}, + isClusteringEnabled = true, ) } } diff --git a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/component/MenuItem.kt b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/component/MenuItem.kt index be8fb8e3..8c6e2833 100644 --- a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/component/MenuItem.kt +++ b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/component/MenuItem.kt @@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Switch +import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -23,6 +25,9 @@ fun MenuItem( icon: ImageVector, title: String, onClick: () -> Unit, + isToggleMenuItem: Boolean = false, + checked: Boolean = true, + onCheckedChange: (Boolean) -> Unit = {}, ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -43,5 +48,16 @@ fun MenuItem( color = MaterialTheme.colorScheme.onBackground, style = Content8, ) + if (isToggleMenuItem) { + Spacer(modifier = Modifier.weight(1f)) + Switch( + checked = checked, + onCheckedChange = onCheckedChange, + colors = SwitchDefaults.colors( + checkedThumbColor = MaterialTheme.colorScheme.primary, + checkedTrackColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f), + ), + ) + } } } diff --git a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/navigation/MenuNavigation.kt b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/navigation/MenuNavigation.kt index d3195c47..7edcb50a 100644 --- a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/navigation/MenuNavigation.kt +++ b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/navigation/MenuNavigation.kt @@ -6,11 +6,13 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.unifest.android.core.common.UiText -import com.unifest.android.core.navigation.MainTabRoute import com.unifest.android.feature.menu.MenuRoute +const val MENU_ROUTE = "menu_route" + fun NavController.navigateToMenu(navOptions: NavOptions) { - navigate(MainTabRoute.Menu, navOptions) + // navigate(MainTabRoute.Menu, navOptions) + navigate(MENU_ROUTE, navOptions) } fun NavGraphBuilder.menuNavGraph( @@ -20,7 +22,8 @@ fun NavGraphBuilder.menuNavGraph( navigateToBoothDetail: (Long) -> Unit, onShowSnackBar: (UiText) -> Unit, ) { - composable { + // composable { + composable(route = MENU_ROUTE) { MenuRoute( padding = padding, popBackStack = popBackStack, diff --git a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuUiAction.kt b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuUiAction.kt index 284cc4e0..84f577bc 100644 --- a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuUiAction.kt +++ b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuUiAction.kt @@ -10,6 +10,7 @@ sealed interface MenuUiAction { data object OnContactClick : MenuUiAction data object OnAdministratorModeClick : MenuUiAction data class OnRetryClick(val error: ErrorType) : MenuUiAction + data class OnToggleClustering(val isChecked: Boolean) : MenuUiAction } enum class ErrorType { diff --git a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuUiState.kt b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuUiState.kt index 95265723..2b5b006d 100644 --- a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuUiState.kt +++ b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuUiState.kt @@ -11,4 +11,5 @@ data class MenuUiState( val likedFestivals: ImmutableList = persistentListOf(), val isNetworkErrorDialogVisible: Boolean = false, val isServerErrorDialogVisible: Boolean = false, + val isClusteringEnabled: Boolean = true, ) diff --git a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuViewModel.kt b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuViewModel.kt index 4a3a59ea..fd3d076f 100644 --- a/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuViewModel.kt +++ b/feature/menu/src/main/kotlin/com/unifest/android/feature/menu/viewmodel/MenuViewModel.kt @@ -9,6 +9,7 @@ import com.unifest.android.core.data.repository.BoothRepository import com.unifest.android.core.data.repository.FestivalRepository import com.unifest.android.core.data.repository.LikedBoothRepository import com.unifest.android.core.data.repository.LikedFestivalRepository +import com.unifest.android.core.data.repository.SettingRepository import com.unifest.android.core.designsystem.R as designR import com.unifest.android.core.model.LikedBoothModel import dagger.hilt.android.lifecycle.HiltViewModel @@ -31,6 +32,7 @@ class MenuViewModel @Inject constructor( private val likedFestivalRepository: LikedFestivalRepository, private val boothRepository: BoothRepository, private val likedBoothRepository: LikedBoothRepository, + private val settingRepository: SettingRepository, ) : ViewModel(), ErrorHandlerActions { private val _uiState = MutableStateFlow(MenuUiState()) val uiState: StateFlow = _uiState.asStateFlow() @@ -38,6 +40,8 @@ class MenuViewModel @Inject constructor( private val _uiEvent = Channel() val uiEvent: Flow = _uiEvent.receiveAsFlow() + val isClusteringEnabled = settingRepository.flowIsClusteringEnabled() + init { observeLikedFestivals() // observeLikedBooth() @@ -53,6 +57,7 @@ class MenuViewModel @Inject constructor( is MenuUiAction.OnContactClick -> navigateToContact() is MenuUiAction.OnAdministratorModeClick -> navigateToAdministratorMode() is MenuUiAction.OnRetryClick -> refresh(action.error) + is MenuUiAction.OnToggleClustering -> updateIsClusteringEnabled(action.isChecked) } } @@ -109,6 +114,12 @@ class MenuViewModel @Inject constructor( // } // } + private fun updateIsClusteringEnabled(checked: Boolean) { + viewModelScope.launch { + settingRepository.updateIsClusteringEnabled(checked) + } + } + private fun navigateToLikedBooth() { viewModelScope.launch { _uiEvent.send(MenuUiEvent.NavigateToLikedBooth) diff --git a/feature/menu/src/main/res/drawable-night/ic_clustering.xml b/feature/menu/src/main/res/drawable-night/ic_clustering.xml new file mode 100644 index 00000000..c641c443 --- /dev/null +++ b/feature/menu/src/main/res/drawable-night/ic_clustering.xml @@ -0,0 +1,14 @@ + + + + diff --git a/feature/menu/src/main/res/drawable/ic_clustering.xml b/feature/menu/src/main/res/drawable/ic_clustering.xml new file mode 100644 index 00000000..7b5a2c71 --- /dev/null +++ b/feature/menu/src/main/res/drawable/ic_clustering.xml @@ -0,0 +1,14 @@ + + + + diff --git a/feature/menu/src/main/res/values/strings.xml b/feature/menu/src/main/res/values/strings.xml index 474164c7..4651c3f3 100644 --- a/feature/menu/src/main/res/values/strings.xml +++ b/feature/menu/src/main/res/values/strings.xml @@ -7,5 +7,6 @@ 더보기 > 이용 문의 운영자 모드 진입 + 지도 위에 부스 묶어서 보기 diff --git a/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/SplashActivity.kt b/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/SplashActivity.kt index 2be20bcf..7814dc1d 100644 --- a/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/SplashActivity.kt +++ b/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/SplashActivity.kt @@ -10,7 +10,6 @@ import androidx.compose.ui.graphics.Color import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import com.unifest.android.core.designsystem.theme.DarkGrey100 import com.unifest.android.core.designsystem.theme.UnifestTheme -import com.unifest.android.feature.navigator.IntroNavigator import com.unifest.android.feature.navigator.MainNavigator import dagger.hilt.android.AndroidEntryPoint import tech.thdev.compose.exteions.system.ui.controller.rememberExSystemUiController @@ -19,8 +18,8 @@ import javax.inject.Inject @SuppressLint("CustomSplashScreen") @AndroidEntryPoint class SplashActivity : ComponentActivity() { - @Inject - lateinit var introNavigator: IntroNavigator +// @Inject +// lateinit var introNavigator: IntroNavigator @Inject lateinit var mainNavigator: MainNavigator @@ -44,15 +43,15 @@ class SplashActivity : ComponentActivity() { UnifestTheme { SplashRoute( - navigateToIntro = { - introNavigator.navigateFrom( - activity = this, - withFinish = true, - ) - }, +// navigateToIntro = { +// introNavigator.navigateFrom( +// activity = this@SplashActivity, +// withFinish = true, +// ) +// }, navigateToMain = { mainNavigator.navigateFrom( - activity = this, + activity = this@SplashActivity, withFinish = true, ) }, diff --git a/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/SplashScreen.kt b/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/SplashScreen.kt index 29adcb38..a223ddce 100644 --- a/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/SplashScreen.kt +++ b/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/SplashScreen.kt @@ -26,7 +26,7 @@ import com.unifest.android.feature.splash.viewmodel.SplashViewModel @Composable internal fun SplashRoute( navigateToMain: () -> Unit, - navigateToIntro: () -> Unit, + // navigateToIntro: () -> Unit, viewModel: SplashViewModel = hiltViewModel(), ) { val shouldUpdate by viewModel.shouldUpdate.collectAsStateWithLifecycle(null) @@ -42,9 +42,9 @@ internal fun SplashRoute( ObserveAsEvents(flow = viewModel.uiEvent) { event -> when (event) { - is SplashUiEvent.NavigateToIntro -> { - navigateToIntro() - } +// is SplashUiEvent.NavigateToIntro -> { +// navigateToIntro() +// } is SplashUiEvent.NavigateToMain -> { navigateToMain() diff --git a/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/di/SplashNavigator.kt b/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/di/SplashNavigatorModule.kt similarity index 100% rename from feature/splash/src/main/kotlin/com/unifest/android/feature/splash/di/SplashNavigator.kt rename to feature/splash/src/main/kotlin/com/unifest/android/feature/splash/di/SplashNavigatorModule.kt diff --git a/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/viewmodel/SplashUiEvent.kt b/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/viewmodel/SplashUiEvent.kt index 6d291837..cf4ec6b7 100644 --- a/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/viewmodel/SplashUiEvent.kt +++ b/feature/splash/src/main/kotlin/com/unifest/android/feature/splash/viewmodel/SplashUiEvent.kt @@ -1,7 +1,7 @@ package com.unifest.android.feature.splash.viewmodel sealed interface SplashUiEvent { - data object NavigateToIntro : SplashUiEvent + // data object NavigateToIntro : SplashUiEvent data object NavigateToMain : SplashUiEvent data object NavigateToPlayStore : SplashUiEvent data object CloseApp : SplashUiEvent diff --git a/feature/stamp/src/main/kotlin/com/unifest/android/feature/stamp/navigation/StampNavigation.kt b/feature/stamp/src/main/kotlin/com/unifest/android/feature/stamp/navigation/StampNavigation.kt index a404e5bc..58a5ba0e 100644 --- a/feature/stamp/src/main/kotlin/com/unifest/android/feature/stamp/navigation/StampNavigation.kt +++ b/feature/stamp/src/main/kotlin/com/unifest/android/feature/stamp/navigation/StampNavigation.kt @@ -5,11 +5,13 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable -import com.unifest.android.core.navigation.MainTabRoute import com.unifest.android.feature.stamp.StampRoute +const val STAMP_ROUTE = "stamp_route" + fun NavController.navigateToStamp(navOptions: NavOptions) { - navigate(MainTabRoute.Stamp, navOptions) + // navigate(MainTabRoute.Stamp, navOptions) + navigate(STAMP_ROUTE, navOptions) } fun NavGraphBuilder.stampNavGraph( @@ -17,7 +19,8 @@ fun NavGraphBuilder.stampNavGraph( popBackStack: () -> Unit, navigateToBoothDetail: (Long) -> Unit, ) { - composable { + // composable { + composable(route = STAMP_ROUTE) { StampRoute( padding = padding, popBackStack = popBackStack, diff --git a/feature/waiting/src/main/kotlin/com/unifest/android/feature/waiting/navigation/WaitingNavigation.kt b/feature/waiting/src/main/kotlin/com/unifest/android/feature/waiting/navigation/WaitingNavigation.kt index 9acbb511..643ef753 100644 --- a/feature/waiting/src/main/kotlin/com/unifest/android/feature/waiting/navigation/WaitingNavigation.kt +++ b/feature/waiting/src/main/kotlin/com/unifest/android/feature/waiting/navigation/WaitingNavigation.kt @@ -5,11 +5,13 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable -import com.unifest.android.core.navigation.MainTabRoute import com.unifest.android.feature.waiting.WaitingRoute +const val WAITING_ROUTE = "waiting_route" + fun NavController.navigateToWaiting(navOptions: NavOptions) { - navigate(MainTabRoute.Waiting, navOptions) + // navigate(MainTabRoute.Waiting, navOptions) + navigate(WAITING_ROUTE, navOptions) } fun NavGraphBuilder.waitingNavGraph( @@ -17,7 +19,8 @@ fun NavGraphBuilder.waitingNavGraph( popBackStack: () -> Unit, navigateToBoothDetail: (Long) -> Unit, ) { - composable { + // composable { + composable(route = WAITING_ROUTE) { WaitingRoute( padding = padding, popBackStack = popBackStack, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f15330e0..08f76754 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,59 +3,64 @@ minSdk = "26" targetSdk = "34" compileSdk = "34" -versionCode = "17" +versionCode = "18" versionName = "1.1.0" packageName = "com.unifest.android" -android-gradle-plugin = "8.5.2" -kotlin-core = "2.0.20" +android-gradle-plugin = "8.2.2" +kotlin-core = "1.9.23" gradle-dependency-handler-extensions = "1.1.0" google-secrets = "2.0.1" -google-service = "4.4.2" +google-service = "4.4.1" kotlin-detekt = "1.23.6" kotlin-ktlint-gradle = "11.6.1" kotlin-ktlint-source = "0.50.0" -kotlinx-coroutines = "1.9.0" -kotlinx-datetime = "0.6.1" -kotlinx-serialization = "1.7.2" +kotlinx-coroutines = "1.8.0" +kotlinx-datetime = "0.5.0" +kotlinx-serialization = "1.6.3" kotlinx-serialization-converter = "1.0.0" -kotlinx-collections-immutable = "0.3.8" +kotlinx-collections-immutable = "0.3.7" -android-play-services-location = "21.3.0" +android-play-services-location = "21.0.1" androidx-core = "1.13.1" -androidx-lifecycle = "2.8.6" +androidx-lifecycle = "2.8.0-alpha02" androidx-splash = "1.0.1" -androidx-startup = "1.2.0" -androidx-navigation = "2.8.1" +androidx-startup = "1.1.1" +androidx-navigation = "2.7.7" androidx-datastore = "1.1.1" androidx-room = "2.6.1" -androidx-activity-compose = "1.9.2" -androidx-compose = "1.6.8" +androidx-activity-compose = "1.9.0" +androidx-compose-compiler = "1.5.11" +androidx-compose-bom = "2024.05.00" +# androidx-compose = "1.6.8" androidx-compose-material3 = "1.2.1" +androidx-compose-animation = "1.7.0-alpha08" androidx-hilt-navigation-compose = "1.2.0" -desugar-jdk-libs = "2.1.2" -hilt = "2.52" +desugar-jdk-libs = "2.0.4" +hilt = "2.51.1" javax-inject = "1" retrofit = "2.11.0" okhttp = "5.0.0-alpha.14" -coil-compose = "2.7.0" +coil-compose = "2.6.0" timber = "5.0.1" -firebase-bom = "33.3.0" -firebase-crashlytics = "3.0.2" -ksp = "2.0.20-1.0.25" -compose-extensions = "1.6.8" -compose-stable-marker = "1.0.5" -compose-investigator = "1.5.11-0.2.1" -landscapist = "2.3.6" -balloon = "1.6.7" -calendar-compose = "2.6.0-beta04" -flexible-bottomsheet = "0.1.5" -naver-map-compose = "1.7.1" +firebase-bom = "32.8.1" +firebase-crashlytics = "2.9.9" +firebase-config = "21.6.3" +ksp = "1.9.23-1.0.20" +compose-extensions = "1.6.5" +compose-stable-marker = "1.0.4" +# compose-investigator = "1.5.11-0.2.1" +landscapist = "2.3.3" +balloon = "1.6.4" +calendar-compose = "2.5.0" +flexible-bottomsheet = "0.1.2" + +naver-map-compose = "1.5.7" naver-map-location = "21.0.1" tedclustering-naver = "1.0.2" zxing = "4.3.0" @@ -86,13 +91,20 @@ androidx-activity-compose = { group = "androidx.activity", name = "activity-comp androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" } -androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "androidx-compose" } -androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "androidx-compose" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidx-compose-bom" } +# androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "androidx-compose" } +androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation"} +# androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "androidx-compose" } +androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended"} androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidx-compose-material3" } -androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "androidx-compose" } -androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "androidx-compose" } -androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "androidx-compose" } -androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "androidx-compose" } +# androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "androidx-compose" } +androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" } +# androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "androidx-compose" } +androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } +# androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "androidx-compose" } +androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +# androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "androidx-compose" } +androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidx-navigation" } androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidx-hilt-navigation-compose" } androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "androidx-room" } @@ -151,7 +163,6 @@ androidx-room = { id = "androidx.room", version.ref = "androidx-room" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } -compose-investigator = { id = "land.sungbin.composeinvestigator", version.ref = "compose-investigator" } # Plugins defined by this project unifest-android-application = { id = "unifest.android.application", version = "unspecified" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9355b415..a3c822d2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,8 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +android.enableJetifier=true