diff --git a/.gitignore b/.gitignore index aa724b77..1f613224 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,187 @@ +# Created by https://www.toptal.com/developers/gitignore/api/android,androidstudio,kotlin +# Edit at https://www.toptal.com/developers/gitignore?templates=android,androidstudio,kotlin + +### Android ### +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Log/OS Files +*.log + +# Android Studio generated files and folders +captures/ +.externalNativeBuild/ +.cxx/ +*.apk +output.json + +# IntelliJ *.iml +.idea/ +misc.xml +deploymentTargetDropDown.xml +render.experimental.xml + +# Keystore files +*.jks +*.keystore + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Android Profiling +*.hprof + +### Android Patch ### +gen-external-apklibs + +# Replacement of .externalNativeBuild directories introduced +# with Android Studio 3.5. + +### Kotlin ### +# Compiled class file +*.class + +# Log file + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### AndroidStudio ### +# Covers files to be ignored for android development using Android Studio. + +# Built application files +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files + +# Generated files +bin/ +gen/ +out/ + +# Gradle files .gradle -/local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -.DS_Store -/build -/captures + +# Signing files +.signing/ + +# Local configuration file (sdk path, etc) + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files + +# Android Studio +/*/build/ +/*/local.properties +/*/out +/*/*/build +/*/*/production +.navigation/ +*.ipr +*~ +*.swp + +# Keystore files + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Android Patch + +# External native build folder generated in Android Studio 2.2 and later .externalNativeBuild -.cxx -local.properties + +# NDK +obj/ + +# IntelliJ IDEA +*.iws +/out/ + +# User-specific configurations +.idea/caches/ +.idea/libraries/ +.idea/shelf/ +.idea/workspace.xml +.idea/tasks.xml +.idea/.name +.idea/compiler.xml +.idea/copyright/profiles_settings.xml +.idea/encodings.xml +.idea/misc.xml +.idea/modules.xml +.idea/scopes/scope_settings.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml +.idea/datasources.xml +.idea/dataSources.ids +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml +.idea/assetWizardSettings.xml +.idea/gradle.xml +.idea/jarRepositories.xml +.idea/navEditor.xml + +# Legacy Eclipse project files +.classpath +.project +.cproject +.settings/ + +# Mobile Tools for Java (J2ME) + +# Package Files # + +# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) + +## Plugin-specific files: + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Mongo Explorer plugin +.idea/mongoSettings.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### AndroidStudio Patch ### + +!/gradle/wrapper/gradle-wrapper.jar + +# End of https://www.toptal.com/developers/gitignore/api/android,androidstudio,kotlin \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index d843f340..94a25f7f 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,4 +1,6 @@ - + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d8be16d6..56b95fd3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,25 +1,26 @@ plugins { - alias(libs.plugins.android.application) + alias(libs.plugins.spot.android.application) alias(libs.plugins.kotlin.android) - alias(libs.plugins.kotlin.compose) + alias(libs.plugins.spot.android.hilt) } android { namespace = "com.umcspot.spot" - compileSdk = 35 - defaultConfig { - applicationId = "com.umcspot.spot" - minSdk = 35 - targetSdk = 35 - versionCode = 1 - versionName = "1.0" - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + signingConfigs { + getByName("debug") { + keyAlias = "androiddebugkey" + keyPassword = "android" + storeFile = file("debug.keystore") + } } buildTypes { + debug { + signingConfig = signingConfigs.getByName("debug") + } release { + signingConfig = signingConfigs.getByName("debug") isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), @@ -27,33 +28,19 @@ android { ) } } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - kotlinOptions { - jvmTarget = "11" - } - buildFeatures { - compose = true - } } dependencies { - - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.lifecycle.runtime.ktx) - implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.ui) - implementation(libs.androidx.ui.graphics) - implementation(libs.androidx.ui.tooling.preview) - implementation(libs.androidx.material3) - testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) - androidTestImplementation(platform(libs.androidx.compose.bom)) - androidTestImplementation(libs.androidx.ui.test.junit4) - debugImplementation(libs.androidx.ui.tooling) - debugImplementation(libs.androidx.ui.test.manifest) + implementation(projects.feature.home) + implementation(projects.feature.main) + implementation(projects.feature.mypage) + implementation(projects.domain.home) + implementation(projects.core.ui) + implementation(projects.core.network) + implementation(projects.core.model) + implementation(projects.core.designsystem) + implementation(projects.core.common) + implementation(projects.core.buildconfig) + implementation(projects.core.navigation) + implementation(projects.data.home) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d48b97fa..b4251374 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,13 @@ + + + + + + + android:theme="@style/Theme.Spot"> - - - + + - \ No newline at end of file diff --git a/app/src/main/java/com/umcspot/spot/MainActivity.kt b/app/src/main/java/com/umcspot/spot/MainActivity.kt deleted file mode 100644 index d920d79e..00000000 --- a/app/src/main/java/com/umcspot/spot/MainActivity.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.umcspot.spot - -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import com.umcspot.spot.ui.theme.SPOTTheme - -class MainActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContent { - SPOTTheme { - Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> - Greeting( - name = "Android", - modifier = Modifier.padding(innerPadding) - ) - } - } - } - } -} - -@Composable -fun Greeting(name: String, modifier: Modifier = Modifier) { - Text( - text = "Hello $name!", - modifier = modifier - ) -} - -@Preview(showBackground = true) -@Composable -fun GreetingPreview() { - SPOTTheme { - Greeting("Android") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/umcspot/spot/SpotApplication.kt b/app/src/main/java/com/umcspot/spot/SpotApplication.kt new file mode 100644 index 00000000..0b442c23 --- /dev/null +++ b/app/src/main/java/com/umcspot/spot/SpotApplication.kt @@ -0,0 +1,11 @@ +package com.umcspot.spot + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class SpotApplication : Application() { + override fun onCreate() { + super.onCreate() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/umcspot/spot/ui/theme/Color.kt b/app/src/main/java/com/umcspot/spot/ui/theme/Color.kt deleted file mode 100644 index 88be1ab2..00000000 --- a/app/src/main/java/com/umcspot/spot/ui/theme/Color.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.umcspot.spot.ui.theme - -import androidx.compose.ui.graphics.Color - -val Purple80 = Color(0xFFD0BCFF) -val PurpleGrey80 = Color(0xFFCCC2DC) -val Pink80 = Color(0xFFEFB8C8) - -val Purple40 = Color(0xFF6650a4) -val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/com/umcspot/spot/ui/theme/Theme.kt b/app/src/main/java/com/umcspot/spot/ui/theme/Theme.kt deleted file mode 100644 index 27398949..00000000 --- a/app/src/main/java/com/umcspot/spot/ui/theme/Theme.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.umcspot.spot.ui.theme - -import android.app.Activity -import android.os.Build -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext - -private val DarkColorScheme = darkColorScheme( - primary = Purple80, - secondary = PurpleGrey80, - tertiary = Pink80 -) - -private val LightColorScheme = lightColorScheme( - primary = Purple40, - secondary = PurpleGrey40, - tertiary = Pink40 - - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ -) - -@Composable -fun SPOTTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, - content: @Composable () -> Unit -) { - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - darkTheme -> DarkColorScheme - else -> LightColorScheme - } - - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/umcspot/spot/ui/theme/Type.kt b/app/src/main/java/com/umcspot/spot/ui/theme/Type.kt deleted file mode 100644 index 50f65e35..00000000 --- a/app/src/main/java/com/umcspot/spot/ui/theme/Type.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.umcspot.spot.ui.theme - -import androidx.compose.material3.Typography -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp - -// Set of Material typography styles to start with -val Typography = Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp - ) - /* Other default text styles to override - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp - ) - */ -) \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 9587e093..a7ea9ace 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,5 +1,18 @@ - - + + + \ No newline at end of file diff --git a/build-logic/convention/.gitignore b/build-logic/convention/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/build-logic/convention/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts new file mode 100644 index 00000000..e8ae96bc --- /dev/null +++ b/build-logic/convention/build.gradle.kts @@ -0,0 +1,56 @@ +plugins { + `kotlin-dsl` +} +group = "com.umcspot.spot.convention" + +dependencies { + compileOnly(libs.android.gradle.plugin) + compileOnly(libs.kotlin.gradle.plugin) + compileOnly(libs.ksp.gradle.plugin) + compileOnly(libs.compose.compiler.extension) +} + +gradlePlugin { + plugins { + register("androidApplication") { + id = "spot.android.application" + implementationClass = "AndroidApplicationPlugin" + } + register("buildConfig") { + id = "spot.android.build.config" + implementationClass = "BuildConfigPlugin" + } + register("androidLibrary") { + id = "spot.android.library" + implementationClass = "AndroidLibraryPlugin" + } + register("androidComposeLibrary") { + id = "spot.android.compose.library" + implementationClass = "AndroidComposeLibraryPlugin" + } + register("javaLibrary") { + id = "spot.android.java.library" + implementationClass = "JavaLibraryPlugin" + } + register("androidTest") { + id = "spot.android.test" + implementationClass = "AndroidTestPlugin" + } + register("unitTest") { + id = "spot.android.unittest" + implementationClass = "UnitTestPlugin" + } + register("androidHilt") { + id = "spot.android.hilt" + implementationClass = "HiltPlugin" + } + register("spotFeature") { + id = "spot.feature" + implementationClass = "SpotFeaturePlugin" + } + register("spotData") { + id = "spot.data" + implementationClass = "SpotDataPlugin" + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/AndroidApplicationPlugin.kt b/build-logic/convention/src/main/java/AndroidApplicationPlugin.kt new file mode 100644 index 00000000..dcaa6ab5 --- /dev/null +++ b/build-logic/convention/src/main/java/AndroidApplicationPlugin.kt @@ -0,0 +1,27 @@ +import com.android.build.api.dsl.ApplicationExtension +import com.umcspot.spot.convention.configureAndroidCompose +import com.umcspot.spot.convention.configureKotlinAndroid +import com.umcspot.spot.convention.extension.getVersion +import com.umcspot.spot.convention.extension.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class AndroidApplicationPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.application") + } + extensions.configure { + configureKotlinAndroid(this) + configureAndroidCompose(this) + with(defaultConfig) { + targetSdk = libs.getVersion("targetSdk").requiredVersion.toInt() + versionCode = libs.getVersion("versionCode").requiredVersion.toInt() + versionName = libs.getVersion("versionName").requiredVersion + } + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/AndroidComposeLibraryPlugin.kt b/build-logic/convention/src/main/java/AndroidComposeLibraryPlugin.kt new file mode 100644 index 00000000..5de04b09 --- /dev/null +++ b/build-logic/convention/src/main/java/AndroidComposeLibraryPlugin.kt @@ -0,0 +1,19 @@ +import com.android.build.api.dsl.LibraryExtension +import com.umcspot.spot.convention.configureAndroidCompose +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class AndroidComposeLibraryPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("spot.android.library") + } + + extensions.configure { + configureAndroidCompose(this) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/AndroidLibraryPlugin.kt b/build-logic/convention/src/main/java/AndroidLibraryPlugin.kt new file mode 100644 index 00000000..cb659bc3 --- /dev/null +++ b/build-logic/convention/src/main/java/AndroidLibraryPlugin.kt @@ -0,0 +1,29 @@ +import com.android.build.api.dsl.LibraryExtension +import com.umcspot.spot.convention.configureKotlinAndroid +import com.umcspot.spot.convention.configureKotlinCoroutine +import com.umcspot.spot.convention.extension.getLibrary +import com.umcspot.spot.convention.extension.implementation +import com.umcspot.spot.convention.extension.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies + +class AndroidLibraryPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.library") + } + + extensions.configure { + configureKotlinAndroid(this) + configureKotlinCoroutine(this) + } + + dependencies { + implementation(libs.getLibrary("timber")) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/AndroidTestPlugin.kt b/build-logic/convention/src/main/java/AndroidTestPlugin.kt new file mode 100644 index 00000000..84866454 --- /dev/null +++ b/build-logic/convention/src/main/java/AndroidTestPlugin.kt @@ -0,0 +1,29 @@ +import com.android.build.api.dsl.LibraryExtension +import com.umcspot.spot.convention.extension.androidTestImplementation +import com.umcspot.spot.convention.extension.debugImplementation +import com.umcspot.spot.convention.extension.getLibrary +import com.umcspot.spot.convention.extension.implementation +import com.umcspot.spot.convention.extension.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies + +class AndroidTestPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + dependencies { + androidTestImplementation(libs.getLibrary("androidx.test.runner")) + debugImplementation(libs.getLibrary("androidx.test.core")) + androidTestImplementation(libs.getLibrary("kotlinx.coroutines.test")) + implementation(libs.getLibrary("androidx.test.ext.junit")) + } + + extensions.configure { + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/BuildConfigPlugin.kt b/build-logic/convention/src/main/java/BuildConfigPlugin.kt new file mode 100644 index 00000000..6a779e52 --- /dev/null +++ b/build-logic/convention/src/main/java/BuildConfigPlugin.kt @@ -0,0 +1,15 @@ +import com.android.build.gradle.LibraryExtension +import com.umcspot.spot.convention.configureBuildConfig +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class BuildConfigPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + extensions.configure { + configureBuildConfig(this) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/HiltPlugin.kt b/build-logic/convention/src/main/java/HiltPlugin.kt new file mode 100644 index 00000000..dc804355 --- /dev/null +++ b/build-logic/convention/src/main/java/HiltPlugin.kt @@ -0,0 +1,26 @@ +import com.umcspot.spot.convention.extension.getLibrary +import com.umcspot.spot.convention.extension.implementation +import com.umcspot.spot.convention.extension.ksp +import com.umcspot.spot.convention.extension.kspTest +import com.umcspot.spot.convention.extension.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.dependencies + +class HiltPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.google.devtools.ksp") + apply("dagger.hilt.android.plugin") + } + + dependencies { + implementation(libs.getLibrary("hilt.android")) + ksp(libs.getLibrary("hilt.compiler")) + implementation(libs.getLibrary("hilt.testing")) + kspTest(libs.getLibrary("hilt.testing.compiler")) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/JavaLibraryPlugin.kt b/build-logic/convention/src/main/java/JavaLibraryPlugin.kt new file mode 100644 index 00000000..cea86265 --- /dev/null +++ b/build-logic/convention/src/main/java/JavaLibraryPlugin.kt @@ -0,0 +1,35 @@ +import com.umcspot.spot.convention.extension.getLibrary +import com.umcspot.spot.convention.extension.getVersion +import com.umcspot.spot.convention.extension.implementation +import com.umcspot.spot.convention.extension.libs +import org.gradle.api.JavaVersion +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension + +class JavaLibraryPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("java-library") + apply("org.jetbrains.kotlin.jvm") + } + + extensions.configure { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + extensions.configure { + jvmToolchain(libs.getVersion("jdkVersion").requiredVersion.toInt()) + } + + dependencies { + implementation(libs.getLibrary("javax.inject")) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/SpotDataPlugin.kt b/build-logic/convention/src/main/java/SpotDataPlugin.kt new file mode 100644 index 00000000..23deb8d9 --- /dev/null +++ b/build-logic/convention/src/main/java/SpotDataPlugin.kt @@ -0,0 +1,28 @@ +import com.umcspot.spot.convention.extension.getLibrary +import com.umcspot.spot.convention.extension.implementation +import com.umcspot.spot.convention.extension.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.dependencies + +class SpotDataPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("spot.android.library") + apply("spot.android.hilt") + apply("org.jetbrains.kotlin.plugin.serialization") + } + + dependencies { + implementation(project(":core:network")) + implementation(project(":core:common")) + implementation(project(":core:model")) + + implementation(libs.getLibrary("kotlinx.serialization.json")) + implementation(libs.getLibrary("retrofit.core")) + implementation(libs.getLibrary("retrofit.kotlin.serialization")) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/SpotFeaturePlugin.kt b/build-logic/convention/src/main/java/SpotFeaturePlugin.kt new file mode 100644 index 00000000..9ee9db5c --- /dev/null +++ b/build-logic/convention/src/main/java/SpotFeaturePlugin.kt @@ -0,0 +1,28 @@ +import com.umcspot.spot.convention.extension.getBundle +import com.umcspot.spot.convention.extension.getLibrary +import com.umcspot.spot.convention.extension.implementation +import com.umcspot.spot.convention.extension.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.dependencies + +class SpotFeaturePlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("spot.android.compose.library") + apply("spot.android.hilt") + apply("org.jetbrains.kotlin.plugin.serialization") + } + + dependencies { + implementation(libs.getLibrary("kotlinx.serialization.json")) + implementation(project(":core:ui")) + implementation(project(":core:designsystem")) + implementation(project(":core:model")) + implementation(project(":core:navigation")) + implementation(libs.getBundle("compose")) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/UnitTestPlugin.kt b/build-logic/convention/src/main/java/UnitTestPlugin.kt new file mode 100644 index 00000000..e5d55268 --- /dev/null +++ b/build-logic/convention/src/main/java/UnitTestPlugin.kt @@ -0,0 +1,29 @@ +import com.android.build.api.dsl.LibraryExtension +import com.umcspot.spot.convention.extension.getLibrary +import com.umcspot.spot.convention.extension.libs +import com.umcspot.spot.convention.extension.testImplementation +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies + +class UnitTestPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + dependencies { + testImplementation(libs.getLibrary("kotlinx.coroutines.test")) + testImplementation(libs.getLibrary("mockito")) + testImplementation(libs.getLibrary("junit")) + testImplementation(libs.getLibrary("robolectric")) + } + + extensions.configure { + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/AndroidCompose.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/AndroidCompose.kt new file mode 100644 index 00000000..83450583 --- /dev/null +++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/AndroidCompose.kt @@ -0,0 +1,34 @@ +package com.umcspot.spot.convention + +import com.android.build.api.dsl.CommonExtension +import com.umcspot.spot.convention.extension.debugImplementation +import com.umcspot.spot.convention.extension.getBundle +import com.umcspot.spot.convention.extension.getLibrary +import com.umcspot.spot.convention.extension.implementation +import com.umcspot.spot.convention.extension.libs +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType +import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension +import org.gradle.kotlin.dsl.dependencies + +internal fun Project.configureAndroidCompose( + commonExtension: CommonExtension<*, *, *, *, *, *>, +) { + pluginManager.apply("org.jetbrains.kotlin.plugin.compose") + + commonExtension.apply { + buildFeatures { + compose = true + } + extensions.getByType().apply { + enableStrongSkippingMode.set(true) + includeSourceInformation.set(true) + } + dependencies { + implementation(platform(libs.getLibrary("compose.bom"))) + implementation(libs.getBundle("compose")) + debugImplementation(libs.getBundle("compose.debug")) + implementation(libs.getLibrary("kotlinx.collections")) + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/BuildConfig.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/BuildConfig.kt new file mode 100644 index 00000000..94a5dd54 --- /dev/null +++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/BuildConfig.kt @@ -0,0 +1,30 @@ +package com.umcspot.spot.convention + +import com.android.build.api.dsl.CommonExtension +import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties +import org.gradle.api.Project + +internal fun Project.configureBuildConfig( + commonExtension: CommonExtension<*, *, *, *, *, *>, +) { + commonExtension.apply { + defaultConfig { + val properties = gradleLocalProperties(rootDir, providers) + + buildConfigField( + "String", + "BASE_URL", + "\"${properties.getProperty("base.url") ?: "https://default-url.com/"}\"" + ) + buildConfigField( + "String", + "KAKAO_NATIVE_KEY", + "\"${properties.getProperty("kakao.native.key") ?: ""}\"" + ) + } + + buildFeatures { + buildConfig = true + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinAndroid.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinAndroid.kt new file mode 100644 index 00000000..afbafd68 --- /dev/null +++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinAndroid.kt @@ -0,0 +1,56 @@ +package com.umcspot.spot.convention + +import com.android.build.api.dsl.CommonExtension +import com.umcspot.spot.convention.extension.getVersion +import com.umcspot.spot.convention.extension.libs +import org.gradle.api.JavaVersion +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension + +internal fun Project.configureKotlinAndroid( + commonExtension: CommonExtension<*, *, *, *, *, *>, +) { + pluginManager.apply("org.jetbrains.kotlin.android") + + commonExtension.apply { + compileSdk = libs.getVersion("compileSdk").requiredVersion.toInt() + + defaultConfig { + minSdk = libs.getVersion("minSdk").requiredVersion.toInt() + + vectorDrawables.useSupportLibrary = true + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + extensions.getByType().configureCompilerOptions() + + buildTypes { + getByName("debug") { + proguardFiles( + getDefaultProguardFile("proguard-android.txt"), + "proguard-debug.pro", + ) + } + + getByName("release") { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android.txt"), + "proguard-rules.pro", + ) + } + } + } +} + +fun KotlinAndroidProjectExtension.configureCompilerOptions() { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinCoroutine.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinCoroutine.kt new file mode 100644 index 00000000..678c6baa --- /dev/null +++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinCoroutine.kt @@ -0,0 +1,18 @@ +package com.umcspot.spot.convention + +import com.android.build.api.dsl.CommonExtension +import com.umcspot.spot.convention.extension.getBundle +import com.umcspot.spot.convention.extension.implementation +import com.umcspot.spot.convention.extension.libs +import org.gradle.api.Project +import org.gradle.kotlin.dsl.dependencies + +internal fun Project.configureKotlinCoroutine( + commonExtension: CommonExtension<*, *, *, *, *, *>, +) { + commonExtension.apply { + dependencies { + implementation(libs.getBundle("coroutine")) + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/DependencyHandlerScope.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/DependencyHandlerScope.kt new file mode 100644 index 00000000..128262c1 --- /dev/null +++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/DependencyHandlerScope.kt @@ -0,0 +1,55 @@ +package com.umcspot.spot.convention.extension + +import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.ConfigurableFileTree +import org.gradle.api.provider.Provider +import org.gradle.kotlin.dsl.DependencyHandlerScope + +fun DependencyHandlerScope.implementation(project: Project) { + "implementation"(project) +} + +fun DependencyHandlerScope.implementation(provider: Provider<*>) { + "implementation"(provider) +} + +fun DependencyHandlerScope.implementation(fileTree: ConfigurableFileTree) { + "implementation"(fileTree) +} + +fun DependencyHandlerScope.implementation(fileCollection: ConfigurableFileCollection) { + "implementation"(fileCollection) +} + +fun DependencyHandlerScope.debugImplementation(provider: Provider<*>) { + "debugImplementation"(provider) +} + +fun DependencyHandlerScope.releaseImplementation(provider: Provider<*>) { + "releaseImplementation"(provider) +} + +fun DependencyHandlerScope.ksp(provider: Provider<*>) { + "ksp"(provider) +} + +fun DependencyHandlerScope.kspTest(provider: Provider<*>) { + "kspTest"(provider) +} + +fun DependencyHandlerScope.coreLibraryDesugaring(provider: Provider<*>) { + "coreLibraryDesugaring"(provider) +} + +fun DependencyHandlerScope.androidTestImplementation(provider: Provider<*>) { + "androidTestImplementation"(provider) +} + +fun DependencyHandlerScope.testImplementation(provider: Provider<*>) { + "testImplementation"(provider) +} + +fun DependencyHandlerScope.compileOnly(provider: Provider<*>) { + "compileOnly"(provider) +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/ProjectExt.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/ProjectExt.kt new file mode 100644 index 00000000..78ab6fe5 --- /dev/null +++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/ProjectExt.kt @@ -0,0 +1,9 @@ +package com.umcspot.spot.convention.extension + +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalog +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.kotlin.dsl.getByType + +val Project.libs: VersionCatalog + get() = extensions.getByType().named("libs") \ No newline at end of file diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/VersionCatalogExt.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/VersionCatalogExt.kt new file mode 100644 index 00000000..168abe55 --- /dev/null +++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/VersionCatalogExt.kt @@ -0,0 +1,25 @@ +package com.umcspot.spot.convention.extension + +import org.gradle.api.artifacts.ExternalModuleDependencyBundle +import org.gradle.api.artifacts.MinimalExternalModuleDependency +import org.gradle.api.artifacts.VersionCatalog +import org.gradle.api.artifacts.VersionConstraint +import org.gradle.api.provider.Provider + +fun VersionCatalog.getBundle(bundleName: String): Provider { + return findBundle(bundleName).orElseThrow { + NoSuchElementException("Bundle with name $bundleName not found in the catalog") + } +} + +fun VersionCatalog.getLibrary(libraryName: String): Provider { + return findLibrary(libraryName).orElseThrow { + NoSuchElementException("Library with name $libraryName not found in the catalog") + } +} + +fun VersionCatalog.getVersion(versionName: String): VersionConstraint { + return findVersion(versionName).orElseThrow { + NoSuchElementException("Version with name $versionName not found in the catalog") + } +} \ No newline at end of file diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 00000000..62257853 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,14 @@ +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + } + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +rootProject.name = "build-logic" +include(":convention") \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 952b9306..a73ae685 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,5 +2,11 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false - alias(libs.plugins.kotlin.compose) apply false + alias(libs.plugins.compose.compiler) apply false + alias(libs.plugins.android.library) apply false + alias(libs.plugins.kotlin.serialization) apply false + alias(libs.plugins.ksp) apply false + alias(libs.plugins.hilt) apply false + alias(libs.plugins.ktlint) + alias(libs.plugins.kotlin.jvm) apply false } \ No newline at end of file diff --git a/core/buildconfig/.gitignore b/core/buildconfig/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/buildconfig/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/buildconfig/build.gradle.kts b/core/buildconfig/build.gradle.kts new file mode 100644 index 00000000..0417476c --- /dev/null +++ b/core/buildconfig/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + alias(libs.plugins.spot.android.library) + alias(libs.plugins.spot.android.build.config) + alias(libs.plugins.spot.android.hilt) + alias(libs.plugins.spot.android.test) +} + +android { + namespace = "com.umcspot.spot.buildconfig" +} +dependencies { + implementation(projects.core.common) +} \ No newline at end of file diff --git a/core/buildconfig/consumer-rules.pro b/core/buildconfig/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/core/buildconfig/proguard-rules.pro b/core/buildconfig/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/core/buildconfig/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/core/buildconfig/src/main/AndroidManifest.xml b/core/buildconfig/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/buildconfig/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/di/BuildConfigModule.kt b/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/di/BuildConfigModule.kt new file mode 100644 index 00000000..1dffeb3b --- /dev/null +++ b/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/di/BuildConfigModule.kt @@ -0,0 +1,19 @@ +package com.umcspot.spot.buildconfig.di + +import com.umcspot.spot.buildconfig.impl.BuildConfigFieldsProviderImpl +import com.umcspot.spot.common.BuildConfigFieldProvider +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@InstallIn(SingletonComponent::class) +@Module +object BuildConfigModule { + @Provides + @Singleton + fun provideBuildConfigFieldsProvider( + buildConfigFieldProvider: BuildConfigFieldsProviderImpl + ): BuildConfigFieldProvider = buildConfigFieldProvider +} \ No newline at end of file diff --git a/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/impl/BuildConfigFieldsProviderImpl.kt b/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/impl/BuildConfigFieldsProviderImpl.kt new file mode 100644 index 00000000..2f089249 --- /dev/null +++ b/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/impl/BuildConfigFieldsProviderImpl.kt @@ -0,0 +1,18 @@ +package com.umcspot.spot.buildconfig.impl + +import com.umcspot.spot.buildconfig.BuildConfig.BASE_URL +import com.umcspot.spot.buildconfig.BuildConfig.KAKAO_NATIVE_KEY +import com.umcspot.spot.common.BuildConfigFieldProvider +import com.umcspot.spot.common.BuildConfigFields + + +import javax.inject.Inject + +class BuildConfigFieldsProviderImpl @Inject constructor() : BuildConfigFieldProvider { + override fun get(): BuildConfigFields = + BuildConfigFields( + baseUrl = BASE_URL, + kakaoNativeKey = KAKAO_NATIVE_KEY, + isDebug = true + ) +} \ No newline at end of file diff --git a/core/common/.gitignore b/core/common/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/common/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts new file mode 100644 index 00000000..c374ceb2 --- /dev/null +++ b/core/common/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + alias(libs.plugins.spot.android.library) + alias(libs.plugins.spot.android.hilt) + alias(libs.plugins.spot.android.test) +} + +android { + namespace = "com.umcspot.spot.common" +} \ No newline at end of file diff --git a/core/common/consumer-rules.pro b/core/common/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/core/common/proguard-rules.pro b/core/common/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/core/common/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/core/common/src/main/AndroidManifest.xml b/core/common/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/common/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/common/src/main/java/com/umcspot/spot/common/BuildConfigFields.kt b/core/common/src/main/java/com/umcspot/spot/common/BuildConfigFields.kt new file mode 100644 index 00000000..3af0effc --- /dev/null +++ b/core/common/src/main/java/com/umcspot/spot/common/BuildConfigFields.kt @@ -0,0 +1,7 @@ +package com.umcspot.spot.common + +data class BuildConfigFields( + val baseUrl: String, + val kakaoNativeKey: String, + val isDebug: Boolean +) \ No newline at end of file diff --git a/core/common/src/main/java/com/umcspot/spot/common/BuildConfigProvider.kt b/core/common/src/main/java/com/umcspot/spot/common/BuildConfigProvider.kt new file mode 100644 index 00000000..66dbecc2 --- /dev/null +++ b/core/common/src/main/java/com/umcspot/spot/common/BuildConfigProvider.kt @@ -0,0 +1,5 @@ +package com.umcspot.spot.common + +interface BuildConfigFieldProvider { + fun get(): BuildConfigFields +} \ No newline at end of file diff --git a/core/datastore/.gitignore b/core/datastore/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/datastore/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts new file mode 100644 index 00000000..87efe8c7 --- /dev/null +++ b/core/datastore/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + alias(libs.plugins.spot.android.library) + alias(libs.plugins.spot.android.hilt) + alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.spot.android.test) +} + +android { + namespace = "com.umcspot.spot.datastore" +} + +dependencies { + implementation(projects.core.common) + implementation(libs.kotlinx.serialization.json) + implementation(libs.bundles.datastore) +} \ No newline at end of file diff --git a/core/datastore/consumer-rules.pro b/core/datastore/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/core/datastore/proguard-rules.pro b/core/datastore/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/core/datastore/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/core/datastore/src/main/AndroidManifest.xml b/core/datastore/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/datastore/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotSecureDataStoreSerializer.kt b/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotSecureDataStoreSerializer.kt new file mode 100644 index 00000000..d8a58837 --- /dev/null +++ b/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotSecureDataStoreSerializer.kt @@ -0,0 +1,100 @@ +package com.umcspot.spot.datastore + +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import androidx.datastore.core.Serializer +import kotlinx.serialization.json.Json +import java.io.InputStream +import java.io.OutputStream +import java.security.KeyStore +import javax.crypto.Cipher +import javax.crypto.KeyGenerator +import javax.crypto.SecretKey +import javax.crypto.spec.GCMParameterSpec +import javax.inject.Inject + +class SpotSecureDataStoreSerializer @Inject constructor() : Serializer { + companion object { + private const val KEYSTORE_PROVIDER = "AndroidKeyStore" + private const val KEY_ALIAS = "data-store-key" + private const val TRANSFORMATION = "AES/GCM/NoPadding" + private const val IV_SIZE = 12 + } + + private val keyStore: KeyStore by lazy { + KeyStore.getInstance(KEYSTORE_PROVIDER).apply { + load(null) + if (!containsAlias(KEY_ALIAS)) { + createKey() + } + } + } + + private fun createKey() { + val keyGenerator = KeyGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_AES, + KEYSTORE_PROVIDER + ) + val keyGenParameterSpec = KeyGenParameterSpec.Builder( + KEY_ALIAS, + KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT + ) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .setKeySize(256) + .build() + + keyGenerator.init(keyGenParameterSpec) + keyGenerator.generateKey() + } + + override val defaultValue: SpotTokenData + get() = SpotTokenData() + + override suspend fun readFrom(input: InputStream): SpotTokenData { + return try { + val encryptedDataWithIv = input.readBytes() + if (encryptedDataWithIv.size < IV_SIZE) { + return defaultValue + } + + val iv = encryptedDataWithIv.copyOfRange(0, IV_SIZE) + val encryptedData = encryptedDataWithIv.copyOfRange(IV_SIZE, encryptedDataWithIv.size) + + val cipher = Cipher.getInstance(TRANSFORMATION) + val key = keyStore.getKey(KEY_ALIAS, null) as SecretKey + val spec = GCMParameterSpec(128, iv) + cipher.init(Cipher.DECRYPT_MODE, key, spec) + + val decryptedBytes = cipher.doFinal(encryptedData) + Json.decodeFromString( + deserializer = SpotTokenData.serializer(), + string = decryptedBytes.decodeToString() + ) + } catch (e: Exception) { + e.printStackTrace() + defaultValue + } + } + + override suspend fun writeTo(t: SpotTokenData, output: OutputStream) { + try { + val serializedData = Json.encodeToString( + serializer = SpotTokenData.serializer(), + value = t + ) + + val cipher = Cipher.getInstance(TRANSFORMATION) + val key = keyStore.getKey(KEY_ALIAS, null) as SecretKey + cipher.init(Cipher.ENCRYPT_MODE, key) + + val iv = cipher.iv + val encryptedData = cipher.doFinal(serializedData.toByteArray()) + + output.write(iv + encryptedData) + } catch (e: Exception) { + e.printStackTrace() + throw e + } + } +} \ No newline at end of file diff --git a/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotTokenData.kt b/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotTokenData.kt new file mode 100644 index 00000000..8fb51620 --- /dev/null +++ b/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotTokenData.kt @@ -0,0 +1,10 @@ +package com.umcspot.spot.datastore + + +import kotlinx.serialization.Serializable + +@Serializable +data class SpotTokenData( + val accessToken: String = "", + val refreshToken: String = "", +) \ No newline at end of file diff --git a/core/datastore/src/main/java/com/umcspot/spot/datastore/di/DataStoreModule.kt b/core/datastore/src/main/java/com/umcspot/spot/datastore/di/DataStoreModule.kt new file mode 100644 index 00000000..2f4b76f4 --- /dev/null +++ b/core/datastore/src/main/java/com/umcspot/spot/datastore/di/DataStoreModule.kt @@ -0,0 +1,32 @@ +package com.umcspot.spot.datastore.di + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.core.DataStoreFactory +import androidx.datastore.dataStoreFile +import com.umcspot.spot.datastore.SpotSecureDataStoreSerializer +import com.umcspot.spot.datastore.SpotTokenData +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object DataStoreModule { + @Provides + @Singleton + fun providesDataStore( + @ApplicationContext context: Context, + spotSecureDataStoreSerializer: SpotSecureDataStoreSerializer + ): DataStore = + DataStoreFactory.create( + serializer = spotSecureDataStoreSerializer + ) { + context.dataStoreFile(DATASTORE_PREFERENCES) + } + + private const val DATASTORE_PREFERENCES = "com.umcspot.spot.datastore" +} \ No newline at end of file diff --git a/core/designsystem/.gitignore b/core/designsystem/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/designsystem/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts new file mode 100644 index 00000000..31077f7e --- /dev/null +++ b/core/designsystem/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + alias(libs.plugins.spot.android.compose.library) +} + +android { + namespace = "com.umcspot.spot.designsystem" +} +dependencies { + implementation(projects.core.ui) + implementation(libs.flexible.bottomsheet) +} \ No newline at end of file diff --git a/core/designsystem/consumer-rules.pro b/core/designsystem/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/core/designsystem/proguard-rules.pro b/core/designsystem/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/core/designsystem/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/core/designsystem/src/main/AndroidManifest.xml b/core/designsystem/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/designsystem/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/designsystem/src/main/java/com/umcspot/spot/designsystem/component/button/SpotButton.kt b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/component/button/SpotButton.kt new file mode 100644 index 00000000..54bd2415 --- /dev/null +++ b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/component/button/SpotButton.kt @@ -0,0 +1,51 @@ +package com.umcspot.spot.designsystem.component.button + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.umcspot.spot.ui.extension.noRippleClickable +import com.umcspot.spot.ui.extension.screenHeightDp + +@Composable +fun SpotButton( + onClick: () -> Unit, + buttonText: String, + buttonStyle: TextStyle, + buttonTextColor: Color, + modifier: Modifier = Modifier, + buttonBackgroundColor: Color = Color.Unspecified, + buttonStrokeColor: Color = Color.Unspecified, + textAlign: TextAlign = TextAlign.Center +) { + Row( + modifier = modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(color = buttonBackgroundColor) + .border(width = 1.dp, color = buttonStrokeColor, shape = RoundedCornerShape(12.dp)) + .noRippleClickable(onClick = onClick) + .padding(vertical = screenHeightDp(16.dp)), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = buttonText, + style = buttonStyle, + color = buttonTextColor, + textAlign = textAlign + ) + } +} diff --git a/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Color.kt b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Color.kt new file mode 100644 index 00000000..8021782f --- /dev/null +++ b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Color.kt @@ -0,0 +1,446 @@ +package com.umcspot.spot.designsystem.theme + +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.Color + +// primary +val primary50 = Color(0xFFF5F2FC) +val primary100 = Color(0xFFF0EBFA) +val primary200 = Color(0xFFE0D5F5) +val primary300 = Color(0xFFAA7CFF) +val primary400 = Color(0xFF8C6DC8) +val primary500 = Color(0xFF7D61B2) +val primary600 = Color(0xFF755BA7) +val primary700 = Color(0xFF5E4985) +val primary800 = Color(0xFF463664) +val primary900 = Color(0xFF372A4E) + +// secondary +val secondary50 = Color(0xFFFFFAEE) +val secondary100 = Color(0xFFFFF8E5) +val secondary300 = Color(0xFFFFCD54) + +// olive_green +val oliveGreen300 = Color(0xFF96CE5A) +val oliveGreen700 = Color(0xFF5A7C37) + +// soft_blue +val softBlue200 = Color(0xFFD4EEFA) +val softBlue300 = Color(0xFF75C8EE) +val softBlue600 = Color(0xFF5896B3) + +// sub +val sand100 = Color(0xFFFFF8E5) + +val orange300 = Color(0xFFFDC6B0) + +val warning50 = Color(0xFFFFF5E6) +val warning300 = Color(0xFFFFA01A) + +// state +val success50 = Color(0xFFE8F4FC) +val success300 = Color(0xFF2C9CE3) + +val error50 = Color(0xFFFCEAEA) +val error300 = Color(0xFFF15655) + +// grayscale +val gray50 = Color(0xFFFEFEFE) +val gray100 = Color(0xFFF4F4F4) +val gray200 = Color(0xFFE2E2E2) +val gray300 = Color(0xFFC4C4C4) +val gray400 = Color(0xFFA6A6A6) +val gray500 = Color(0xFF878787) +val gray600 = Color(0xFF6E6E6E) +val gray700 = Color(0xFF404040) +val gray800 = Color(0xFF262626) +val gray900 = Color(0xFF161616) + +// black_and_white +val black = Color(0xFF000000) +val white = Color(0xFFFFFFFF) + +// opacity +val blackAlpha80 = black.copy(alpha = 0.8f) +val blackAlpha50 = black.copy(alpha = 0.5f) + +val whiteAlpha50 = white.copy(alpha = 0.5f) +val whiteAlpha10 = white.copy(alpha = 0.1f) + +val primary300Alpha10 = primary300.copy(alpha = 0.1f) +val primary300Alpha20 = primary300.copy(alpha = 0.2f) +val primary50Alpha50 = primary50.copy(alpha = 0.5f) + +val secondary300Alpha30 = secondary300.copy(alpha = 0.3f) +val secondary300Alpha10 = secondary300.copy(alpha = 0.1f) + +val gray900Alpha80 = gray900.copy(alpha = 0.8f) + +// kakao +val kakaoYellow = Color(0xFFFEE500) + +@Stable +class SpotColors( + primary50: Color, + primary100: Color, + primary200: Color, + primary300: Color, + primary400: Color, + primary500: Color, + primary600: Color, + primary700: Color, + primary800: Color, + primary900: Color, + secondary50: Color, + secondary100: Color, + secondary300: Color, + oliveGreen300: Color, + oliveGreen700: Color, + softBlue200: Color, + softBlue300: Color, + softBlue600: Color, + sand100: Color, + orange300: Color, + warning50: Color, + warning300: Color, + success50: Color, + success300: Color, + error50: Color, + error300: Color, + gray50: Color, + gray100: Color, + gray200: Color, + gray300: Color, + gray400: Color, + gray500: Color, + gray600: Color, + gray700: Color, + gray800: Color, + gray900: Color, + black: Color, + white: Color, + blackAlpha80: Color, + blackAlpha50: Color, + whiteAlpha50: Color, + whiteAlpha10: Color, + primary300Alpha10: Color, + primary300Alpha20: Color, + primary50Alpha50: Color, + secondary300Alpha30: Color, + secondary300Alpha10: Color, + gray900Alpha80: Color, + kakaoYellow: Color, + isLight: Boolean +) { + var primary50 by mutableStateOf(primary50) + private set + var primary100 by mutableStateOf(primary100) + private set + var primary200 by mutableStateOf(primary200) + private set + var primary300 by mutableStateOf(primary300) + private set + var primary400 by mutableStateOf(primary400) + private set + var primary500 by mutableStateOf(primary500) + private set + var primary600 by mutableStateOf(primary600) + private set + var primary700 by mutableStateOf(primary700) + private set + var primary800 by mutableStateOf(primary800) + private set + var primary900 by mutableStateOf(primary900) + private set + var secondary50 by mutableStateOf(secondary50) + private set + var secondary100 by mutableStateOf(secondary100) + private set + var secondary300 by mutableStateOf(secondary300) + private set + var oliveGreen300 by mutableStateOf(oliveGreen300) + private set + var oliveGreen700 by mutableStateOf(oliveGreen700) + private set + var softBlue200 by mutableStateOf(softBlue200) + private set + var softBlue300 by mutableStateOf(softBlue300) + private set + var softBlue600 by mutableStateOf(softBlue600) + private set + var sand100 by mutableStateOf(sand100) + private set + var orange300 by mutableStateOf(orange300) + private set + var warning50 by mutableStateOf(warning50) + private set + var warning300 by mutableStateOf(warning300) + private set + var success50 by mutableStateOf(success50) + private set + var success300 by mutableStateOf(success300) + private set + var error50 by mutableStateOf(error50) + private set + var error300 by mutableStateOf(error300) + private set + var gray50 by mutableStateOf(gray50) + private set + var gray100 by mutableStateOf(gray100) + private set + var gray200 by mutableStateOf(gray200) + private set + var gray300 by mutableStateOf(gray300) + private set + var gray400 by mutableStateOf(gray400) + private set + var gray500 by mutableStateOf(gray500) + private set + var gray600 by mutableStateOf(gray600) + private set + var gray700 by mutableStateOf(gray700) + private set + var gray800 by mutableStateOf(gray800) + private set + var gray900 by mutableStateOf(gray900) + private set + var black by mutableStateOf(black) + private set + var white by mutableStateOf(white) + private set + var blackAlpha80 by mutableStateOf(blackAlpha80) + private set + var blackAlpha50 by mutableStateOf(blackAlpha50) + private set + var whiteAlpha50 by mutableStateOf(whiteAlpha50) + private set + var whiteAlpha10 by mutableStateOf(whiteAlpha10) + private set + var primary300Alpha10 by mutableStateOf(primary300Alpha10) + private set + var primary300Alpha20 by mutableStateOf(primary300Alpha20) + private set + var primary50Alpha50 by mutableStateOf(primary50Alpha50) + private set + var secondary300Alpha30 by mutableStateOf(secondary300Alpha30) + private set + var secondary300Alpha10 by mutableStateOf(secondary300Alpha10) + private set + var gray900Alpha80 by mutableStateOf(gray900Alpha80) + private set + var kakaoYellow by mutableStateOf(kakaoYellow) + private set + var isLight by mutableStateOf(isLight) + + fun copy(): SpotColors = SpotColors( + primary50, + primary100, + primary200, + primary300, + primary400, + primary500, + primary600, + primary700, + primary800, + primary900, + secondary50, + secondary100, + secondary300, + oliveGreen300, + oliveGreen700, + softBlue200, + softBlue300, + softBlue600, + sand100, + orange300, + warning50, + warning300, + success50, + success300, + error50, + error300, + gray50, + gray100, + gray200, + gray300, + gray400, + gray500, + gray600, + gray700, + gray800, + gray900, + black, + white, + blackAlpha80, + blackAlpha50, + whiteAlpha50, + whiteAlpha10, + primary300Alpha10, + primary300Alpha20, + primary50Alpha50, + secondary300Alpha30, + secondary300Alpha10, + gray900Alpha80, + kakaoYellow, + isLight + ) + + fun update(colors: SpotColors) { + primary50 = colors.primary50 + primary100 = colors.primary100 + primary200 = colors.primary200 + primary300 = colors.primary300 + primary400 = colors.primary400 + primary500 = colors.primary500 + primary600 = colors.primary600 + primary700 = colors.primary700 + primary800 = colors.primary800 + primary900 = colors.primary900 + secondary50 = colors.secondary50 + secondary100 = colors.secondary100 + secondary300 = colors.secondary300 + oliveGreen300 = colors.oliveGreen300 + oliveGreen700 = colors.oliveGreen700 + softBlue200 = colors.softBlue200 + softBlue300 = colors.softBlue300 + softBlue600 = colors.softBlue600 + sand100 = colors.sand100 + orange300 = colors.orange300 + warning50 = colors.warning50 + warning300 = colors.warning300 + success50 = colors.success50 + success300 = colors.success300 + error50 = colors.error50 + error300 = colors.error300 + gray50 = colors.gray50 + gray100 = colors.gray100 + gray200 = colors.gray200 + gray300 = colors.gray300 + gray400 = colors.gray400 + gray500 = colors.gray500 + gray600 = colors.gray600 + gray700 = colors.gray700 + gray800 = colors.gray800 + gray900 = colors.gray900 + black = colors.black + white = colors.white + blackAlpha80 = colors.blackAlpha80 + blackAlpha50 = colors.blackAlpha50 + whiteAlpha50 = colors.whiteAlpha50 + whiteAlpha10 = colors.whiteAlpha10 + primary300Alpha10 = colors.primary300Alpha10 + primary300Alpha20 = colors.primary300Alpha20 + primary50Alpha50 = colors.primary50Alpha50 + secondary300Alpha30 = colors.secondary300Alpha30 + secondary300Alpha10 = colors.secondary300Alpha10 + gray900Alpha80 = colors.gray900Alpha80 + kakaoYellow = colors.kakaoYellow + isLight = colors.isLight + } +} + +fun SpotDayColors( + Primary50: Color = primary50, + Primary100: Color = primary100, + Primary200: Color = primary200, + Primary300: Color = primary300, + Primary400: Color = primary400, + Primary500: Color = primary500, + Primary600: Color = primary600, + Primary700: Color = primary700, + Primary800: Color = primary800, + Primary900: Color = primary900, + Secondary50: Color = secondary50, + Secondary100: Color = secondary100, + Secondary300: Color = secondary300, + OliveGreen300: Color = oliveGreen300, + OliveGreen700: Color = oliveGreen700, + SoftBlue200: Color = softBlue200, + SoftBlue300: Color = softBlue300, + SoftBlue600: Color = softBlue600, + Sand100: Color = sand100, + Orange300: Color = orange300, + Warning50: Color = warning50, + Warning300: Color = warning300, + Success50: Color = success50, + Success300: Color = success300, + Error50: Color = error50, + Error300: Color = error300, + Gray50: Color = gray50, + Gray100: Color = gray100, + Gray200: Color = gray200, + Gray300: Color = gray300, + Gray400: Color = gray400, + Gray500: Color = gray500, + Gray600: Color = gray600, + Gray700: Color = gray700, + Gray800: Color = gray800, + Gray900: Color = gray900, + Black: Color = black, + White: Color = white, + BlackAlpha80: Color = blackAlpha80, + BlackAlpha50: Color = blackAlpha50, + WhiteAlpha50: Color = whiteAlpha50, + WhiteAlpha10: Color = whiteAlpha10, + Primary300Alpha10: Color = primary300Alpha10, + Primary300Alpha20: Color = primary300Alpha20, + Primary50Alpha50: Color = primary50Alpha50, + Secondary300Alpha30: Color = secondary300Alpha30, + Secondary300Alpha10: Color = secondary300Alpha10, + Gray900Alpha80: Color = gray900Alpha80, + KakaoYellow: Color = kakaoYellow +) = SpotColors( + Primary50, + Primary100, + Primary200, + Primary300, + Primary400, + Primary500, + Primary600, + Primary700, + Primary800, + Primary900, + Secondary50, + Secondary100, + Secondary300, + OliveGreen300, + OliveGreen700, + SoftBlue200, + SoftBlue300, + SoftBlue600, + Sand100, + Orange300, + Warning50, + Warning300, + Success50, + Success300, + Error50, + Error300, + Gray50, + Gray100, + Gray200, + Gray300, + Gray400, + Gray500, + Gray600, + Gray700, + Gray800, + Gray900, + Black, + White, + BlackAlpha80, + BlackAlpha50, + WhiteAlpha50, + WhiteAlpha10, + Primary300Alpha10, + Primary300Alpha20, + Primary50Alpha50, + Secondary300Alpha30, + Secondary300Alpha10, + Gray900Alpha80, + KakaoYellow, + isLight = true +) diff --git a/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Theme.kt b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Theme.kt new file mode 100644 index 00000000..86ea9259 --- /dev/null +++ b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Theme.kt @@ -0,0 +1,71 @@ +package com.umcspot.spot.designsystem.theme + + +import android.app.Activity +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +private val LocalSpotColors = + staticCompositionLocalOf { error("No colors provided") } +private val LocalSpotTypography = + staticCompositionLocalOf { error("No typography provided") } + +object SpotTheme { + val colors: SpotColors + @Composable + @ReadOnlyComposable + get() = LocalSpotColors.current + + val typography: SpotTypography + @Composable + @ReadOnlyComposable + get() = LocalSpotTypography.current +} + +@Composable +fun ProvideSpotColorsAndTypography( + colors: SpotColors, + typography: SpotTypography, + content: @Composable () -> Unit +) { + val provideColors = remember { colors.copy() } + provideColors.update(colors) + val provideTypography = remember { typography.copy() } + provideTypography.update(typography) + + CompositionLocalProvider( + LocalSpotColors provides provideColors, + LocalSpotTypography provides provideTypography, + content = content + ) +} + +@Composable +fun SpotTheme( + darkTheme: Boolean = false, + content: @Composable () -> Unit +) { + val colors = SpotDayColors() + val typography = SpotTypography() + + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = true + WindowCompat.getInsetsController(window, view).isAppearanceLightNavigationBars = true + } + } + ProvideSpotColorsAndTypography(colors, typography) { + MaterialTheme( + content = content + ) + } +} diff --git a/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/TypoGraphy.kt b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/TypoGraphy.kt new file mode 100644 index 00000000..1735bd70 --- /dev/null +++ b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/TypoGraphy.kt @@ -0,0 +1,207 @@ +package com.umcspot.spot.designsystem.theme + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.LineHeightStyle +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.sp +import com.umcspot.spot.designsystem.R + +val pretendardSemiBold = FontFamily(Font(R.font.pretendard_semibold, FontWeight.SemiBold)) +val pretendardMedium = FontFamily(Font(R.font.pretendard_medium, FontWeight.Medium)) +val pretendardRegular = FontFamily(Font(R.font.pretendard_regular, FontWeight.Normal)) + +private fun SpotTextStyle( + fontFamily: FontFamily, + fontSize: TextUnit, + lineHeight: TextUnit, + letterSpacing: TextUnit +): TextStyle = TextStyle( + fontFamily = fontFamily, + fontSize = fontSize, + lineHeight = lineHeight, + letterSpacing = letterSpacing, + lineHeightStyle = LineHeightStyle( + alignment = LineHeightStyle.Alignment.Center, + trim = LineHeightStyle.Trim.None + ) +) + +@Stable +class SpotTypography internal constructor( + head1: TextStyle, + head2: TextStyle, + sub1: TextStyle, + sub2: TextStyle, + sub3: TextStyle, + sub4: TextStyle, + body1: TextStyle, + body2: TextStyle, + body3: TextStyle, + body4: TextStyle, + body5: TextStyle, + body6: TextStyle, + cap1: TextStyle, + cap2: TextStyle +) { + var head1 by mutableStateOf(head1) + private set + var head2 by mutableStateOf(head2) + private set + var sub1 by mutableStateOf(sub1) + private set + var sub2 by mutableStateOf(sub2) + private set + var sub3 by mutableStateOf(sub3) + private set + var sub4 by mutableStateOf(sub4) + private set + var body1 by mutableStateOf(body1) + private set + var body2 by mutableStateOf(body2) + private set + var body3 by mutableStateOf(body3) + private set + var body4 by mutableStateOf(body4) + private set + var body5 by mutableStateOf(body5) + private set + var body6 by mutableStateOf(body6) + private set + var cap1 by mutableStateOf(cap1) + private set + var cap2 by mutableStateOf(cap2) + private set + + fun copy(): SpotTypography = SpotTypography( + head1 = head1, + head2 = head2, + sub1 = sub1, + sub2 = sub2, + sub3 = sub3, + sub4 = sub4, + body1 = body1, + body2 = body2, + body3 = body3, + body4 = body4, + body5 = body5, + body6 = body6, + cap1 = cap1, + cap2 = cap2 + ) + + fun update(typography: SpotTypography) { + head1 = typography.head1 + head2 = typography.head2 + sub1 = typography.sub1 + sub2 = typography.sub2 + sub3 = typography.sub3 + sub4 = typography.sub4 + body1 = typography.body1 + body2 = typography.body2 + body3 = typography.body3 + body4 = typography.body4 + body5 = typography.body5 + body6 = typography.body6 + cap1 = typography.cap1 + cap2 = typography.cap2 + } +} + +@Composable +fun SpotTypography(): SpotTypography { + return SpotTypography( + head1 = SpotTextStyle( + fontFamily = pretendardMedium, + fontSize = 24.sp, + lineHeight = 31.sp, + letterSpacing = 0.em + ), + head2 = SpotTextStyle( + fontFamily = pretendardMedium, + fontSize = 22.sp, + lineHeight = 26.sp, + letterSpacing = 0.em + ), + sub1 = SpotTextStyle( + fontFamily = pretendardSemiBold, + fontSize = 20.sp, + lineHeight = 24.sp, + letterSpacing = 0.em + ), + sub2 = SpotTextStyle( + fontFamily = pretendardSemiBold, + fontSize = 18.sp, + lineHeight = 22.sp, + letterSpacing = 0.em + ), + sub3 = SpotTextStyle( + fontFamily = pretendardMedium, + fontSize = 18.sp, + lineHeight = 22.sp, + letterSpacing = 0.em + ), + sub4 = SpotTextStyle( + fontFamily = pretendardRegular, + fontSize = 18.sp, + lineHeight = 22.sp, + letterSpacing = 0.em + ), + body1 = SpotTextStyle( + fontFamily = pretendardSemiBold, + fontSize = 16.sp, + lineHeight = 21.sp, + letterSpacing = 0.em + ), + body2 = SpotTextStyle( + fontFamily = pretendardMedium, + fontSize = 16.sp, + lineHeight = 21.sp, + letterSpacing = 0.em + ), + body3 = SpotTextStyle( + fontFamily = pretendardRegular, + fontSize = 16.sp, + lineHeight = 21.sp, + letterSpacing = 0.em + ), + body4 = SpotTextStyle( + fontFamily = pretendardSemiBold, + fontSize = 14.sp, + lineHeight = 18.sp, + letterSpacing = 0.em + ), + body5 = SpotTextStyle( + fontFamily = pretendardMedium, + fontSize = 14.sp, + lineHeight = 18.sp, + letterSpacing = 0.em + ), + body6 = SpotTextStyle( + fontFamily = pretendardRegular, + fontSize = 14.sp, + lineHeight = 18.sp, + letterSpacing = 0.em + ), + cap1 = SpotTextStyle( + fontFamily = pretendardMedium, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.em + ), + cap2 = SpotTextStyle( + fontFamily = pretendardRegular, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.em + ) + ) +} diff --git a/core/designsystem/src/main/res/drawable/ic_home.xml b/core/designsystem/src/main/res/drawable/ic_home.xml new file mode 100644 index 00000000..cde30daf --- /dev/null +++ b/core/designsystem/src/main/res/drawable/ic_home.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/core/designsystem/src/main/res/drawable/ic_user.xml b/core/designsystem/src/main/res/drawable/ic_user.xml new file mode 100644 index 00000000..43fa8103 --- /dev/null +++ b/core/designsystem/src/main/res/drawable/ic_user.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/core/designsystem/src/main/res/font/pretendard_medium.otf b/core/designsystem/src/main/res/font/pretendard_medium.otf new file mode 100644 index 00000000..05750698 Binary files /dev/null and b/core/designsystem/src/main/res/font/pretendard_medium.otf differ diff --git a/core/designsystem/src/main/res/font/pretendard_regular.otf b/core/designsystem/src/main/res/font/pretendard_regular.otf new file mode 100644 index 00000000..08bf4cfc Binary files /dev/null and b/core/designsystem/src/main/res/font/pretendard_regular.otf differ diff --git a/core/designsystem/src/main/res/font/pretendard_semibold.otf b/core/designsystem/src/main/res/font/pretendard_semibold.otf new file mode 100644 index 00000000..e7e36abc Binary files /dev/null and b/core/designsystem/src/main/res/font/pretendard_semibold.otf differ diff --git a/core/model/.gitignore b/core/model/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/model/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts new file mode 100644 index 00000000..0b14d571 --- /dev/null +++ b/core/model/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + alias(libs.plugins.spot.android.java.library) + alias(libs.plugins.kotlin.serialization) +} +dependencies { + implementation(libs.kotlinx.serialization.json) +} \ No newline at end of file diff --git a/core/model/src/main/java/com/umcspot/spot/model/CoreDummy.kt b/core/model/src/main/java/com/umcspot/spot/model/CoreDummy.kt new file mode 100644 index 00000000..72d866fe --- /dev/null +++ b/core/model/src/main/java/com/umcspot/spot/model/CoreDummy.kt @@ -0,0 +1,5 @@ +package com.umcspot.spot.model + +enum class CoreDummy( + val coreDummy: String +) \ No newline at end of file diff --git a/core/navigation/.gitignore b/core/navigation/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/navigation/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts new file mode 100644 index 00000000..213673a6 --- /dev/null +++ b/core/navigation/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + alias(libs.plugins.spot.android.library) +} + +android { + namespace = "com.umcspot.spot.navigation" +} \ No newline at end of file diff --git a/core/navigation/consumer-rules.pro b/core/navigation/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/core/navigation/proguard-rules.pro b/core/navigation/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/core/navigation/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/core/navigation/src/main/AndroidManifest.xml b/core/navigation/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/navigation/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/navigation/src/main/java/com/umcspot/spot/navigation/MainTabRoute.kt b/core/navigation/src/main/java/com/umcspot/spot/navigation/MainTabRoute.kt new file mode 100644 index 00000000..9ae0d96a --- /dev/null +++ b/core/navigation/src/main/java/com/umcspot/spot/navigation/MainTabRoute.kt @@ -0,0 +1,3 @@ +package com.umcspot.spot.navigation + +interface MainTabRoute : Route \ No newline at end of file diff --git a/core/navigation/src/main/java/com/umcspot/spot/navigation/Route.kt b/core/navigation/src/main/java/com/umcspot/spot/navigation/Route.kt new file mode 100644 index 00000000..db143b3d --- /dev/null +++ b/core/navigation/src/main/java/com/umcspot/spot/navigation/Route.kt @@ -0,0 +1,3 @@ +package com.umcspot.spot.navigation + +interface Route \ No newline at end of file diff --git a/core/network/.gitignore b/core/network/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/network/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts new file mode 100644 index 00000000..4e05fdbb --- /dev/null +++ b/core/network/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + alias(libs.plugins.spot.android.library) + alias(libs.plugins.spot.android.hilt) + alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.spot.android.test) +} + +android { + namespace = "com.umcspot.spot.network" +} +dependencies { + implementation(libs.bundles.datastore) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(libs.kotlinx.serialization.json) + implementation(libs.retrofit.core) + implementation(libs.retrofit.kotlin.serialization) + implementation(libs.okhttp.logging) + implementation(libs.process.phoenix) +} \ No newline at end of file diff --git a/core/network/consumer-rules.pro b/core/network/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/core/network/proguard-rules.pro b/core/network/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/core/network/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/core/network/src/main/AndroidManifest.xml b/core/network/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/network/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/network/src/main/java/com/umcspot/spot/network/di/NetworkModule.kt b/core/network/src/main/java/com/umcspot/spot/network/di/NetworkModule.kt new file mode 100644 index 00000000..730a7247 --- /dev/null +++ b/core/network/src/main/java/com/umcspot/spot/network/di/NetworkModule.kt @@ -0,0 +1,54 @@ +package com.umcspot.spot.network.di + +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import com.umcspot.spot.common.BuildConfigFieldProvider +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Converter +import retrofit2.Retrofit + +@Module +@InstallIn(SingletonComponent::class) +object NetworkModule { + @Provides + @Singleton + fun providesLoggingInterceptor() = + HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + + @Provides + @Singleton + fun providesOkHttpClient(loggingInterceptor: HttpLoggingInterceptor): OkHttpClient = + OkHttpClient.Builder() + .addInterceptor(loggingInterceptor) + .build() + + @OptIn(ExperimentalSerializationApi::class) + @Provides + @Singleton + fun providesConverterFactory(): Converter.Factory = Json.asConverterFactory( + "application/json".toMediaType() + ) + + @Provides + @Singleton + fun providesRetrofit( + client: OkHttpClient, + converterFactory: Converter.Factory, + buildConfigProvider: BuildConfigFieldProvider + ): Retrofit = + Retrofit.Builder() + .baseUrl(buildConfigProvider.get().baseUrl) + .client(client) + .addConverterFactory(converterFactory) + .build() +} \ No newline at end of file diff --git a/core/network/src/main/java/com/umcspot/spot/network/model/BaseResponse.kt b/core/network/src/main/java/com/umcspot/spot/network/model/BaseResponse.kt new file mode 100644 index 00000000..a13dd6d7 --- /dev/null +++ b/core/network/src/main/java/com/umcspot/spot/network/model/BaseResponse.kt @@ -0,0 +1,14 @@ +package network.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class BaseResponse( + @SerialName("success") + val success: Boolean, + @SerialName("message") + val message: String, + @SerialName("data") + val data: T +) \ No newline at end of file diff --git a/core/network/src/main/java/com/umcspot/spot/network/service/TokenRefreshService.kt b/core/network/src/main/java/com/umcspot/spot/network/service/TokenRefreshService.kt new file mode 100644 index 00000000..6c0421ed --- /dev/null +++ b/core/network/src/main/java/com/umcspot/spot/network/service/TokenRefreshService.kt @@ -0,0 +1,4 @@ +package network.service + +class TokenRefreshService { +} \ No newline at end of file diff --git a/core/ui/.gitignore b/core/ui/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/ui/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts new file mode 100644 index 00000000..83674fcc --- /dev/null +++ b/core/ui/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + alias(libs.plugins.spot.android.compose.library) +} + +android { + namespace = "com.umcspot.spot.ui" +} + +dependencies { + androidTestImplementation(libs.bundles.coil) +} \ No newline at end of file diff --git a/core/ui/consumer-rules.pro b/core/ui/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/core/ui/proguard-rules.pro b/core/ui/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/core/ui/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/core/ui/src/main/AndroidManifest.xml b/core/ui/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/ui/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/ui/src/main/java/com/umcspot/spot/ui/extension/DimensionExt.kt b/core/ui/src/main/java/com/umcspot/spot/ui/extension/DimensionExt.kt new file mode 100644 index 00000000..8e938c85 --- /dev/null +++ b/core/ui/src/main/java/com/umcspot/spot/ui/extension/DimensionExt.kt @@ -0,0 +1,23 @@ +package com.umcspot.spot.ui.extension + +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +private val figmaScreenWidth = 360.dp +private val figmaScreenHeight = 800.dp + +@Composable +fun screenHeightDp(height: Dp): Dp { + val screenHeight = LocalConfiguration.current.screenHeightDp.dp + val ratio = screenHeight / figmaScreenHeight + return height * ratio +} + +@Composable +fun screenWidthDp(width: Dp): Dp { + val screenWidth = LocalConfiguration.current.screenWidthDp.dp + val ratio = screenWidth / figmaScreenWidth + return width * ratio +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/umcspot/spot/ui/extension/ModifierExt.kt b/core/ui/src/main/java/com/umcspot/spot/ui/extension/ModifierExt.kt new file mode 100644 index 00000000..3b757fa8 --- /dev/null +++ b/core/ui/src/main/java/com/umcspot/spot/ui/extension/ModifierExt.kt @@ -0,0 +1,32 @@ +package com.umcspot.spot.ui.extension + +import android.annotation.SuppressLint +import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed + +@SuppressLint("ModifierFactoryUnreferencedReceiver") +inline fun androidx.compose.ui.Modifier.noRippleClickable(crossinline onClick: () -> Unit = {}): Modifier = + composed { + this.clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { + onClick() + } + } + +inline fun Modifier.noRippleCombineClickable( + crossinline onClick: () -> Unit = {}, + crossinline onLongClick: () -> Unit +): Modifier = composed { + combinedClickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = { onClick() }, + onLongClick = { onLongClick() } + ) +} diff --git a/core/ui/src/main/java/com/umcspot/spot/ui/state/UiState.kt b/core/ui/src/main/java/com/umcspot/spot/ui/state/UiState.kt new file mode 100644 index 00000000..ff1eebce --- /dev/null +++ b/core/ui/src/main/java/com/umcspot/spot/ui/state/UiState.kt @@ -0,0 +1,15 @@ +package com.umcspot.spot.ui.state + +sealed interface UiState { + data object Empty : UiState + + data object Loading : UiState + + data class Success( + val data: T + ) : UiState + + data class Failure( + val msg: String + ) : UiState +} \ No newline at end of file diff --git a/data/home/.gitignore b/data/home/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/data/home/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/data/home/build.gradle.kts b/data/home/build.gradle.kts new file mode 100644 index 00000000..05197446 --- /dev/null +++ b/data/home/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + alias(libs.plugins.spot.data) +} + +android { + namespace = "com.umcspot.spot.home" +} +dependencies { + implementation(projects.domain.home) +} \ No newline at end of file diff --git a/data/home/consumer-rules.pro b/data/home/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/data/home/proguard-rules.pro b/data/home/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/data/home/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/data/home/src/main/AndroidManifest.xml b/data/home/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/data/home/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/data/home/src/main/java/com/umcspot/spot/home/datasource/HomeDataSource.kt b/data/home/src/main/java/com/umcspot/spot/home/datasource/HomeDataSource.kt new file mode 100644 index 00000000..d06b2254 --- /dev/null +++ b/data/home/src/main/java/com/umcspot/spot/home/datasource/HomeDataSource.kt @@ -0,0 +1,9 @@ +package com.umcspot.spot.home.datasource + +import com.umcspot.spot.home.dto.request.HomeRequestDto +import com.umcspot.spot.home.dto.response.HomeResponseDto +import network.model.BaseResponse + +interface HomeDataSource { + suspend fun getDummies(request: HomeRequestDto): BaseResponse +} \ No newline at end of file diff --git a/data/home/src/main/java/com/umcspot/spot/home/datasourceimpl/HomeDataSourceImpl.kt b/data/home/src/main/java/com/umcspot/spot/home/datasourceimpl/HomeDataSourceImpl.kt new file mode 100644 index 00000000..a087b289 --- /dev/null +++ b/data/home/src/main/java/com/umcspot/spot/home/datasourceimpl/HomeDataSourceImpl.kt @@ -0,0 +1,18 @@ +package com.umcspot.spot.home.datasourceimpl + +import com.umcspot.spot.home.datasource.HomeDataSource +import com.umcspot.spot.home.dto.request.HomeRequestDto +import com.umcspot.spot.home.dto.response.HomeResponseDto +import com.umcspot.spot.home.service.HomeService +import network.model.BaseResponse +import javax.inject.Inject + + +class HomeDataSourceImpl @Inject constructor( + private val homeService: HomeService +) : HomeDataSource { + override suspend fun getDummies( + request: HomeRequestDto + ): BaseResponse = + homeService.getDummies(request) +} \ No newline at end of file diff --git a/data/home/src/main/java/com/umcspot/spot/home/di/HomeDataModule.kt b/data/home/src/main/java/com/umcspot/spot/home/di/HomeDataModule.kt new file mode 100644 index 00000000..61a51d89 --- /dev/null +++ b/data/home/src/main/java/com/umcspot/spot/home/di/HomeDataModule.kt @@ -0,0 +1,18 @@ +package com.umcspot.spot.home.di + +import com.umcspot.spot.home.datasource.HomeDataSource +import com.umcspot.spot.home.datasourceimpl.HomeDataSourceImpl +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + + +@Module +@InstallIn(SingletonComponent::class) +abstract class HomeDataModule { + @Binds + @Singleton + abstract fun bindDummyRemoteDataSource(impl: HomeDataSourceImpl): HomeDataSource +} \ No newline at end of file diff --git a/data/home/src/main/java/com/umcspot/spot/home/di/HomeRepositoryModule.kt b/data/home/src/main/java/com/umcspot/spot/home/di/HomeRepositoryModule.kt new file mode 100644 index 00000000..bba43bfe --- /dev/null +++ b/data/home/src/main/java/com/umcspot/spot/home/di/HomeRepositoryModule.kt @@ -0,0 +1,18 @@ +package com.umcspot.spot.home.di + + +import com.umcspot.spot.home.repository.HomeRepository +import com.umcspot.spot.home.repositoryimpl.HomeRepositoryImpl +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +abstract class HomeRepositoryModule { + @Binds + @Singleton + abstract fun bindsDummyRepository(dummyRepositoryImpl: HomeRepositoryImpl): HomeRepository +} \ No newline at end of file diff --git a/data/home/src/main/java/com/umcspot/spot/home/di/HomeServiceModule.kt b/data/home/src/main/java/com/umcspot/spot/home/di/HomeServiceModule.kt new file mode 100644 index 00000000..71a60c30 --- /dev/null +++ b/data/home/src/main/java/com/umcspot/spot/home/di/HomeServiceModule.kt @@ -0,0 +1,19 @@ +package com.umcspot.spot.home.di + +import com.umcspot.spot.home.service.HomeService +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import retrofit2.Retrofit +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object HomeServiceModule { + @Provides + @Singleton + fun providesDummyService(retrofit: Retrofit): HomeService = retrofit.create( + HomeService::class.java + ) +} \ No newline at end of file diff --git a/data/home/src/main/java/com/umcspot/spot/home/dto/request/HomeRequestDto.kt b/data/home/src/main/java/com/umcspot/spot/home/dto/request/HomeRequestDto.kt new file mode 100644 index 00000000..a996e217 --- /dev/null +++ b/data/home/src/main/java/com/umcspot/spot/home/dto/request/HomeRequestDto.kt @@ -0,0 +1,12 @@ +package com.umcspot.spot.home.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class HomeRequestDto( + @SerialName("id") + val id: Int, + @SerialName("email") + val email: String +) diff --git a/data/home/src/main/java/com/umcspot/spot/home/dto/response/HomeResponseDto.kt b/data/home/src/main/java/com/umcspot/spot/home/dto/response/HomeResponseDto.kt new file mode 100644 index 00000000..6ff3d08c --- /dev/null +++ b/data/home/src/main/java/com/umcspot/spot/home/dto/response/HomeResponseDto.kt @@ -0,0 +1,10 @@ +package com.umcspot.spot.home.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class HomeResponseDto( + @SerialName("info") + val info: List +) \ No newline at end of file diff --git a/data/home/src/main/java/com/umcspot/spot/home/mapper/HomeMapper.kt b/data/home/src/main/java/com/umcspot/spot/home/mapper/HomeMapper.kt new file mode 100644 index 00000000..769bb7de --- /dev/null +++ b/data/home/src/main/java/com/umcspot/spot/home/mapper/HomeMapper.kt @@ -0,0 +1,18 @@ +package com.umcspot.spot.home.mapper + + +import com.umcspot.spot.home.dto.request.HomeRequestDto +import com.umcspot.spot.home.dto.response.HomeResponseDto +import com.umcspot.spot.home.model.Dummy +import com.umcspot.spot.home.model.DummyResult + +fun Dummy.toData(): HomeRequestDto = + HomeRequestDto( + id = this.id, + email = this.email + ) + +fun HomeResponseDto.toDomain(): DummyResult = + DummyResult( + info = info + ) \ No newline at end of file diff --git a/data/home/src/main/java/com/umcspot/spot/home/repositoryimpl/HomeRepositoryImpl.kt b/data/home/src/main/java/com/umcspot/spot/home/repositoryimpl/HomeRepositoryImpl.kt new file mode 100644 index 00000000..0f1b05fe --- /dev/null +++ b/data/home/src/main/java/com/umcspot/spot/home/repositoryimpl/HomeRepositoryImpl.kt @@ -0,0 +1,20 @@ +package com.umcspot.spot.home.repositoryimpl + +import com.umcspot.spot.home.mapper.toData +import com.umcspot.spot.home.mapper.toDomain +import com.umcspot.spot.home.model.Dummy +import com.umcspot.spot.home.model.DummyResult +import com.umcspot.spot.home.repository.HomeRepository +import com.umcspot.spot.home.service.HomeService +import javax.inject.Inject + + +class HomeRepositoryImpl @Inject constructor( + private val homeService: HomeService +) : HomeRepository { + override suspend fun getDummies(request: Dummy): Result = + runCatching { + val response = homeService.getDummies(request = request.toData()) + response.data.toDomain() + } +} \ No newline at end of file diff --git a/data/home/src/main/java/com/umcspot/spot/home/service/HomeService.kt b/data/home/src/main/java/com/umcspot/spot/home/service/HomeService.kt new file mode 100644 index 00000000..f0f5595f --- /dev/null +++ b/data/home/src/main/java/com/umcspot/spot/home/service/HomeService.kt @@ -0,0 +1,16 @@ +package com.umcspot.spot.home.service + + +import com.umcspot.spot.home.dto.request.HomeRequestDto +import com.umcspot.spot.home.dto.response.HomeResponseDto +import network.model.BaseResponse +import retrofit2.http.Body +import retrofit2.http.POST + + +interface HomeService { + @POST("/api/v1/service") + suspend fun getDummies( + @Body request: HomeRequestDto + ): BaseResponse +} \ No newline at end of file diff --git a/domain/home/.gitignore b/domain/home/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/domain/home/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/domain/home/build.gradle.kts b/domain/home/build.gradle.kts new file mode 100644 index 00000000..14f8d7b6 --- /dev/null +++ b/domain/home/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + alias(libs.plugins.spot.android.java.library) +} +dependencies { + implementation(projects.core.model) + implementation(libs.bundles.coroutine) +} \ No newline at end of file diff --git a/domain/home/src/main/java/com/umcspot/spot/home/model/Dummy.kt b/domain/home/src/main/java/com/umcspot/spot/home/model/Dummy.kt new file mode 100644 index 00000000..03995228 --- /dev/null +++ b/domain/home/src/main/java/com/umcspot/spot/home/model/Dummy.kt @@ -0,0 +1,6 @@ +package com.umcspot.spot.home.model + +data class Dummy( + val id: Int, + val email: String +) \ No newline at end of file diff --git a/domain/home/src/main/java/com/umcspot/spot/home/model/DummyResult.kt b/domain/home/src/main/java/com/umcspot/spot/home/model/DummyResult.kt new file mode 100644 index 00000000..cc79504c --- /dev/null +++ b/domain/home/src/main/java/com/umcspot/spot/home/model/DummyResult.kt @@ -0,0 +1,5 @@ +package com.umcspot.spot.home.model + +data class DummyResult( + val info: List +) \ No newline at end of file diff --git a/domain/home/src/main/java/com/umcspot/spot/home/repository/HomeRepository.kt b/domain/home/src/main/java/com/umcspot/spot/home/repository/HomeRepository.kt new file mode 100644 index 00000000..55e99740 --- /dev/null +++ b/domain/home/src/main/java/com/umcspot/spot/home/repository/HomeRepository.kt @@ -0,0 +1,8 @@ +package com.umcspot.spot.home.repository + +import com.umcspot.spot.home.model.Dummy +import com.umcspot.spot.home.model.DummyResult + +interface HomeRepository { + suspend fun getDummies(request: Dummy): Result +} \ No newline at end of file diff --git a/feature/home/.gitignore b/feature/home/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/home/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts new file mode 100644 index 00000000..24510945 --- /dev/null +++ b/feature/home/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + alias(libs.plugins.spot.feature) +} +android { + namespace = "com.umcspot.spot.home" +} + +dependencies { + implementation(projects.domain.home) +} \ No newline at end of file diff --git a/feature/home/consumer-rules.pro b/feature/home/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/feature/home/proguard-rules.pro b/feature/home/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/home/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/home/src/main/AndroidManifest.xml b/feature/home/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/home/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/home/src/main/java/com/umcspot/spot/home/HomeScreen.kt b/feature/home/src/main/java/com/umcspot/spot/home/HomeScreen.kt new file mode 100644 index 00000000..31b2dae5 --- /dev/null +++ b/feature/home/src/main/java/com/umcspot/spot/home/HomeScreen.kt @@ -0,0 +1,48 @@ +package com.umcspot.spot.home + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.umcspot.spot.ui.state.UiState + +@Composable +fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + when (val state = uiState.user) { + is UiState.Loading -> { + Text(text = "로딩 중...", color = Color.Gray) + } + + is UiState.Success -> { + state.data.info.forEachIndexed { index, item -> + Text(text = "Info[$index]: $item") + } + } + + is UiState.Failure -> { + Text(text = "에러: ${state.msg}", color = Color.Red) + } + + UiState.Empty -> { + Text(text = "데이터가 없습니다.") + } + } + } +} \ No newline at end of file diff --git a/feature/home/src/main/java/com/umcspot/spot/home/HomeState.kt b/feature/home/src/main/java/com/umcspot/spot/home/HomeState.kt new file mode 100644 index 00000000..941fb4f4 --- /dev/null +++ b/feature/home/src/main/java/com/umcspot/spot/home/HomeState.kt @@ -0,0 +1,8 @@ +package com.umcspot.spot.home + +import com.umcspot.spot.home.model.DummyResult +import com.umcspot.spot.ui.state.UiState + +data class HomeState( + var user: UiState = UiState.Loading +) \ No newline at end of file diff --git a/feature/home/src/main/java/com/umcspot/spot/home/HomeViewModel.kt b/feature/home/src/main/java/com/umcspot/spot/home/HomeViewModel.kt new file mode 100644 index 00000000..f37a6178 --- /dev/null +++ b/feature/home/src/main/java/com/umcspot/spot/home/HomeViewModel.kt @@ -0,0 +1,45 @@ +package com.umcspot.spot.home + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.umcspot.spot.home.model.Dummy +import com.umcspot.spot.home.repository.HomeRepository +import com.umcspot.spot.ui.state.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +@HiltViewModel +class HomeViewModel +@Inject +constructor( + private val dummyRepository: HomeRepository +) : ViewModel() { + var uiState = MutableStateFlow(HomeState()) + private set + + fun getDummies( + id: Int, + email: String + ) { + viewModelScope.launch { + dummyRepository.getDummies( + request = Dummy(id = id, email = email) + ) + .onSuccess { response -> + uiState.update { + it.copy( + user = UiState.Success(response) + ) + } + } + .onFailure { e -> + uiState.update { + it.copy(user = UiState.Failure(e.message ?: "오류 발생")) + } + } + } + } +} \ No newline at end of file diff --git a/feature/home/src/main/java/com/umcspot/spot/home/navigation/HomeNavigation.kt b/feature/home/src/main/java/com/umcspot/spot/home/navigation/HomeNavigation.kt new file mode 100644 index 00000000..37d09563 --- /dev/null +++ b/feature/home/src/main/java/com/umcspot/spot/home/navigation/HomeNavigation.kt @@ -0,0 +1,23 @@ +package com.umcspot.spot.home.navigation + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.umcspot.spot.home.HomeScreen +import com.umcspot.spot.navigation.MainTabRoute +import kotlinx.serialization.Serializable + + +fun NavController.navigateToHome(navOptions: NavOptions? = null) { + navigate(Home, navOptions) +} + +fun NavGraphBuilder.homeGraph() { + composable { + HomeScreen() + } +} + +@Serializable +data object Home : MainTabRoute \ No newline at end of file diff --git a/feature/main/.gitignore b/feature/main/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/main/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts new file mode 100644 index 00000000..ad2ad365 --- /dev/null +++ b/feature/main/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + alias(libs.plugins.spot.feature) +} +android { + namespace = "com.umcspot.spot.main" +} + +dependencies { + implementation(projects.feature.home) + implementation(projects.feature.mypage) + implementation(libs.androidx.splashscreen) + implementation(libs.lottie) + implementation(libs.lottie.compose) + implementation(libs.material3.compose) + implementation(libs.androidx.constraintlayout) + implementation(libs.google.material) +} \ No newline at end of file diff --git a/feature/main/consumer-rules.pro b/feature/main/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/feature/main/proguard-rules.pro b/feature/main/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/main/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/main/src/main/AndroidManifest.xml b/feature/main/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/main/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/main/src/main/java/com/umcspot/spot/main/MainActivity.kt b/feature/main/src/main/java/com/umcspot/spot/main/MainActivity.kt new file mode 100644 index 00000000..b198799b --- /dev/null +++ b/feature/main/src/main/java/com/umcspot/spot/main/MainActivity.kt @@ -0,0 +1,26 @@ +package com.umcspot.spot.main + +import android.graphics.Color +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import com.umcspot.spot.designsystem.theme.SpotTheme +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge( + statusBarStyle = SystemBarStyle.dark(Color.TRANSPARENT), + navigationBarStyle = SystemBarStyle.dark(Color.TRANSPARENT) + ) + setContent { + SpotTheme { + MainScreen() + } + } + } +} \ No newline at end of file diff --git a/feature/main/src/main/java/com/umcspot/spot/main/MainNavHost.kt b/feature/main/src/main/java/com/umcspot/spot/main/MainNavHost.kt new file mode 100644 index 00000000..e6f66e24 --- /dev/null +++ b/feature/main/src/main/java/com/umcspot/spot/main/MainNavHost.kt @@ -0,0 +1,27 @@ +package com.umcspot.spot.main + +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.navigation.compose.NavHost +import com.umcspot.spot.home.navigation.homeGraph +import com.umcspot.spot.mypage.navigation.mypageGraph + +@Composable +fun MainNavHost( + navigator: MainNavigator, + modifier: Modifier = Modifier +) { + NavHost( + enterTransition = { EnterTransition.None }, + exitTransition = { ExitTransition.None }, + popEnterTransition = { EnterTransition.None }, + popExitTransition = { ExitTransition.None }, + navController = navigator.navController, + startDestination = navigator.startDestination + ) { + homeGraph() + mypageGraph() + } +} \ No newline at end of file diff --git a/feature/main/src/main/java/com/umcspot/spot/main/MainNavTab.kt b/feature/main/src/main/java/com/umcspot/spot/main/MainNavTab.kt new file mode 100644 index 00000000..608280e0 --- /dev/null +++ b/feature/main/src/main/java/com/umcspot/spot/main/MainNavTab.kt @@ -0,0 +1,42 @@ +package com.umcspot.spot.main + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.compose.runtime.Composable +import com.umcspot.spot.designsystem.R.drawable.ic_user +import com.umcspot.spot.designsystem.R.drawable.ic_home +import com.umcspot.spot.home.navigation.Home +import com.umcspot.spot.mypage.navigation.Mypage +import com.umcspot.spot.main.R.string.ic_home_desc +import com.umcspot.spot.main.R.string.ic_mypage_desc +import com.umcspot.spot.navigation.MainTabRoute +import com.umcspot.spot.navigation.Route + +enum class MainNavTab( + @DrawableRes val icon: Int, + @StringRes val contentDescription: Int, + val route: MainTabRoute +) { + HOME( + icon = ic_home, + contentDescription = ic_home_desc, + route = Home + ), + MYPAGE( + icon = ic_user, + contentDescription = ic_mypage_desc, + route = Mypage + ); + + companion object { + @Composable + fun find(predicate: @Composable (MainTabRoute) -> Boolean): MainNavTab? { + return entries.find { predicate(it.route) } + } + + @Composable + fun contains(predicate: @Composable (Route) -> Boolean): Boolean { + return entries.map { it.route }.any { predicate(it) } + } + } +} \ No newline at end of file diff --git a/feature/main/src/main/java/com/umcspot/spot/main/MainNavigator.kt b/feature/main/src/main/java/com/umcspot/spot/main/MainNavigator.kt new file mode 100644 index 00000000..a8b3b25c --- /dev/null +++ b/feature/main/src/main/java/com/umcspot/spot/main/MainNavigator.kt @@ -0,0 +1,65 @@ +package com.umcspot.spot.main + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.navigation.NavDestination +import androidx.navigation.NavDestination.Companion.hasRoute +import androidx.navigation.NavHostController +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navOptions +import com.umcspot.spot.home.navigation.Home +import com.umcspot.spot.home.navigation.navigateToHome +import com.umcspot.spot.mypage.navigation.navigateToMypage + +class MainNavigator( + val navController: NavHostController +) { + private val currentDestination: NavDestination? + @Composable get() = + navController + .currentBackStackEntryAsState().value?.destination + + val startDestination = Home + + val currentTab: MainNavTab? + @Composable get() = + MainNavTab.find { tab -> + currentDestination?.hasRoute(tab::class) == true + } + + fun navigate(tab: MainNavTab) { + val navOptions = + navOptions { + navController.currentDestination?.route?.let { + popUpTo(it) { + inclusive = true + saveState = true + } + } + launchSingleTop = true + restoreState = true + } + when (tab) { + MainNavTab.HOME -> navController.navigateToHome(navOptions) + MainNavTab.MYPAGE -> navController.navigateToMypage(navOptions) + } + } + + @Composable + fun showBottomBar() = + MainNavTab.contains { + currentDestination?.hasRoute(it::class) == true + } + + fun navigateUp() { + navController.navigateUp() + } +} + +@Composable +fun rememberMainNavigator( + navController: NavHostController = rememberNavController() +): MainNavigator = remember(navController) { + MainNavigator(navController) +} \ No newline at end of file diff --git a/feature/main/src/main/java/com/umcspot/spot/main/MainScreen.kt b/feature/main/src/main/java/com/umcspot/spot/main/MainScreen.kt new file mode 100644 index 00000000..d2752ac0 --- /dev/null +++ b/feature/main/src/main/java/com/umcspot/spot/main/MainScreen.kt @@ -0,0 +1,36 @@ +package com.umcspot.spot.main + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import com.umcspot.spot.main.component.MainBottomBar +import kotlinx.collections.immutable.toImmutableList + +@Composable +fun MainScreen(navigator: MainNavigator = rememberMainNavigator()) { + Scaffold( + bottomBar = { + MainBottomBar( + visible = navigator.showBottomBar(), + tabs = MainNavTab.entries.toImmutableList(), + currentTab = navigator.currentTab, + onTabSelected = navigator::navigate + ) + }, + modifier = + Modifier + .background(Color.White) + .systemBarsPadding() + .fillMaxSize() + ) { innerPadding -> + MainNavHost( + navigator = navigator, + modifier = Modifier.padding(innerPadding) + ) + } +} \ No newline at end of file diff --git a/feature/main/src/main/java/com/umcspot/spot/main/component/MainBottomBar.kt b/feature/main/src/main/java/com/umcspot/spot/main/component/MainBottomBar.kt new file mode 100644 index 00000000..80cf7c99 --- /dev/null +++ b/feature/main/src/main/java/com/umcspot/spot/main/component/MainBottomBar.kt @@ -0,0 +1,105 @@ +package com.umcspot.spot.main.component + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.key +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.umcspot.spot.designsystem.theme.SpotTheme +import com.umcspot.spot.designsystem.theme.black +import com.umcspot.spot.designsystem.theme.success300 +import com.umcspot.spot.main.MainNavTab +import com.umcspot.spot.ui.extension.noRippleClickable +import com.umcspot.spot.ui.extension.screenHeightDp +import kotlinx.collections.immutable.ImmutableList + +@Composable +fun MainBottomBar( + visible: Boolean, + tabs: ImmutableList, + currentTab: MainNavTab?, + onTabSelected: (MainNavTab) -> Unit +) { + AnimatedVisibility( + visible = visible, + enter = EnterTransition.None, + exit = ExitTransition.None + ) { + Column( + modifier = Modifier.background(SpotTheme.colors.white) + ) { + HorizontalDivider( + thickness = 1.dp, + color = SpotTheme.colors.gray300 + ) + Row( + modifier = Modifier + .navigationBarsPadding() + .fillMaxWidth() + .padding(vertical = screenHeightDp(10.dp)), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly + ) { + tabs.forEach { tab -> + key(tab.route) { + val selected = currentTab == tab + MainBottomBarItem( + tab = tab, + selected = selected, + onClick = { onTabSelected(tab) } + ) + } + } + } + } + } +} + +@Composable +fun RowScope.MainBottomBarItem( + modifier: Modifier = Modifier, + tab: MainNavTab, + selected: Boolean, + onClick: () -> Unit +) { + val bottomItemColor = if (selected) success300 else black + val bottomTextStyle = if (selected) SpotTheme.typography.body4 else SpotTheme.typography.body6 + + Column( + modifier = modifier + .noRippleClickable(onClick = onClick) + .weight(1f), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(1.dp) + ) { + Icon( + imageVector = ImageVector.vectorResource(tab.icon), + contentDescription = stringResource(tab.contentDescription), + tint = bottomItemColor + ) + Text( + text = stringResource(tab.contentDescription), + fontSize = 14.sp, + style = bottomTextStyle, + color = bottomItemColor + ) + } +} diff --git a/feature/main/src/main/res/values/strings.xml b/feature/main/src/main/res/values/strings.xml new file mode 100644 index 00000000..92f79e6e --- /dev/null +++ b/feature/main/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + + 마이페이지 + \ No newline at end of file diff --git a/feature/mypage/.gitignore b/feature/mypage/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/mypage/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/mypage/build.gradle.kts b/feature/mypage/build.gradle.kts new file mode 100644 index 00000000..cde42ceb --- /dev/null +++ b/feature/mypage/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + alias(libs.plugins.spot.feature) +} + +android { + namespace = "com.umcspot.spot.mypage" +} \ No newline at end of file diff --git a/feature/mypage/consumer-rules.pro b/feature/mypage/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/feature/mypage/proguard-rules.pro b/feature/mypage/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/mypage/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/mypage/src/main/AndroidManifest.xml b/feature/mypage/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/mypage/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/mypage/src/main/java/com/umcspot/spot/mypage/MypageScreen.kt b/feature/mypage/src/main/java/com/umcspot/spot/mypage/MypageScreen.kt new file mode 100644 index 00000000..8fdb8b9a --- /dev/null +++ b/feature/mypage/src/main/java/com/umcspot/spot/mypage/MypageScreen.kt @@ -0,0 +1,27 @@ +package com.umcspot.spot.mypage + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color + +@Composable +fun MypageScreen() { + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = "mypage Screen", + color = Color.Black + ) + } +} \ No newline at end of file diff --git a/feature/mypage/src/main/java/com/umcspot/spot/mypage/navigation/MypageNavigation.kt b/feature/mypage/src/main/java/com/umcspot/spot/mypage/navigation/MypageNavigation.kt new file mode 100644 index 00000000..976361a4 --- /dev/null +++ b/feature/mypage/src/main/java/com/umcspot/spot/mypage/navigation/MypageNavigation.kt @@ -0,0 +1,22 @@ +package com.umcspot.spot.mypage.navigation + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.umcspot.spot.mypage.MypageScreen +import com.umcspot.spot.navigation.MainTabRoute +import kotlinx.serialization.Serializable + +fun NavController.navigateToMypage(navOptions: NavOptions? = null) { + navigate(Mypage, navOptions) +} + +fun NavGraphBuilder.mypageGraph() { + composable { + MypageScreen() + } +} + +@Serializable +data object Mypage : MainTabRoute \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fbf747f8..8e33a70e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,32 +1,271 @@ [versions] -agp = "8.9.1" -kotlin = "2.0.21" -coreKtx = "1.17.0" +# SDK Versions +accompanistInsets = "0.24.13-rc" +accompanistPermissions = "0.34.0" +awsAndroidSdkMobileClient = "2.13.5" +compileSdk = "35" +lightcompressor = "1.3.2" +minSdk = "28" +targetSdk = "35" +jdkVersion = "17" + +# App Versioning +versionName = "1.0.0" +versionCode = "1" + +# Android Plugin Versions +android-gradle-plugin = "8.6.0" +ksp = "2.0.0-1.0.22" + +# Formatting Plugin Versions +ktlint = "11.6.1" +detekt = "1.23.6" + +# Kotlin Versions +kotlin = "2.0.0" +kotlinx-coroutines = "1.8.1" +kotlinx-serialization-json = "1.8.0" +kotlinx-datetime = "0.6.0" +kotlinx-collections = "0.3.7" + +# Test Libraries Versions junit = "4.13.2" -junitVersion = "1.3.0" -espressoCore = "3.7.0" -lifecycleRuntimeKtx = "2.9.2" -activityCompose = "1.10.1" -composeBom = "2024.09.00" +mockito = "5.12.0" +robolectric = "4.12.2" +androidx-test-ext-junit = "1.2.1" +androidx-test-runner = "1.6.1" +androidx-test = "1.6.1" +espresso-core = "3.6.1" -[libraries] -androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } -junit = { group = "junit", name = "junit", version.ref = "junit" } -androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } -androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } -androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } -androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } -androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } -androidx-ui = { group = "androidx.compose.ui", name = "ui" } -androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } -androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } -androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } -androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } -androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } -androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +# AndroidX Libraries Versions +androidx-app-compat = "1.7.0" +androidx-core = "1.13.1" +androidx-lifecycle = "2.8.3" +androidx-navigation = "2.8.2" +androidx-constraint-layout = "2.1.4" +androidx-activity-ktx = "1.9.0" +androidx-fragment-ktx = "1.8.1" +androidx-recycler-view = "1.3.2" +androidx-splash-screen = "1.0.1" +androidx-datastore = "1.1.1" +androidx-paging = "3.3.0" +androidx-paging-compose = "3.3.0" +androidx-media3 = "1.3.1" +androidx-splashscreen = "1.0.1" +androix-room = "2.6.1" +androidx-hilt-navigation-compose = "1.2.0" +androidx-workmanager = "2.9.0" + +# Google Libraries Versions +google-service = "4.4.2" +material = "1.12.0" +firebase-bom = "33.1.1" +crashlytics = "3.0.2" +google-location = "21.3.0" + +# Naver Maps Versions +naverMapCompose = "1.8.2" +naverMapLocation = "21.0.2" +naverMapSdk = "3.22.0" + +# Compose Versions +compose-compiler = "1.5.1" +compose-bom = "2025.06.01" +compose-material3 = "1.2.0-alpha07" +activity-compose = "1.8.2" +compose-stable-marker = "1.0.3" + +# Hilt Versions +hilt = "2.51.1" +hilt-androidx = "1.2.0" + +# Other Libraries Versions +javax = "1" +retrofit = "2.11.0" +retrofit-kotlinx-serialization-json = "1.0.0" +okhttp = "4.12.0" +orbit = "8.0.0" +timber = "5.0.1" +coil = "2.7.0" +lottie = "6.4.1" +jsoup = "1.17.2" +kakao-login = "2.19.0" +process-pheonix = "3.0.0" +preference = "1.2.1" +collapsing-toolbar = "2.3.5" +runtimeAndroid = "1.7.8" +flexibleBottomSheet = "0.1.5" [plugins] -android-application = { id = "com.android.application", version.ref = "agp" } +# Gradle Plugins +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } +android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" } +android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" } +hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +google-service = { id = "com.google.gms.google-services", version.ref = "google-service" } +navigation-safeargs = { id = "androidx.navigation.safeargs.kotlin", version.ref = "androidx-navigation" } +android-test = { id = "com.android.test", version.ref = "android-gradle-plugin" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } + +# Custom Plugins +spot-android-application = { id = "spot.android.application", version = "unspecified" } +spot-android-build-config = { id = "spot.android.build.config", version = "unspecified" } +spot-android-library = { id = "spot.android.library", version = "unspecified" } +spot-android-compose-library = { id = "spot.android.compose.library", version = "unspecified" } +spot-android-java-library = { id = "spot.android.java.library", version = "unspecified" } +spot-android-test = { id = "spot.android.test", version = "unspecified" } +spot-android-unitTest = { id = "spot.android.unittest", version = "unspecified" } +spot-android-hilt = { id = "spot.android.hilt", version = "unspecified" } +spot-feature = { id = "spot.feature", version = "unspecified" } +spot-data = { id = "spot.data", version = "unspecified"} + +[libraries] +# Android Gradle Plugin +android-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "android-gradle-plugin" } +kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } +ksp-gradle-plugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } + +# Kotlin Extensions and Libraries +kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } +kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } +kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" } +kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" } +kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinx-datetime" } +kotlinx-collections = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinx-collections" } + +# Testing Libraries +junit = { group = "junit", name = "junit", version.ref = "junit" } +lightcompressor = { module = "com.github.AbedElazizShe:LightCompressor", version.ref = "lightcompressor" } +mockito = { group = "org.mockito", name = "mockito-core", version.ref = "mockito" } +robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } +androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } +androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidx-test-runner" } +androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidx-test" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } + +# AndroidX Libraries +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-app-compat" } +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraint-layout" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" } +androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" } +androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "androidx-activity-ktx" } +androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "androidx-fragment-ktx" } +androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "androidx-recycler-view" } +androidx-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidx-splash-screen" } +androidx-paging = { group = "androidx.paging", name = "paging-runtime", version.ref = "androidx-paging" } +androidx-paging-common = { group = "androidx.paging", name = "paging-common", version.ref = "androidx-paging" } +androidx-paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "androidx-paging-compose" } +androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "androidx-media3" } +androidx-media3-exoplayer-dash = { group = "androidx.media3", name = "media3-exoplayer-dash", version.ref = "androidx-media3" } +androidx-media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "androidx-media3" } +navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "androidx-navigation" } +navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "androidx-navigation" } +androidx-datastore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidx-datastore" } +androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "androidx-datastore" } +androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "androix-room" } +androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "androix-room" } +androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "androix-room" } +androidx-workmanager = { group = "androidx.work", name = "work-runtime", version.ref = "androidx-workmanager" } + +# Compose Libraries +compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } +ui = { group = "androidx.compose.ui", name = "ui" } +ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } +ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } +ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } +ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +ui-foundation = { group = "androidx.compose.foundation", name = "foundation" } +material3-compose = { group = "androidx.compose.material3", name = "material3" } +coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" } +coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" } +coil-gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" } +activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activity-ktx" } +lifecycle-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } +navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidx-navigation" } +hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidx-hilt-navigation-compose" } +compose-compiler-extension = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" } + +# Hilt +hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } +hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } +hilt-core = { group = "com.google.dagger", name = "hilt-core", version.ref = "hilt" } +hilt-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" } +hilt-testing-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } +hilt_androidx_common = { module = "androidx.hilt:hilt-common", version.ref = "hilt_androidx" } +hilt_androidx_work = { module = "androidx.hilt:hilt-work", version.ref = "hilt_androidx" } +hilt_androidx_compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hilt_androidx" } +javax-inject = { group = "javax.inject", name = "javax.inject", version.ref = "javax" } + +# Orbit +orbit-core = { group = "org.orbit-mvi", name = "orbit-core", version.ref = "orbit" } +orbit-compose = { group = "org.orbit-mvi", name = "orbit-compose", version.ref = "orbit" } +orbit-viewmodel = { group = "org.orbit-mvi", name = "orbit-viewmodel", version.ref = "orbit" } + +# Retrofit +retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } +retrofit-kotlin-serialization = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "retrofit-kotlinx-serialization-json" } +okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } + +# Google Libraries +google-material = { group = "com.google.android.material", name = "material", version.ref = "material" } +crashlytics-plugin = { module = "com.google.firebase:firebase-crashlytics-gradle", version.ref = "crashlytics" } +firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" } +firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging-ktx" } +firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" } +firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics-ktx" } +firebase-remoteConfig = { group = "com.google.firebase", name = "firebase-config-ktx" } +firebase-database = { group = "com.google.firebase", name = "firebase-database-ktx" } +accompanist-insets = { module = "com.google.accompanist:accompanist-insets", version.ref = "accompanistInsets" } +accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" } +accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistPermissions" } +google-location = { group = "com.google.android.gms", name = "play-services-location", version.ref = "google-location" } + +# Naver Libraries +naver-map-compose = { group = "io.github.fornewid", name = "naver-map-compose", version.ref = "naverMapCompose" } +naver-map-location = { group = "io.github.fornewid", name = "naver-map-location", version.ref = "naverMapLocation" } +naver-map-sdk = { group = "com.naver.maps", name = "map-sdk", version.ref = "naverMapSdk" } + +# AWS +aws-android-sdk-cognito = { module = "com.amazonaws:aws-android-sdk-cognito", version.ref = "awsAndroidSdkMobileClient" } +aws-android-sdk-mobile-client = { module = "com.amazonaws:aws-android-sdk-mobile-client", version.ref = "awsAndroidSdkMobileClient" } +aws-android-sdk-s3 = { module = "com.amazonaws:aws-android-sdk-s3", version.ref = "awsAndroidSdkMobileClient" } + +# Timber +timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" } + +# formatting +detekt-plugin-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" } +itlab-detekt-plugin-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-plugin-formatting", version.ref = "detekt" } + +# Miscellaneous Libraries +coil = { group = "io.coil-kt", name = "coil", version.ref = "coil" } +lottie = { group = "com.airbnb.android", name = "lottie", version.ref = "lottie" } +lottie-compose = { group = "com.airbnb.android", name = "lottie-compose", version.ref = "lottie" } +jsoup = { group = "org.jsoup", name = "jsoup", version.ref = "jsoup" } +kakao-login = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-login" } +process-phoenix = { module = "com.jakewharton:process-phoenix", version.ref = "process-pheonix" } +collapsing-toolbar = { group = "me.onebone", name = "toolbar-compose", version.ref = "collapsing-toolbar" } +androidx-runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroid" } +flexible-bottomsheet = { group = "com.github.skydoves", name = "flexible-bottomsheet-material3", version.ref = "flexibleBottomSheet" } +[bundles] +coil = ["coil", "coil-svg", "coil-gif"] +firebase = ["firebase-analytics", "firebase-database", "firebase-messaging", "firebase-remoteConfig"] +androidx-lifecycle = ["androidx-lifecycle-runtime-ktx", "androidx-lifecycle-viewmodel-ktx"] +androidx-navigation = ["navigation-fragment-ktx", "navigation-ui-ktx"] +coroutine = ["kotlinx-coroutines-android", "kotlinx-coroutines-core"] +datastore = ["androidx-datastore-core", "androidx-datastore-preferences"] +compose = ["ui", "ui-graphics", "ui-tooling-preview", "material3-compose", "coil-compose", "ui-foundation", "activity-compose", "lifecycle-compose", "navigation-compose", "hilt-navigation-compose"] +compose-debug = ["ui-tooling", "ui-test-manifest"] +media3 = ["androidx-media3-exoplayer", "androidx-media3-exoplayer-dash", "androidx-media3-ui"] +accompanist = ["accompanist-insets", "accompanist-permissions", "accompanist-systemuicontroller"] +aws = ["aws-android-sdk-s3", "aws-android-sdk-cognito", "aws-android-sdk-mobile-client"] +orbit = ["orbit-core", "orbit-viewmodel", "orbit-compose"] +naver-maps = ["naver-map-compose", "naver-map-location", "google-location", ] \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index ad2c20cd..cd8a755e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,6 @@ +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") pluginManagement { + includeBuild("build-logic") repositories { google { content { @@ -21,4 +23,16 @@ dependencyResolutionManagement { rootProject.name = "SPOT" include(":app") - \ No newline at end of file +include(":core:designsystem") +include(":core:buildconfig") +include(":core:common") +include(":core:model") +include(":core:navigation") +include(":core:network") +include(":data:home") +include(":domain:home") +include(":feature:home") +include(":core:ui") +include(":feature:main") +include(":feature:mypage") +include(":core:datastore")