├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── Dependencies.kt ├── data ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── team │ └── dahaeng │ └── android │ └── data │ ├── aouth │ ├── mapper │ │ └── mapper.kt │ └── repository │ │ └── AouthRepositoryImpl.kt │ ├── community │ └── repository │ │ └── FirebaseRepositoryImpl.kt │ └── util │ ├── Constants.kt │ ├── DataLayerUtil.kt │ ├── extensions.kt │ └── typealias.kt ├── domain ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── team │ └── dahaeng │ └── android │ └── domain │ ├── aouth │ ├── model │ │ └── User.kt │ ├── repository │ │ └── AouthRepository.kt │ └── usecase │ │ └── KakaoLoginUseCase.kt │ └── community │ ├── model │ ├── common │ │ └── Photo.kt │ ├── post │ │ └── Post.kt │ ├── schedule │ │ └── Schedule.kt │ └── travel │ │ ├── Accommodation.kt │ │ ├── Course.kt │ │ ├── CourseList.kt │ │ ├── Locate.kt │ │ ├── Period.kt │ │ ├── Place.kt │ │ ├── Target.kt │ │ ├── Theme.kt │ │ ├── Transportation.kt │ │ └── Travel.kt │ ├── repository │ └── FirebaseRepository.kt │ └── usecase │ ├── post │ ├── ImportPostsUseCase.kt │ ├── UploadImageUseCase.kt │ └── UploadPostUseCase.kt │ └── schedule │ ├── ChangeScheduleUseCase.kt │ ├── DeleteScheduleUseCase.kt │ ├── ImportScheduleUseCase.kt │ └── UploadScheduleUseCase.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── naming-convention.md ├── presentation ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── team │ │ └── dahaeng │ │ └── android │ │ ├── DahaengAndroid.kt │ │ ├── activity │ │ ├── base │ │ │ ├── BaseActivity.kt │ │ │ ├── BaseFragment.kt │ │ │ └── BaseViewModel.kt │ │ ├── createschedule │ │ │ └── CreateScheduleActivity.kt │ │ ├── error │ │ │ └── ErrorActivity.kt │ │ ├── login │ │ │ ├── LoginActivity.kt │ │ │ └── LoginViewModel.kt │ │ ├── main │ │ │ ├── MainActivity.kt │ │ │ ├── MainViewModel.kt │ │ │ └── fragment │ │ │ │ ├── like │ │ │ │ └── LikeFragment.kt │ │ │ │ ├── list │ │ │ │ ├── ListAdapter.kt │ │ │ │ └── ListFragment.kt │ │ │ │ └── schedule │ │ │ │ ├── DetailScheduleFragment.kt │ │ │ │ ├── ScheduleAdapter.kt │ │ │ │ └── ScheduleFragment.kt │ │ ├── modifyschedule │ │ │ ├── ModifyCourseActivity.kt │ │ │ ├── ModifyCourseAdapter.kt │ │ │ ├── ModifyScheduleActivity.kt │ │ │ ├── ModifyScheduleAdapter.kt │ │ │ ├── ModifyScheduleViewModel.kt │ │ │ └── PlaceAdapter.kt │ │ └── tasking │ │ │ ├── TaskingActivity.kt │ │ │ └── TaskingViewModel.kt │ │ ├── data │ │ └── DataStore.kt │ │ ├── di │ │ ├── common │ │ │ └── SharedPreferencesModule.kt │ │ ├── repository │ │ │ └── RepositoryModule.kt │ │ └── usecase │ │ │ ├── AouthUseCaseModule.kt │ │ │ └── CommunityUseCaseModule.kt │ │ ├── ui │ │ └── databinding │ │ │ └── ImageViewAdapter.kt │ │ └── util │ │ ├── NetworkUtil.kt │ │ ├── constants │ │ └── Key.kt │ │ ├── extensions │ │ ├── Flow.kt │ │ ├── LifeCycle.kt │ │ ├── SharedPreferences.kt │ │ ├── json.kt │ │ └── toast.kt │ │ └── test │ │ └── TestUtil.kt │ └── res │ ├── drawable │ ├── bg_intro_thumbnail.png │ ├── bg_rounded_blue.xml │ ├── bg_rounded_lightblue.xml │ ├── ic_baseline_edit_24.xml │ ├── ic_baseline_more_24.xml │ ├── ic_baseline_post_add_24.xml │ ├── ic_baseline_reorder_24.xml │ ├── ic_outline_location_on_24.xml │ ├── ic_round_airplane_ticket_24.xml │ ├── ic_round_card_travel_24.xml │ ├── ic_round_castle_24.xml │ ├── ic_round_favorite_24.xml │ ├── ic_round_filter_list_24.xml │ ├── ic_round_search_24.xml │ ├── ic_round_star_24.xml │ └── ic_splash_logo.png │ ├── font │ ├── nanumbarungothic.ttf │ └── nanumbarunpen_b.ttf │ ├── layout │ ├── activity_create_schedule.xml │ ├── activity_error.xml │ ├── activity_login.xml │ ├── activity_main.xml │ ├── activity_modify_course.xml │ ├── activity_modify_schedule.xml │ ├── activity_tasking.xml │ ├── fragment_deatil_schedule.xml │ ├── fragment_like.xml │ ├── fragment_list.xml │ ├── fragment_schedule.xml │ ├── layout_avatar.xml │ ├── layout_modify_course.xml │ ├── layout_modify_schedule.xml │ ├── layout_post.xml │ ├── layout_rv_modfiy_schedule_item.xml │ └── layout_schedule.xml │ ├── menu │ ├── main_menu.xml │ └── schedule_menu.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── navigation │ └── main_navigation.xml │ ├── raw │ ├── no_internet.json │ └── request_fail.json │ ├── values-v27 │ └── themes.xml │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── strings.xml │ └── themes.xml ├── secrets.tar.gpg └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | secrets.tar 3 | local.properties 4 | google-services.json 5 | /.idea 6 | /build 7 | /buildSrc/.gradle/ 8 | /buildSrc/build/ 9 | /data/build 10 | /domain/build 11 | /presentation/build 12 | /presentation/release 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Dahaeng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 다행 [![CodeFactor](https://www.codefactor.io/repository/github/dahaeng/dahaeng-android/badge)](https://www.codefactor.io/repository/github/dahaeng/dahaeng-android) 2 | 3 | > **다**같이 정하는 여**행** 계획 4 | 5 | 매번 발생하는 읽씹과 파토나는 여행 계획 세우기.
6 | 한 번에, 빠르고, 편리하게 일정을 잡을 수 있게 도와드립니다 ✨ 7 | 8 | --- 9 | 10 | # 프로젝트 규칙 11 | 12 | 1. 클린 아키텍처 적용 13 | 2. [코틀린 린트](https://ktlint.github.io/) 14 | , [다행 네이밍 컨벤션](https://github.com/dahaeng/dahaeng-android/blob/develop/naming-convention.md)으로 코드 15 | 스타일 통일 16 | 3. 프로젝트 최대한 깔끔하게 진행 (warning 지양) 17 | 18 | # 개발 19 | 20 | - [@jisungbin](https://github.com/jisungbin) 21 | - [@210202](https://github.com/jkey20) 22 | 23 | # 수익 모델 24 | 25 | 주변 여행지 검색시 더 자세한 정보(평점, 영업시간, 메뉴판, 사진 등등)를 보기 위해선 계정 업그레이드 필요 -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | 7 | dependencies { 8 | classpath("com.android.tools.build:gradle:${Versions.Essential.Gradle}") 9 | classpath("com.google.gms:google-services:${Versions.Essential.GoogleService}") 10 | classpath("com.google.dagger:hilt-android-gradle-plugin:${Versions.Jetpack.Hilt}") 11 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.Essential.Kotlin}") 12 | classpath("com.google.android.gms:oss-licenses-plugin:${Versions.OssLicense.Classpath}") 13 | classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:${Versions.Util.SecretsGradlePlugin}") 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | maven { setUrl("https://jitpack.io") } 22 | maven { setUrl("https://devrepo.kakao.com/nexus/content/groups/public/") } 23 | } 24 | } 25 | 26 | tasks.register("clean", Delete::class) { 27 | allprojects.map { it.buildDir }.forEach(::delete) 28 | } 29 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | gradlePluginPortal() 7 | } 8 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/Dependencies.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.JavaVersion 2 | 3 | object Application { 4 | const val minSdk = 24 5 | const val targetSdk = 31 6 | const val compileSdk = 31 7 | const val jvmTarget = "11" 8 | const val versionCode = 1 9 | const val versionName = "1.0.0" 10 | 11 | val targetCompat = JavaVersion.VERSION_11 12 | val sourceCompat = JavaVersion.VERSION_11 13 | } 14 | 15 | object Versions { 16 | const val Kakao = "2.8.4" 17 | const val FirebaseBom = "29.0.3" 18 | 19 | object Essential { 20 | const val Kotlin = "1.6.10" 21 | const val Coroutines = "1.6.0" 22 | const val Gradle = "7.1.2" 23 | const val GoogleService = "4.3.3" 24 | } 25 | 26 | object Ktx { 27 | const val Core = "1.7.0" 28 | const val Fragment = "1.4.1" 29 | const val LifeCycle = "2.4.1" 30 | const val ViewModel = "2.4.1" 31 | const val Navigation = "2.4.1" 32 | } 33 | 34 | object Ui { 35 | const val Coil = "1.4.0" 36 | const val Lottie = "5.0.2" 37 | const val Flexbox = "3.0.0" 38 | const val Material = "1.5.0" 39 | const val AppCompat = "1.4.1" 40 | const val ExoPlayer = "2.17.0" 41 | const val Splash = "1.0.0-beta01" 42 | const val SmoothBottomBar = "1.7.9" 43 | const val ConstraintLayout = "2.1.3" 44 | } 45 | 46 | object Util { 47 | const val Moshi = "1.13.0" 48 | const val Erratum = "1.0.1" 49 | const val Logeukes = "1.0.1" 50 | const val Jackson = "2.13.1" 51 | const val LeakCanary = "2.8.1" 52 | const val SystemUiController = "1.0.0" 53 | const val SecretsGradlePlugin = "2.0.0" 54 | const val CheckDependencyUpdates = "1.5.0" 55 | } 56 | 57 | object Location { 58 | const val Gms = "19.0.1" 59 | const val Locus = "4.0.1" 60 | } 61 | 62 | object Jetpack { 63 | const val Hilt = "2.41" 64 | } 65 | 66 | object OssLicense { 67 | const val Master = "17.0.0" 68 | const val Classpath = "0.10.4" 69 | } 70 | } 71 | 72 | object Dependencies { 73 | const val Kakao = "com.kakao.sdk:v2-user:${Versions.Kakao}" 74 | const val Hilt = "com.google.dagger:hilt-android:${Versions.Jetpack.Hilt}" 75 | const val FirebaseBom = "com.google.firebase:firebase-bom:${Versions.FirebaseBom}" 76 | 77 | object Compiler { 78 | const val Hilt = "com.google.dagger:hilt-android-compiler:${Versions.Jetpack.Hilt}" 79 | } 80 | 81 | val Firebase = listOf( 82 | "com.google.firebase:firebase-storage-ktx", 83 | "com.google.firebase:firebase-firestore-ktx" 84 | ) 85 | 86 | val Essential = listOf( 87 | "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Essential.Coroutines}" 88 | ) 89 | 90 | val Ktx = listOf( 91 | "androidx.core:core-ktx:${Versions.Ktx.Core}", 92 | "androidx.fragment:fragment-ktx:${Versions.Ktx.Fragment}", 93 | "androidx.navigation:navigation-ui-ktx:${Versions.Ktx.Navigation}", 94 | "androidx.lifecycle:lifecycle-runtime-ktx:${Versions.Ktx.LifeCycle}", 95 | "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.Ktx.ViewModel}", 96 | "androidx.navigation:navigation-fragment-ktx:${Versions.Ktx.Navigation}" 97 | ) 98 | 99 | val Ui = listOf( 100 | "io.coil-kt:coil:${Versions.Ui.Coil}", 101 | "com.airbnb.android:lottie:${Versions.Ui.Lottie}", 102 | "androidx.appcompat:appcompat:${Versions.Ui.AppCompat}", 103 | "androidx.core:core-splashscreen:${Versions.Ui.Splash}", 104 | "com.google.android.flexbox:flexbox:${Versions.Ui.Flexbox}", 105 | "com.google.android.material:material:${Versions.Ui.Material}", 106 | "com.google.android.exoplayer:exoplayer:${Versions.Ui.ExoPlayer}", 107 | "com.google.android.exoplayer:exoplayer-core:${Versions.Ui.ExoPlayer}", 108 | "com.github.ibrahimsn98:SmoothBottomBar:${Versions.Ui.SmoothBottomBar}", 109 | "androidx.constraintlayout:constraintlayout:${Versions.Ui.ConstraintLayout}", 110 | "com.google.android.gms:play-services-oss-licenses:${Versions.OssLicense.Master}", 111 | ) 112 | 113 | val Util = listOf( 114 | "com.squareup.moshi:moshi:${Versions.Util.Moshi}", 115 | "io.github.jisungbin:erratum:${Versions.Util.Erratum}", 116 | "io.github.jisungbin:logeukes:${Versions.Util.Logeukes}", 117 | "land.sungbin:systemuicontroller:${Versions.Util.SystemUiController}" 118 | ) 119 | 120 | val Location = listOf( 121 | "com.github.BirjuVachhani:locus-android:${Versions.Location.Locus}", 122 | "com.google.android.gms:play-services-location:${Versions.Location.Gms}" 123 | ) 124 | 125 | val Jackson = listOf( 126 | "com.fasterxml.jackson.core:jackson-core:${Versions.Util.Jackson}", 127 | "com.fasterxml.jackson.core:jackson-databind:${Versions.Util.Jackson}", 128 | "com.fasterxml.jackson.core:jackson-annotations:${Versions.Util.Jackson}", 129 | "com.fasterxml.jackson.module:jackson-module-kotlin:${Versions.Util.Jackson}" 130 | ) 131 | 132 | val Debug = listOf( 133 | "com.squareup.leakcanary:leakcanary-android:${Versions.Util.LeakCanary}" 134 | ) 135 | } 136 | -------------------------------------------------------------------------------- /data/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("kotlin-android") 4 | id("com.google.gms.google-services") 5 | } 6 | 7 | android { 8 | compileSdk = Application.compileSdk 9 | 10 | defaultConfig { 11 | minSdk = Application.minSdk 12 | targetSdk = Application.targetSdk 13 | multiDexEnabled = true 14 | } 15 | 16 | sourceSets { 17 | getByName("main").run { 18 | java.srcDirs("src/main/kotlin") 19 | } 20 | } 21 | 22 | compileOptions { 23 | sourceCompatibility = Application.sourceCompat 24 | targetCompatibility = Application.targetCompat 25 | } 26 | 27 | kotlinOptions { 28 | jvmTarget = Application.jvmTarget 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation(projects.domain) 34 | implementation(Dependencies.Kakao) 35 | implementation(platform(Dependencies.FirebaseBom)) 36 | 37 | Dependencies.Firebase.forEach(::implementation) 38 | Dependencies.Essential.forEach(::implementation) 39 | } 40 | -------------------------------------------------------------------------------- /data/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /data/src/main/kotlin/team/dahaeng/android/data/aouth/mapper/mapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [mapper.kt] created by Ji Sungbin on 22. 1. 6. 오전 1:56 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.data.aouth.mapper 11 | 12 | import com.kakao.sdk.user.model.User 13 | import team.dahaeng.android.data.util.UserDomain 14 | import kotlin.random.Random 15 | 16 | fun User.toDomain() = UserDomain( 17 | id = id ?: Random.nextLong(), 18 | nickname = kakaoAccount?.profile?.nickname ?: UserDomain.getDefaultNickname(), 19 | profileImageUrl = kakaoAccount?.profile?.profileImageUrl 20 | ?: UserDomain.getDefaultProfileImageColor() 21 | ) 22 | -------------------------------------------------------------------------------- /data/src/main/kotlin/team/dahaeng/android/data/aouth/repository/AouthRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [LoginRepositoryImpl.kt] created by Ji Sungbin on 22. 1. 6. 오전 1:37 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.data.aouth.repository 11 | 12 | import android.content.Context 13 | import com.kakao.sdk.user.UserApiClient 14 | import com.kakao.sdk.user.model.User 15 | import kotlinx.coroutines.suspendCancellableCoroutine 16 | import team.dahaeng.android.data.aouth.mapper.toDomain 17 | import team.dahaeng.android.data.util.UserDomain 18 | import team.dahaeng.android.domain.aouth.repository.AouthRepository 19 | import kotlin.coroutines.resume 20 | 21 | private const val RESPONSE_NOTHING = "Kakao API response is nothing." 22 | 23 | class AouthRepositoryImpl : AouthRepository { 24 | override suspend fun kakaoLogin(context: Context): UserDomain { 25 | if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) { 26 | loginWithKakaoTalk(context) 27 | } else { 28 | loginWithWebView(context) 29 | } 30 | return getUser().toDomain() 31 | } 32 | 33 | private suspend fun loginWithKakaoTalk(context: Context): Unit = 34 | suspendCancellableCoroutine { continuation -> 35 | UserApiClient.instance.loginWithKakaoTalk(context) { token, error -> 36 | continuation.resume( 37 | when { 38 | error != null -> throw error 39 | token != null -> Unit 40 | else -> throw Throwable(RESPONSE_NOTHING) 41 | } 42 | ) 43 | } 44 | } 45 | 46 | private suspend fun loginWithWebView(context: Context): Unit = 47 | suspendCancellableCoroutine { continuation -> 48 | UserApiClient.instance.loginWithKakaoAccount(context) { token, error -> 49 | continuation.resume( 50 | when { 51 | error != null -> throw error 52 | token != null -> Unit 53 | else -> throw Throwable(RESPONSE_NOTHING) 54 | } 55 | ) 56 | } 57 | } 58 | 59 | private suspend fun getUser(): User = suspendCancellableCoroutine { continuation -> 60 | UserApiClient.instance.me { user, error -> 61 | continuation.resume( 62 | when { 63 | error != null -> throw error 64 | user != null -> user 65 | else -> throw Throwable(RESPONSE_NOTHING) 66 | } 67 | ) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /data/src/main/kotlin/team/dahaeng/android/data/community/repository/FirebaseRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [FirebaseRepositoryImpl.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.data.community.repository 11 | 12 | import com.google.firebase.firestore.DocumentSnapshot 13 | import com.google.firebase.firestore.ktx.firestore 14 | import com.google.firebase.ktx.Firebase 15 | import com.google.firebase.storage.ktx.storage 16 | import kotlinx.coroutines.suspendCancellableCoroutine 17 | import team.dahaeng.android.data.util.Constants 18 | import team.dahaeng.android.data.util.toObjectNonNull 19 | import team.dahaeng.android.domain.community.model.common.Photo 20 | import team.dahaeng.android.domain.community.model.post.Post 21 | import team.dahaeng.android.domain.community.model.schedule.Schedule 22 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 23 | import kotlin.coroutines.resume 24 | 25 | private const val UPLOAD_IMAGE_EXCEPTION = "이미지 업로드중 에러" 26 | 27 | class FirebaseRepositoryImpl : FirebaseRepository { 28 | 29 | private val firestore by lazy { Firebase.firestore } 30 | private val storageRef by lazy { Firebase.storage.reference } 31 | 32 | /** 33 | * @return 성공시 이미지 주소, 실패시 null 34 | */ 35 | // override suspend fun uploadImage(uri: Uri, imageName: String): String? = 36 | // suspendCancellableCoroutine { continuation -> 37 | // storageRef.child(Constants.Firestore.Post).run { 38 | // child(imageName) 39 | // .putFile(uri) 40 | // .continueWithTask { task -> 41 | // if (!task.isSuccessful && task.exception != null) { 42 | // continuation.resume(null) 43 | // throw task.exception!! 44 | // } 45 | // downloadUrl 46 | // }.addOnCompleteListener { task -> 47 | // if (task.isSuccessful && task.result != null) { 48 | // continuation.resume(task.result!!.toString()) 49 | // } else { 50 | // continuation.resume(null) 51 | // throw task.exception ?: Exception(UPLOAD_IMAGE_EXCEPTION) 52 | // } 53 | // } 54 | // } 55 | // } 56 | 57 | override suspend fun uploadPhotos(photos: List, imageName: String): String? { 58 | TODO("Not yet implemented") 59 | } 60 | 61 | /** 62 | * @return 성공 여부 boolean 63 | */ 64 | override suspend fun uploadPost(post: Post): Boolean = 65 | suspendCancellableCoroutine { continuation -> 66 | firestore.collection(Constants.Firestore.Post) 67 | .document(post.id.toString()) 68 | .set(post) 69 | .addOnSuccessListener { 70 | continuation.resume(true) 71 | }.addOnFailureListener { exception -> 72 | continuation.resume(false) 73 | throw exception 74 | } 75 | } 76 | 77 | /** 78 | * 전체 포스트 조회 79 | */ 80 | override suspend fun importPosts(): List = 81 | suspendCancellableCoroutine { continuation -> 82 | firestore.collection(Constants.Firestore.Post) 83 | .get() 84 | .addOnSuccessListener { result -> 85 | continuation.resume(result.documents.map(DocumentSnapshot::toObjectNonNull)) 86 | } 87 | .addOnFailureListener { exception -> 88 | continuation.resume(emptyList()) 89 | throw exception 90 | } 91 | } 92 | 93 | override suspend fun deletePost(): Boolean { 94 | TODO("Not yet implemented") 95 | } 96 | 97 | /** 98 | * 내 스케줄 리스트 조회 99 | */ 100 | override suspend fun importSchedules(ownerId: Long): List = 101 | suspendCancellableCoroutine { continuation -> 102 | firestore.collection(Constants.Firestore.User) 103 | //.document(ownerId.toString()) 104 | .document("null") 105 | .collection(Constants.Firestore.Schedule) 106 | .get() 107 | .addOnSuccessListener { result -> 108 | continuation.resume(result.documents.map(DocumentSnapshot::toObjectNonNull)) 109 | } 110 | .addOnFailureListener { exception -> 111 | continuation.resume(emptyList()) 112 | throw exception 113 | } 114 | } 115 | 116 | /** 117 | * 일정 업로드 118 | * @return 성공 여부 boolean 119 | */ 120 | override suspend fun uploadSchedule(schedule: Schedule): Boolean = 121 | suspendCancellableCoroutine { continuation -> 122 | firestore.collection(Constants.Firestore.User) 123 | // .document(schedule.participant.first().toString()) 124 | .document(schedule.participant.firstOrNull().toString()) 125 | .collection(Constants.Firestore.Schedule) 126 | .document(schedule.id.toString()) 127 | .set(schedule) 128 | .addOnSuccessListener { 129 | continuation.resume(true) 130 | } 131 | .addOnFailureListener { exception -> 132 | continuation.resume(false) 133 | throw exception 134 | } 135 | } 136 | 137 | /** 138 | * 일정 삭제 139 | * @return 성공 여부 boolean 140 | */ 141 | override suspend fun deleteSchedule(schedule: Schedule): Boolean = 142 | suspendCancellableCoroutine { continuation -> 143 | firestore.collection(Constants.Firestore.User) 144 | // .document(schedule.participant.first().toString()) 145 | .document(schedule.participant.firstOrNull().toString()) 146 | .collection(Constants.Firestore.Schedule) 147 | .document(schedule.id.toString()) 148 | .delete() 149 | .addOnSuccessListener { 150 | continuation.resume(true) 151 | } 152 | .addOnFailureListener { exception -> 153 | continuation.resume(false) 154 | throw exception 155 | } 156 | } 157 | 158 | override suspend fun changeSchedule(schedule: Schedule): Boolean = 159 | suspendCancellableCoroutine { continuation -> 160 | firestore.collection(Constants.Firestore.User) 161 | .document(schedule.participant.firstOrNull().toString()) 162 | .collection(Constants.Firestore.Schedule) 163 | .document(schedule.id.toString()) 164 | .update(schedule.toMap()) 165 | .addOnSuccessListener { 166 | continuation.resume(true) 167 | }.addOnFailureListener { exception -> 168 | continuation.resume(false) 169 | throw exception 170 | } 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /data/src/main/kotlin/team/dahaeng/android/data/util/Constants.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Constants.kt] created by Ji Sungbin on 22. 1. 13. 오후 7:12 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.data.util 11 | 12 | object Constants { 13 | object Firestore { 14 | const val Post = "post" 15 | const val User = "user" 16 | const val Schedule = "schedule" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /data/src/main/kotlin/team/dahaeng/android/data/util/DataLayerUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [DataLayerUtil.kt] created by Ji Sungbin on 22. 1. 14. 오후 5:56 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.data.util 11 | 12 | import android.content.Context 13 | import com.kakao.sdk.common.KakaoSdk 14 | 15 | object DataLayerUtil { 16 | fun initKakaoSdk(context: Context, apiKey: String) { 17 | KakaoSdk.init(context, apiKey) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data/src/main/kotlin/team/dahaeng/android/data/util/extensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [extensions.kt] created by Ji Sungbin on 22. 1. 13. 오후 6:34 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.data.util 11 | 12 | import com.google.firebase.firestore.DocumentSnapshot 13 | 14 | inline fun DocumentSnapshot.toObjectNonNull(): T = toObject(T::class.java)!! 15 | -------------------------------------------------------------------------------- /data/src/main/kotlin/team/dahaeng/android/data/util/typealias.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [typealias.kt] created by Ji Sungbin on 22. 1. 13. 오후 6:06 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.data.util 11 | 12 | typealias UserDomain = team.dahaeng.android.domain.aouth.model.User 13 | -------------------------------------------------------------------------------- /domain/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("kotlin-android") 4 | } 5 | 6 | android { 7 | compileSdk = Application.compileSdk 8 | 9 | defaultConfig { 10 | minSdk = Application.minSdk 11 | targetSdk = Application.targetSdk 12 | multiDexEnabled = true 13 | } 14 | 15 | sourceSets { 16 | getByName("main").run { 17 | java.srcDirs("src/main/kotlin") 18 | } 19 | } 20 | 21 | compileOptions { 22 | sourceCompatibility = Application.sourceCompat 23 | targetCompatibility = Application.targetCompat 24 | } 25 | 26 | kotlinOptions { 27 | jvmTarget = Application.jvmTarget 28 | } 29 | } 30 | 31 | dependencies { 32 | Dependencies.Essential.forEach(::implementation) 33 | } 34 | -------------------------------------------------------------------------------- /domain/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/aouth/model/User.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [User.kt] created by Ji Sungbin on 22. 1. 6. 오전 1:01 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.aouth.model 11 | 12 | import android.graphics.drawable.ColorDrawable 13 | import kotlin.random.Random 14 | 15 | // profileImageUrl: String or ColorDrawable 16 | data class User(val id: Long, val nickname: String, val profileImageUrl: Any) { 17 | companion object { 18 | private val randomColor get() = (Math.random() * 16777215).toInt() or (0xFF shl 24) 19 | fun getDefaultNickname() = "사용자${Random.nextInt(1000, 10000)}" 20 | fun getDefaultProfileImageColor() = ColorDrawable(randomColor) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/aouth/repository/AouthRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [LoginRepository.kt] created by Ji Sungbin on 22. 1. 6. 오전 1:29 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.aouth.repository 11 | 12 | import android.content.Context 13 | import team.dahaeng.android.domain.aouth.model.User 14 | 15 | interface AouthRepository { 16 | suspend fun kakaoLogin(context: Context): User 17 | } 18 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/aouth/usecase/KakaoLoginUseCase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [KakaoLoginUseCase.kt] created by Ji Sungbin on 22. 1. 6. 오전 1:33 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.aouth.usecase 11 | 12 | import android.content.Context 13 | import team.dahaeng.android.domain.aouth.repository.AouthRepository 14 | 15 | class KakaoLoginUseCase(private val repository: AouthRepository, private val context: Context) { 16 | suspend operator fun invoke() = runCatching { 17 | repository.kakaoLogin(context) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/common/Photo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Photo.kt] created by Ji Sungbin on 22. 2. 13. 오후 4:08 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.common 11 | 12 | import android.graphics.Bitmap 13 | import team.dahaeng.android.domain.community.model.travel.Place 14 | 15 | /** 16 | * [Place] 에 들어갈 이미지를 Firestore 에 올리기 위해 필요한 정보들을 담는 객체 17 | * 18 | * @property bitmap 이미지 비트맵 19 | * 갤러리에서 이미지를 가져옴: URI 변환, 카메라에서 이미지를 바로 찍음: Bitmap 변환 20 | * URI은 비트맵으로 바로 변환할 수 있지만, 카메라에서 찍은 이미지를 URI로 변환하는건 21 | * 찍은 이미지를 따로 파일에 저장하고 해당 파일의 URI을 가져와야 함 22 | * TODO: 카메라로 찍은 사진도 URI로 가져오기 23 | * TODO: 비트맵을 통채로 가지고 있는건 메모리 측면에서 매우 안 좋음 24 | * @property name Firebase Storage에 저장될 이미지 이름 ({이미지 이름}.jpg 로 저장됨) 25 | */ 26 | data class Photo(val bitmap: Bitmap, val name: String) 27 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/post/Post.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Post.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.post 11 | 12 | import team.dahaeng.android.domain.community.model.travel.Travel 13 | import java.util.Date 14 | import kotlin.random.Random 15 | 16 | /** 17 | * 여행지 추천 게시글 객체 18 | * 19 | * 사진은 [travel] 에 포함되므로 따로 입력받지 않음 20 | * 21 | * @property id 포스트 UUID 22 | * @property ownerId 포스트 등록자 UUID 23 | * @property title 포스트 제목 24 | * @property content 포스트 내용, HTML 컨텐츠 텍스트 span 한정으로 지원 25 | * @property travel 포스트에 첨부된 여행 정보 26 | * @property createdAt 포스트가 업로드된 시간 27 | */ 28 | data class Post( 29 | val id: Long = Random.nextLong(), 30 | val ownerId: Long = 0L, 31 | val title: String = "", 32 | val content: String = "", 33 | val travel: Travel = Travel(), 34 | val createdAt: Long = Date().time, 35 | ) 36 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/schedule/Schedule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Schedule.kt] created by Ji Sungbin on 22. 2. 4. 오전 12:42 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.schedule 11 | 12 | import team.dahaeng.android.domain.community.model.travel.Travel 13 | import java.io.Serializable 14 | import kotlin.random.Random 15 | 16 | /** 17 | * 일정 객체 18 | * 19 | * @property id 일정 UUID 20 | * @property participant 일정 참여자 UUID 리스트, 첫 요소는 무조건 해당 일정의 owner 임 21 | * @property title 일정 제목 22 | * @property travel 일정동안 쓰일 여행지 객체 23 | */ 24 | data class Schedule( 25 | val id: Long = Random.nextLong(), 26 | val participant: List = emptyList(), 27 | var title: String = "", 28 | var travel: Travel = Travel(), 29 | ) : Serializable { 30 | fun toMap(): Map { 31 | return mapOf( 32 | "id" to id, 33 | "participant" to participant, 34 | "title" to title, 35 | "travel" to travel 36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/travel/Accommodation.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Accommodation.kt] created by Ji Sungbin on 22. 2. 13. 오후 4:20 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.travel 11 | 12 | import java.io.Serializable 13 | 14 | /** 15 | * 숙소 객체 16 | * 17 | * @property name 숙소 이름 18 | * @property locate 숙소 위치 19 | */ 20 | data class Accommodation( 21 | val name: String = "", 22 | val locate: Locate = Locate(), 23 | ) : Serializable 24 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/travel/Course.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Course.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.travel 11 | 12 | import java.io.Serializable 13 | 14 | data class Course( 15 | val transportation: Transportation = Transportation(), 16 | val period: Period = Period(), 17 | val place: Place = Place(), 18 | val accommodation: Accommodation = Accommodation(), 19 | ) : Serializable 20 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/travel/CourseList.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [CourseList.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.travel 11 | 12 | import java.io.Serializable 13 | 14 | data class CourseList( 15 | var courses : List = emptyList() 16 | ): Serializable -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/travel/Locate.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Locate.kt] created by Ji Sungbin on 22. 2. 13. 오후 4:44 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.travel 11 | 12 | import java.io.Serializable 13 | 14 | /** 15 | * 위치 객체 16 | * 17 | * 위도와 경도는 지도에 정확한 마커를 찍기 위해 필요함 18 | * 19 | * @property latitude 위도 20 | * @property longitude 경도 21 | * @property address 주소 (충청남도 서산시 석림동 ~~~~) 22 | */ 23 | data class Locate( 24 | val latitude: Double = Double.NaN, 25 | val longitude: Double = Double.NaN, 26 | val address: String = "", 27 | ) : Serializable 28 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/travel/Period.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [TravelPeriod.kt] created by Ji Sungbin on 22. 1. 13. 오후 6:42 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.travel 11 | 12 | import java.io.Serializable 13 | 14 | /** 15 | * 기간 객체 16 | * 17 | * 모든 값은 Date().time 형태로 가져옴 18 | * 19 | * @property from 시작 일 20 | * @property to 종료 일 21 | */ 22 | data class Period( 23 | val from: Long = 0L, 24 | val to: Long = 0L, 25 | ) : Serializable 26 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/travel/Place.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Place.kt] created by Ji Sungbin on 22. 1. 13. 오후 7:01 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.travel 11 | 12 | import team.dahaeng.android.domain.community.model.common.Photo 13 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 14 | import java.io.Serializable 15 | 16 | /** 17 | * 장소 객체 18 | * 19 | * 어느 상황에서나 공통되는 정보들로만 구성함 20 | * 교통수단이나 기간 등은 매 상황마다 달라질 수 있는 정보임 21 | * 22 | * @property mainPrice 대표되는 금액 (식당이면 대표 메뉴 금액) 23 | * @property name 장소 이름 24 | * @property locate 장소 위치 25 | * @property photos 장소의 사진들인 [Photo] 객체들을 26 | * [FirebaseRepository.uploadPhotos] 로 업로드 하여 받은 결과 -> 이미지 다운로드 주소 배열 27 | */ 28 | data class Place( 29 | val mainPrice: Int = 0, 30 | val name: String = "", 31 | val locate: Locate = Locate(), 32 | val photos: List = emptyList(), 33 | ) : Serializable -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/travel/Target.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Target.kt] created by Ji Sungbin on 22. 1. 13. 오후 6:48 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.travel 11 | 12 | import java.io.Serializable 13 | 14 | /** 15 | * 타켓 객체 16 | * 17 | * value class 로 하게 되면 Firestore 에서 object 변환할 때 값 주입을 못하게 됨 18 | */ 19 | data class Target(val value: String = "") : Serializable { 20 | companion object { 21 | val Random get() = TargetList.All.random() 22 | } 23 | } 24 | 25 | @Suppress("FunctionName", "MemberVisibilityCanBePrivate") 26 | object TargetList { 27 | val Parent = Target("부모님") 28 | val Friend = Target("친구") 29 | val Lover = Target("애인") 30 | val All = listOf(Parent, Friend, Lover) 31 | 32 | fun Other(target: String) = Target(target) 33 | } 34 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/travel/Theme.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Theme.kt] created by Ji Sungbin on 22. 1. 13. 오후 6:44 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.travel 11 | 12 | import java.io.Serializable 13 | 14 | /** 15 | * 테마 객체 16 | * 17 | * value class 로 하게 되면 Firestore 에서 object 변환할 때 값 주입을 못하게 됨 18 | */ 19 | data class Theme(val value: String = "") : Serializable { 20 | companion object { 21 | val Random get() = ThemeList.All.random() 22 | } 23 | } 24 | 25 | @Suppress("FunctionName", "MemberVisibilityCanBePrivate") 26 | object ThemeList { 27 | val Pension = Theme("펜션") 28 | val Camping = Theme("캠핑") 29 | val Sport = Theme("스포츠") 30 | val Healing = Theme("힐링") 31 | val All = listOf(Pension, Camping, Sport, Healing) 32 | 33 | fun Other(theme: String) = Theme(theme) 34 | } 35 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/travel/Transportation.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Transportation.kt] created by Ji Sungbin on 22. 1. 13. 오후 6:50 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.travel 11 | 12 | import java.io.Serializable 13 | 14 | /** 15 | * 교통수단 객체 16 | * 17 | * value class 로 하게 되면 Firestore 에서 object 변환할 때 값 주입을 못하게 됨 18 | * 19 | * @property name 교통수단 이름 20 | * @property description 교통수단에 대해 자유롭게 추가 설명 기제할 필드 (버스 번호 등등) 21 | * @property price 교통수단 금액 22 | * @property availableTime 교통수단 이용가능 시간대 (자유롭게 쓸 수 있게 하기 위해 String 으로 받음) 23 | */ 24 | data class Transportation( 25 | val name: String = "", 26 | val description: String = "", 27 | val price: Int = 0, 28 | val availableTime: String = "", 29 | ) : Serializable { 30 | companion object { 31 | val Random get() = TransportationList.All.random() 32 | } 33 | } 34 | 35 | @Suppress("FunctionName", "MemberVisibilityCanBePrivate") 36 | object TransportationList : Serializable { 37 | val Car = Transportation("자동차") 38 | val Bus = Transportation("버스") 39 | val Subway = Transportation("지하철") 40 | val Plane = Transportation("비행기") 41 | val Ship = Transportation("배") 42 | val All = listOf(Car, Bus, Subway, Plane, Ship) 43 | 44 | fun Other(transportation: String) = Transportation(transportation) 45 | } 46 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/model/travel/Travel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Travel.kt] created by Ji Sungbin on 22. 1. 13. 오후 6:48 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.model.travel 11 | 12 | import team.dahaeng.android.domain.community.model.post.Post 13 | import team.dahaeng.android.domain.community.model.schedule.Schedule 14 | import java.io.Serializable 15 | 16 | /** 17 | * 여행지 객체 18 | * [Post] 와 [Schedule] 에 공통적으로 쓰임 19 | * 20 | * 사용되는 객체에 다 id 필드가 있기 때문에 따로 아이디를 받지 않음 21 | * 22 | * 일정에도 1일차, 2일차,...,N일차와 같이 단계가 있으므로 23 | * 단계에 대응하기 위해 [transportations] ~ [accommodations] 는 모두 리스트이며, 24 | * N일차에 여러가지 장소에 갈 수 있으므로 element 를 list 로 받음 25 | * N일차마다 정보를 조회할 때 해당 정보 list 의 N-1 번째 index로 접근하여 가져옴 26 | * 27 | * 여행지 객체를 사용하는 필드에서 아이디로 갖고 있게 되면 28 | * source 여행지가 업데이트 됐을 경우 내가 가져와 29 | * 수정한 여행지에도 source 에서 바뀐게 반영되기 때문에 30 | * 여행지 객체를 통체로 받음 31 | * (내가 바꾼 여행 정보를 source 에 영향을 32 | * 미치지 않게 반영하는 법도 고려해야 하는 문제가 생김) 33 | * 34 | * 여행지 추가 시나리오 35 | * 1. 여행 장소인 [Place] 입력 받음 36 | * 2. 나머지 추가 정보(교통수단, 기간 등등)들 입력 받음 37 | * 38 | * @property theme 여행지 테마 39 | * @property target 여행지 추천 타켓 40 | * @property commonAddress 여행지의 공통되는 주소 (충청남도 서산시) 41 | * @property totalPrice 여행지 장소들 ([places]) 의 총 금액 (여행지 등록할 때 시스템에서 계산) 42 | * @property totalPeriod 여행지 장소들 ([places]) 의 총 기간 (여행지 등록할 때 시스템에서 계산) 43 | * @property transportations 여행지 장소들 ([places]) 의 교통편 리스트 44 | * @property periods 여행지 장소들 ([places]) 의 기간 리스트 45 | * @property places 여행지 장소 리스트 46 | * @property accommodations 여행지 장소들 ([places]) 마다 머물 숙소들 47 | * 48 | * 49 | * 2022-03-01 변경점 50 | * val courseLists -> var courseLists, courseList 하위 val -> var 51 | * 변경점은 데이터 update시에 변경점이있는 부분만 변경하는것이 좋을것 같아서 var로 변경. 52 | * 53 | */ 54 | data class Travel( 55 | val theme: Theme = Theme(), 56 | val target: Target = Target(), 57 | val commonAddress: String = "", 58 | val totalPrice: Int = 0, 59 | val totalPeriod: Period = Period(), 60 | var courseLists: List = emptyList(), 61 | ) : Serializable 62 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/repository/FirebaseRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [FirebaseRepository.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.repository 11 | 12 | import team.dahaeng.android.domain.community.model.common.Photo 13 | import team.dahaeng.android.domain.community.model.post.Post 14 | import team.dahaeng.android.domain.community.model.schedule.Schedule 15 | 16 | interface FirebaseRepository { 17 | /** 18 | * 포스트 업로드에 쓰일 이미지들 업로드 19 | */ 20 | suspend fun uploadPhotos(photos: List, imageName: String): String? 21 | suspend fun uploadPost(post: Post): Boolean 22 | suspend fun importPosts(): List 23 | suspend fun deletePost(): Boolean 24 | suspend fun importSchedules(ownerId: Long): List 25 | suspend fun uploadSchedule(schedule: Schedule): Boolean 26 | suspend fun deleteSchedule(schedule: Schedule): Boolean 27 | suspend fun changeSchedule(schedule: Schedule): Boolean 28 | } 29 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/usecase/post/ImportPostsUseCase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [ImportFirebaseStorageUseCase.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.usecase.post 11 | 12 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 13 | 14 | class ImportPostsUseCase(private val repository: FirebaseRepository) { 15 | suspend operator fun invoke() = runCatching { 16 | repository.importPosts() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/usecase/post/UploadImageUseCase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [UploadFirebaseStorageUseCase.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.usecase.post 11 | 12 | import android.net.Uri 13 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 14 | 15 | class UploadImageUseCase(private val repository: FirebaseRepository) { 16 | suspend operator fun invoke(uri: Uri, imageName: String) = runCatching { 17 | //repository.uploadImage(uri, imageName) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/usecase/post/UploadPostUseCase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [UploadPostToStorageUseCase.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.usecase.post 11 | 12 | import team.dahaeng.android.domain.community.model.post.Post 13 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 14 | 15 | class UploadPostUseCase(private val repository: FirebaseRepository) { 16 | suspend operator fun invoke(post: Post) = runCatching { 17 | repository.uploadPost(post) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/usecase/schedule/ChangeScheduleUseCase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [ChangeScheduleUseCase.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.usecase.schedule 11 | 12 | import team.dahaeng.android.domain.community.model.schedule.Schedule 13 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 14 | 15 | class ChangeScheduleUseCase(private val repository: FirebaseRepository) { 16 | suspend operator fun invoke(schedule: Schedule) = runCatching { 17 | repository.changeSchedule(schedule) 18 | } 19 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/usecase/schedule/DeleteScheduleUseCase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [DeleteScheduleUseCase.kt] created by Ji Sungbin on 22. 2. 7. 오후 5:26 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.usecase.schedule 11 | 12 | import team.dahaeng.android.domain.community.model.schedule.Schedule 13 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 14 | 15 | class DeleteScheduleUseCase(private val repository: FirebaseRepository) { 16 | suspend operator fun invoke(schedule: Schedule) = runCatching { 17 | repository.deleteSchedule(schedule) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/usecase/schedule/ImportScheduleUseCase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [ImportScheduleUseCase.kt] created by Ji Sungbin on 22. 2. 7. 오후 5:25 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.usecase.schedule 11 | 12 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 13 | 14 | class ImportScheduleUseCase(private val repository: FirebaseRepository) { 15 | suspend operator fun invoke(ownerId: Long) = runCatching { 16 | repository.importSchedules(ownerId) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/team/dahaeng/android/domain/community/usecase/schedule/UploadScheduleUseCase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [UploadScheduleUseCase.kt] created by Ji Sungbin on 22. 2. 7. 오후 5:25 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.domain.community.usecase.schedule 11 | 12 | import team.dahaeng.android.domain.community.model.schedule.Schedule 13 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 14 | 15 | class UploadScheduleUseCase(private val repository: FirebaseRepository) { 16 | suspend operator fun invoke(schedule: Schedule) = runCatching { 17 | repository.uploadSchedule(schedule) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2048m 2 | org.gradle.configureondemand=true 3 | org.gradle.parallel=true 4 | org.gradle.caching=true 5 | android.useAndroidX=true 6 | android.enableJetifier=true 7 | android.enableResourceOptimizations=true 8 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dahaeng/dahaeng-android/8c61b8b5ae5f82dfde97f60795fc65b9abb1aceb/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 3 | distributionPath=wrapper/dists 4 | zipStorePath=wrapper/dists 5 | zipStoreBase=GRADLE_USER_HOME 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /naming-convention.md: -------------------------------------------------------------------------------- 1 | # 다행 네이밍 컨벤션 2 | 3 | --- 4 | 5 | # 리소스 6 | 7 | > 스네이크 표기법 사용 8 | 9 | ## strings.xml 10 | 11 | **> 타입_사용구역_주제_설명** 12 | 13 | > activity_setting_notification_access_permission (주제: notification) 14 | > 15 | > fragment_setting_toast_storage_permission_granted (주제: toast) 16 | 17 | #### 사용구역 18 | 19 | 1. dialog: 다이얼로그 리소스 20 | 2. activity: 엑티비티 리소스 21 | 3. fragment: 프레그맨트 리소스 22 | 4. bottomsheet: 바텀시트 리소스 23 | 24 | ## drawable 25 | 26 | **> 사용용도_타입_설명_사이즈** 27 | 28 | > bg_baseline_gradient_500 29 | 30 | #### 사용용도 31 | 32 | 1. bg: 배경사진 용도 33 | 2. ic: 아이콘 용도 34 | 35 | #### 타입 36 | 37 | 1. rounded: 원형 38 | 2. baseline: 기본값 39 | 40 | #### 사이즈 41 | 42 | 사이즈는 dp기준으로 하며 끝에 dp 표기는 생략함 43 | 44 | ## colors.xml 45 | 46 | **> 색이름_강도(선택사항)** 47 | 48 | > red 49 | > 50 | > blue_200 51 | > 52 | > blue_400 53 | > 54 | > blue_600 55 | 56 | ## themes.xml, View ID (XML) 57 | 58 | **> 타입_설명 (타입은 약어 사용)** 59 | 60 | > tv_header 61 | > 62 | > tv_title 63 | > 64 | > btn_register 65 | 66 | # 클래스명(파일명) 67 | 68 | > 파스칼 표기법 사용 69 | 70 | **> 이름_사용목적** 71 | 72 | > MainActivity 73 | > 74 | > SettingFragment 75 | > 76 | > LoginBottomSheet 77 | 78 | #### 사용목적 79 | 80 | 1. dialog: 다이얼로그 81 | 2. activity: 엑티비티 82 | 3. fragment: 프레그맨트 83 | 4. bottomsheet: 바텀시트 84 | 85 | #### 만약 `extension` 클래스일 경우 86 | 87 | **> 해당 `extension`의 `this` 대상으로 파일명을 작성** 88 | 89 | > TextView.kt 90 | > 91 | > Int.kt 92 | > 93 | > String.kt 94 | 95 | #### 만약 해당 `extension`의 `this` 대상이 `Unit`일 경우 96 | 97 | **> 해당 `extension` 함수들의 공통 목적으로 파일명을 전체 대문자로 작성** 98 | 99 | > UI.kt 100 | -------------------------------------------------------------------------------- /presentation/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("kotlin-android") 4 | id("kotlin-kapt") 5 | id("dagger.hilt.android.plugin") 6 | id("com.google.android.gms.oss-licenses-plugin") 7 | id("name.remal.check-dependency-updates") version Versions.Util.CheckDependencyUpdates 8 | id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") 9 | } 10 | 11 | android { 12 | compileSdk = Application.compileSdk 13 | 14 | defaultConfig { 15 | minSdk = Application.minSdk 16 | targetSdk = Application.targetSdk 17 | versionCode = Application.versionCode 18 | versionName = Application.versionName 19 | multiDexEnabled = true 20 | } 21 | 22 | buildFeatures { 23 | dataBinding = true 24 | } 25 | 26 | buildTypes { 27 | release { 28 | isDebuggable = false 29 | isMinifyEnabled = true 30 | isShrinkResources = true 31 | } 32 | } 33 | 34 | sourceSets { 35 | getByName("main").run { 36 | java.srcDirs("src/main/kotlin") 37 | } 38 | } 39 | 40 | compileOptions { 41 | sourceCompatibility = Application.sourceCompat 42 | targetCompatibility = Application.targetCompat 43 | } 44 | 45 | kotlinOptions { 46 | jvmTarget = Application.jvmTarget 47 | } 48 | } 49 | 50 | dependencies { 51 | val kapts = listOf(Dependencies.Compiler.Hilt) 52 | 53 | implementation(projects.data) 54 | implementation(projects.domain) 55 | implementation(Dependencies.Hilt) 56 | 57 | Dependencies.Ui.forEach(::implementation) 58 | Dependencies.Ktx.forEach(::implementation) 59 | Dependencies.Util.forEach(::implementation) 60 | Dependencies.Jackson.forEach(::implementation) 61 | Dependencies.Location.forEach(::implementation) 62 | Dependencies.Essential.forEach(::implementation) 63 | 64 | Dependencies.Debug.forEach(::debugImplementation) 65 | 66 | kapts.forEach(::kapt) 67 | } 68 | -------------------------------------------------------------------------------- /presentation/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 17 | 21 | 25 | 29 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 46 | 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/DahaengAndroid.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [DahaengAndroid.kt] created by Ji Sungbin on 22. 1. 5. 오후 4:51 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android 11 | 12 | import android.app.Application 13 | import android.content.Intent 14 | import dagger.hilt.android.HiltAndroidApp 15 | import io.github.jisungbin.erratum.Erratum 16 | import io.github.jisungbin.erratum.ErratumExceptionActivity 17 | import io.github.jisungbin.logeukes.Logeukes 18 | import team.dahaeng.android.activity.error.ErrorActivity 19 | import team.dahaeng.android.data.util.DataLayerUtil 20 | import team.dahaeng.android.util.constants.Key 21 | 22 | @HiltAndroidApp 23 | class DahaengAndroid : Application() { 24 | override fun onCreate() { 25 | super.onCreate() 26 | 27 | Erratum.setup( 28 | application = this, 29 | registerExceptionActivityIntent = { _, throwable, lastActivity -> 30 | Intent(lastActivity, ErrorActivity::class.java).apply { 31 | putExtra(ErratumExceptionActivity.EXTRA_EXCEPTION_STRING, throwable.toString()) 32 | putExtra( 33 | ErratumExceptionActivity.EXTRA_LAST_ACTIVITY_INTENT, 34 | lastActivity.intent 35 | ) 36 | putExtra(Key.Intent.Error, Key.Intent.Exception) 37 | } 38 | } 39 | ) 40 | DataLayerUtil.initKakaoSdk(this, BuildConfig.KAKAO_API_KEY) 41 | if (BuildConfig.DEBUG) { 42 | Logeukes.setup() 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [BaseActivity.kt] created by Ji Sungbin on 22. 1. 5. 오후 4:48 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.base 11 | 12 | import android.os.Bundle 13 | import androidx.annotation.LayoutRes 14 | import androidx.appcompat.app.AppCompatActivity 15 | import androidx.databinding.DataBindingUtil 16 | import androidx.databinding.ViewDataBinding 17 | import androidx.databinding.library.baseAdapters.BR 18 | import androidx.lifecycle.ViewModel 19 | 20 | @Suppress("MemberVisibilityCanBePrivate") 21 | abstract class BaseActivity(@LayoutRes private val layoutId: Int) : 22 | AppCompatActivity() { 23 | 24 | abstract val vm: VM 25 | protected lateinit var binding: B 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | binding = DataBindingUtil.setContentView(this, layoutId) 30 | with(binding) { 31 | lifecycleOwner = this@BaseActivity 32 | setVariable(BR.vm, vm) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [BaseFragment.kt] created by Ji Sungbin on 22. 1. 5. 오후 4:49 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.base 11 | 12 | import android.os.Bundle 13 | import android.view.LayoutInflater 14 | import android.view.View 15 | import android.view.ViewGroup 16 | import androidx.annotation.LayoutRes 17 | import androidx.databinding.DataBindingUtil 18 | import androidx.databinding.ViewDataBinding 19 | import androidx.databinding.library.baseAdapters.BR 20 | import androidx.fragment.app.Fragment 21 | import androidx.lifecycle.ViewModel 22 | 23 | @Suppress("MemberVisibilityCanBePrivate") 24 | abstract class BaseFragment(@LayoutRes private val layoutId: Int) : 25 | Fragment() { 26 | 27 | abstract val vm: VM 28 | protected lateinit var binding: B 29 | 30 | override fun onCreateView( 31 | inflater: LayoutInflater, 32 | container: ViewGroup?, 33 | savedInstanceState: Bundle? 34 | ): View { 35 | binding = DataBindingUtil.inflate(inflater, layoutId, container, false) 36 | with(binding) { 37 | lifecycleOwner = this@BaseFragment.viewLifecycleOwner 38 | setVariable(BR.vm, vm) 39 | } 40 | return binding.root 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/base/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [BaseViewModel.kt] created by Ji Sungbin 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.base 11 | 12 | import androidx.lifecycle.ViewModel 13 | import androidx.lifecycle.viewModelScope 14 | import kotlinx.coroutines.flow.MutableSharedFlow 15 | import kotlinx.coroutines.flow.asSharedFlow 16 | import kotlinx.coroutines.launch 17 | 18 | abstract class BaseViewModel : ViewModel() { 19 | private val _exceptionFlow = MutableSharedFlow() 20 | val exceptionFlow = _exceptionFlow.asSharedFlow() 21 | 22 | open fun emitException(throwable: Throwable) = viewModelScope.launch { 23 | _exceptionFlow.emit(throwable) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/createschedule/CreateScheduleActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [CreateScheduleActivity.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.createschedule 11 | 12 | import android.os.Bundle 13 | import androidx.activity.viewModels 14 | import dagger.hilt.android.AndroidEntryPoint 15 | import team.dahaeng.android.R 16 | import team.dahaeng.android.activity.base.BaseActivity 17 | import team.dahaeng.android.activity.main.MainViewModel 18 | import team.dahaeng.android.databinding.ActivityCreateScheduleBinding 19 | import team.dahaeng.android.domain.community.model.schedule.Schedule 20 | import team.dahaeng.android.domain.community.model.travel.* 21 | 22 | @AndroidEntryPoint 23 | class CreateScheduleActivity : BaseActivity( 24 | R.layout.activity_create_schedule 25 | ) { 26 | override val vm: MainViewModel by viewModels() 27 | 28 | override fun onCreate(savedInstanceState: Bundle?) { 29 | super.onCreate(savedInstanceState) 30 | 31 | binding.btnCreateScheduleComplete.setOnClickListener { 32 | vm.addSchedule( 33 | testSchedule() 34 | ) 35 | 36 | finish() 37 | } 38 | } 39 | 40 | private fun testSchedule(): Schedule { 41 | return Schedule( 42 | title = binding.textInputEditText.text.toString(), 43 | travel = Travel( 44 | theme = Theme(), 45 | commonAddress = "공통 주소", 46 | totalPrice = 1000000, 47 | totalPeriod = Period( 48 | to = 20220221, 49 | from = 20220225 50 | ), 51 | courseLists = listOf( 52 | CourseList( 53 | courses = listOf( 54 | Course( 55 | transportation = Transportation( 56 | name = "항공" 57 | ), 58 | period = Period( 59 | to = 20220221, 60 | from = 20220222 61 | ), 62 | place = Place( 63 | mainPrice = 10000, 64 | name = "장소 이름", 65 | locate = Locate(), 66 | photos = listOf("사진들") 67 | ), 68 | accommodation = Accommodation( 69 | name = "호텔", 70 | locate = Locate(), 71 | ), 72 | ), 73 | 74 | Course( 75 | transportation = Transportation( 76 | name = "배" 77 | ), 78 | period = Period( 79 | to = 20220222, 80 | from = 20220223 81 | ), 82 | place = Place( 83 | mainPrice = 20000, 84 | name = "장소 이름", 85 | locate = Locate(), 86 | photos = listOf("사진들") 87 | ), 88 | accommodation = Accommodation( 89 | name = "펜션", 90 | locate = Locate(), 91 | ), 92 | ), 93 | ) 94 | ), 95 | CourseList( 96 | courses = listOf( 97 | Course( 98 | transportation = Transportation( 99 | name = "항공" 100 | ), 101 | period = Period( 102 | to = 20220223, 103 | from = 20220224 104 | ), 105 | place = Place( 106 | mainPrice = 30000, 107 | name = "장소 이름", 108 | locate = Locate(), 109 | photos = listOf("사진들") 110 | ), 111 | accommodation = Accommodation( 112 | name = "호텔", 113 | locate = Locate(), 114 | ), 115 | ), 116 | 117 | Course( 118 | transportation = Transportation( 119 | name = "배" 120 | ), 121 | period = Period( 122 | to = 20220224, 123 | from = 20220225 124 | ), 125 | place = Place( 126 | mainPrice = 40000, 127 | name = "장소 이름", 128 | locate = Locate(), 129 | photos = listOf("사진들") 130 | ), 131 | accommodation = Accommodation( 132 | name = "펜션", 133 | locate = Locate(), 134 | ), 135 | ), 136 | ) 137 | ) 138 | ) 139 | ) 140 | ) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/error/ErrorActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [ErrorActivity.kt] created by Ji Sungbin on 22. 1. 19. 오후 12:05 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.error 11 | 12 | import android.os.Bundle 13 | import androidx.databinding.DataBindingUtil 14 | import io.github.jisungbin.erratum.ErratumExceptionActivity 15 | import io.github.jisungbin.logeukes.LoggerType 16 | import io.github.jisungbin.logeukes.logeukes 17 | import team.dahaeng.android.BuildConfig 18 | import team.dahaeng.android.R 19 | import team.dahaeng.android.databinding.ActivityErrorBinding 20 | import team.dahaeng.android.util.constants.Key 21 | 22 | class ErrorActivity : ErratumExceptionActivity() { 23 | 24 | private lateinit var binding: ActivityErrorBinding 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | super.onCreate(savedInstanceState) 28 | binding = DataBindingUtil.setContentView(this, R.layout.activity_error) 29 | binding.lifecycleOwner = this 30 | 31 | when (intent.getStringExtra(Key.Intent.Error)) { 32 | Key.Intent.NoInternet -> { 33 | binding.lavLottie.run { 34 | setAnimation(R.raw.no_internet) 35 | repeatCount = 100 36 | } 37 | binding.tvException.text = getString(R.string.activity_error_no_internet) 38 | } 39 | else -> { // Key.Intent.Exception 40 | binding.lavLottie.run { 41 | setAnimation(R.raw.request_fail) 42 | setOnClickListener { 43 | playAnimation() 44 | } 45 | } 46 | binding.tvException.text = if (BuildConfig.DEBUG) { 47 | logeukes(type = LoggerType.E) { exceptionString } 48 | exceptionString 49 | } else { 50 | getString(R.string.activity_error_exception) 51 | } 52 | } 53 | } 54 | 55 | binding.btnRestart.setOnClickListener { 56 | openLastActivity() 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/login/LoginActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [LoginActivity.kt] created by Ji Sungbin on 22. 1. 5. 오후 5:13 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.login 11 | 12 | import android.content.Intent 13 | import android.content.SharedPreferences 14 | import android.os.Build 15 | import android.os.Bundle 16 | import android.view.View 17 | import android.view.ViewTreeObserver 18 | import android.view.WindowManager 19 | import android.view.animation.AnticipateInterpolator 20 | import androidx.activity.viewModels 21 | import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen 22 | import com.google.android.exoplayer2.ExoPlayer 23 | import com.google.android.exoplayer2.MediaItem 24 | import com.google.android.exoplayer2.Player 25 | import dagger.hilt.android.AndroidEntryPoint 26 | import io.github.jisungbin.logeukes.LoggerType 27 | import io.github.jisungbin.logeukes.logeukes 28 | import team.dahaeng.android.BuildConfig 29 | import team.dahaeng.android.R 30 | import team.dahaeng.android.activity.base.BaseActivity 31 | import team.dahaeng.android.activity.error.ErrorActivity 32 | import team.dahaeng.android.activity.main.MainActivity 33 | import team.dahaeng.android.data.DataStore 34 | import team.dahaeng.android.databinding.ActivityLoginBinding 35 | import team.dahaeng.android.domain.aouth.model.User 36 | import team.dahaeng.android.util.NetworkUtil 37 | import team.dahaeng.android.util.constants.Key 38 | import team.dahaeng.android.util.extensions.collectWithLifecycle 39 | import team.dahaeng.android.util.extensions.get 40 | import team.dahaeng.android.util.extensions.launchedWhenCreated 41 | import team.dahaeng.android.util.extensions.set 42 | import team.dahaeng.android.util.extensions.toJsonString 43 | import team.dahaeng.android.util.extensions.toModel 44 | import team.dahaeng.android.util.extensions.toast 45 | import javax.inject.Inject 46 | 47 | @AndroidEntryPoint 48 | class LoginActivity : BaseActivity(R.layout.activity_login) { 49 | 50 | override val vm: LoginViewModel by viewModels() 51 | private var player: ExoPlayer? = null 52 | private var isReady = false 53 | 54 | @Inject 55 | lateinit var sharedPreferences: SharedPreferences 56 | 57 | override fun onCreate(savedInstanceState: Bundle?) { 58 | installSplashScreen() 59 | super.onCreate(savedInstanceState) 60 | 61 | if (!NetworkUtil.isNetworkAvailable(applicationContext)) { 62 | finish() 63 | startActivity( 64 | Intent(this, ErrorActivity::class.java).apply { 65 | putExtra(Key.Intent.Error, Key.Intent.NoInternet) 66 | } 67 | ) 68 | return 69 | } 70 | 71 | window.setFlags( 72 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, 73 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 74 | ) 75 | 76 | launchedWhenCreated { 77 | vm.importPostsWithDoneAction()?.let { posts -> 78 | DataStore.updatePosts(posts) 79 | if (sharedPreferences[Key.User.KakaoProfile] != null) { 80 | // 자동 로그인 상태 81 | val me: User = sharedPreferences[Key.User.KakaoProfile]!!.toModel() 82 | DataStore.me = me 83 | startMainActivity() 84 | } else { 85 | isReady = true 86 | } 87 | } 88 | } 89 | 90 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 91 | splashScreen.setOnExitAnimationListener { splashScreenView -> 92 | splashScreenView.animate().run { 93 | alpha(0f) 94 | scaleX(0f) 95 | scaleY(0f) 96 | interpolator = AnticipateInterpolator() 97 | duration = 200L 98 | withEndAction { splashScreenView.remove() } 99 | withLayer() 100 | start() 101 | } 102 | } 103 | } 104 | 105 | binding.root.viewTreeObserver.addOnPreDrawListener( 106 | object : ViewTreeObserver.OnPreDrawListener { 107 | override fun onPreDraw(): Boolean { 108 | return if (isReady) { 109 | binding.root.viewTreeObserver.removeOnPreDrawListener(this) 110 | true 111 | } else { 112 | false 113 | } 114 | } 115 | } 116 | ) 117 | 118 | binding.btnLogin.setOnClickListener { 119 | launchedWhenCreated { 120 | vm.login()?.let { user -> 121 | DataStore.me = user 122 | toast(getString(R.string.activity_login_toast_welcome)) 123 | startMainActivity() 124 | sharedPreferences[Key.User.KakaoProfile] = user.toJsonString() 125 | } 126 | } 127 | } 128 | 129 | vm.exceptionFlow.collectWithLifecycle(this) { exception -> 130 | logeukes(type = LoggerType.E) { exception } 131 | toast(getString(R.string.activity_login_toast_start_fail)) 132 | } 133 | } 134 | 135 | // TODO: 플리커가 왜 생기지?? 136 | private fun playbackStateListener() = object : Player.Listener { 137 | override fun onPlaybackStateChanged(playbackState: Int) { 138 | when (playbackState) { 139 | ExoPlayer.STATE_BUFFERING, ExoPlayer.STATE_IDLE -> { 140 | binding.exoPlayer.visibility = View.GONE 141 | binding.ivIntroThumbnail.visibility = View.VISIBLE 142 | } 143 | ExoPlayer.STATE_READY -> { 144 | binding.exoPlayer.visibility = View.VISIBLE 145 | binding.ivIntroThumbnail.visibility = View.GONE 146 | } 147 | ExoPlayer.STATE_ENDED -> {} 148 | } 149 | } 150 | } 151 | 152 | private fun initExoPlayer() { 153 | player = ExoPlayer.Builder(this) 154 | .build() 155 | .apply { 156 | repeatMode = Player.REPEAT_MODE_ONE 157 | } 158 | .also { player -> 159 | val video = MediaItem.fromUri(BuildConfig.LOGIN_INTRO_VIDEO_URI) 160 | player.setMediaItem(video) 161 | binding.exoPlayer.player = player 162 | } 163 | 164 | player!!.addListener(playbackStateListener()) 165 | player!!.playWhenReady = true 166 | player!!.prepare() 167 | } 168 | 169 | private fun releaseExoPlayer() { 170 | player?.release() 171 | player = null 172 | } 173 | 174 | override fun onStart() { 175 | super.onStart() 176 | initExoPlayer() 177 | } 178 | 179 | override fun onStop() { 180 | super.onStop() 181 | releaseExoPlayer() 182 | } 183 | 184 | private fun startMainActivity() { 185 | finish() 186 | startActivity(Intent(this, MainActivity::class.java)) 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/login/LoginViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [LoginViewModel.kt] created by Ji Sungbin on 22. 1. 5. 오후 4:26 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.login 11 | 12 | import dagger.hilt.android.lifecycle.HiltViewModel 13 | import team.dahaeng.android.activity.base.BaseViewModel 14 | import team.dahaeng.android.domain.aouth.usecase.KakaoLoginUseCase 15 | import team.dahaeng.android.domain.community.usecase.post.ImportPostsUseCase 16 | import javax.inject.Inject 17 | 18 | @HiltViewModel 19 | class LoginViewModel @Inject constructor( 20 | private val kakaoLoginUseCase: KakaoLoginUseCase, 21 | private val importPostsUseCase: ImportPostsUseCase, 22 | ) : BaseViewModel() { 23 | suspend fun login() = kakaoLoginUseCase().getOrElse { exception -> 24 | emitException(exception) 25 | null 26 | } 27 | 28 | suspend fun importPostsWithDoneAction() = importPostsUseCase().getOrElse { exception -> 29 | emitException(exception) 30 | null 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/main/MainActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [MainActivity.kt] created by Ji Sungbin on 22. 1. 17. 오전 9:55 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.main 11 | 12 | import android.os.Bundle 13 | import android.widget.PopupMenu 14 | import androidx.activity.viewModels 15 | import androidx.navigation.NavController 16 | import androidx.navigation.fragment.NavHostFragment 17 | import dagger.hilt.android.AndroidEntryPoint 18 | import team.dahaeng.android.R 19 | import team.dahaeng.android.activity.base.BaseActivity 20 | import team.dahaeng.android.data.DataStore 21 | import team.dahaeng.android.databinding.ActivityMainBinding 22 | 23 | @AndroidEntryPoint 24 | class MainActivity : BaseActivity(R.layout.activity_main) { 25 | 26 | override val vm: MainViewModel by viewModels() 27 | private lateinit var navController: NavController 28 | 29 | override fun onCreate(savedInstanceState: Bundle?) { 30 | super.onCreate(savedInstanceState) 31 | 32 | vm.importSchedule(DataStore.me.id) 33 | 34 | navController = 35 | (supportFragmentManager.findFragmentById(R.id.fcv_fragment) as NavHostFragment).navController 36 | setupSmoothBottomMenu() 37 | } 38 | 39 | private fun setupSmoothBottomMenu() { 40 | val popupMenu = PopupMenu(this, null) 41 | popupMenu.inflate(R.menu.main_menu) 42 | val menu = popupMenu.menu 43 | binding.sbbMenu.setupWithNavController(menu, navController) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/main/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [MainViewModel.kt] created by Ji Sungbin on 22. 1. 5. 오후 4:12 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.main 11 | 12 | import androidx.lifecycle.viewModelScope 13 | import dagger.hilt.android.lifecycle.HiltViewModel 14 | import io.github.jisungbin.logeukes.LoggerType 15 | import io.github.jisungbin.logeukes.logeukes 16 | import kotlinx.coroutines.flow.MutableStateFlow 17 | import kotlinx.coroutines.flow.asStateFlow 18 | import kotlinx.coroutines.flow.update 19 | import kotlinx.coroutines.launch 20 | import team.dahaeng.android.activity.base.BaseViewModel 21 | import team.dahaeng.android.data.DataStore 22 | import team.dahaeng.android.domain.community.model.schedule.Schedule 23 | import team.dahaeng.android.domain.community.usecase.post.ImportPostsUseCase 24 | import team.dahaeng.android.domain.community.usecase.schedule.ChangeScheduleUseCase 25 | import team.dahaeng.android.domain.community.usecase.schedule.DeleteScheduleUseCase 26 | import team.dahaeng.android.domain.community.usecase.schedule.ImportScheduleUseCase 27 | import team.dahaeng.android.domain.community.usecase.schedule.UploadScheduleUseCase 28 | import javax.inject.Inject 29 | 30 | @HiltViewModel 31 | class MainViewModel @Inject constructor( 32 | private val importPostsUseCase: ImportPostsUseCase, 33 | private val importScheduleUseCase: ImportScheduleUseCase, 34 | private val uploadScheduleUseCase: UploadScheduleUseCase, 35 | private val deleteScheduleUseCase: DeleteScheduleUseCase, 36 | private val changeScheduleUseCase: ChangeScheduleUseCase, 37 | ) : BaseViewModel() { 38 | 39 | private val _posts = MutableStateFlow(DataStore.posts) 40 | val posts = _posts.asStateFlow() 41 | 42 | private val _schedules = MutableStateFlow(DataStore.schedules) 43 | val schedules = _schedules.asStateFlow() 44 | 45 | var lastLocate = "" 46 | 47 | fun reimportPosts() = viewModelScope.launch { 48 | importPostsUseCase() 49 | .onSuccess { posts -> 50 | if (posts.isNotEmpty()) { 51 | DataStore.updatePosts(posts) 52 | _posts.emit(posts) 53 | } 54 | } 55 | .onFailure { exception -> 56 | logeukes(type = LoggerType.E) { exception } 57 | emitException(exception) 58 | } 59 | } 60 | 61 | fun importSchedule(ownerId: Long) = viewModelScope.launch { 62 | importScheduleUseCase(ownerId) 63 | .onSuccess { scheduleList -> 64 | if (scheduleList.isNotEmpty()) { 65 | DataStore.updateSchedules(scheduleList) 66 | _schedules.emit(scheduleList) 67 | } 68 | } 69 | .onFailure { exception -> 70 | logeukes(type = LoggerType.E) { exception } 71 | emitException(exception) 72 | } 73 | } 74 | 75 | fun addSchedule(schedule: Schedule) = viewModelScope.launch { 76 | uploadScheduleUseCase(schedule) 77 | .onSuccess { isSuccess -> 78 | if (isSuccess) { 79 | _schedules.update { it + schedule } 80 | } 81 | } 82 | .onFailure { exception -> 83 | logeukes(type = LoggerType.E) { exception } 84 | emitException(exception) 85 | } 86 | } 87 | 88 | fun changeSchedule(changeSchedule: Schedule) = 89 | viewModelScope.launch { 90 | changeScheduleUseCase(changeSchedule) 91 | .onSuccess { isSuccess -> 92 | if (isSuccess) { 93 | logeukes { "update!" } 94 | } 95 | } 96 | .onFailure { exception -> 97 | logeukes(type = LoggerType.E) { exception } 98 | emitException(exception) 99 | } 100 | } 101 | 102 | fun deleteSchedule(schedule: Schedule) = viewModelScope.launch { 103 | deleteScheduleUseCase(schedule) 104 | .onSuccess { isSuccess -> 105 | if (isSuccess) { 106 | _schedules.update { it - schedule } 107 | } 108 | } 109 | .onFailure { exception -> 110 | logeukes(type = LoggerType.E) { exception } 111 | emitException(exception) 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/main/fragment/like/LikeFragment.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [TaskingFragment.kt] created by Ji Sungbin on 22. 1. 18. 오후 9:09 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.main.fragment.like 11 | 12 | import androidx.fragment.app.activityViewModels 13 | import team.dahaeng.android.R 14 | import team.dahaeng.android.activity.base.BaseFragment 15 | import team.dahaeng.android.activity.main.MainViewModel 16 | import team.dahaeng.android.databinding.FragmentLikeBinding 17 | 18 | class LikeFragment : BaseFragment(R.layout.fragment_like) { 19 | override val vm: MainViewModel by activityViewModels() 20 | } 21 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/main/fragment/list/ListAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [PostAdapter.kt] created by Ji Sungbin on 22. 1. 19. 오후 4:28 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.main.fragment.list 11 | 12 | import android.view.LayoutInflater 13 | import android.view.ViewGroup 14 | import androidx.recyclerview.widget.DiffUtil 15 | import androidx.recyclerview.widget.ListAdapter 16 | import androidx.recyclerview.widget.RecyclerView 17 | import team.dahaeng.android.databinding.LayoutPostBinding 18 | import team.dahaeng.android.domain.community.model.post.Post 19 | 20 | private typealias ListAdapterViewHolder = team.dahaeng.android.activity.main.fragment.list.ListAdapter.ViewHolder 21 | 22 | class ListAdapter(private val onPostClick: (Post) -> Unit) : 23 | ListAdapter(diffUtil) { 24 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 25 | val binding = LayoutPostBinding.inflate(LayoutInflater.from(parent.context), parent, false) 26 | return ViewHolder(binding, onPostClick) 27 | } 28 | 29 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 30 | holder.bind(getItem(position)) 31 | } 32 | 33 | class ViewHolder( 34 | private val binding: LayoutPostBinding, 35 | private val onPostClick: (Post) -> Unit, 36 | ) : RecyclerView.ViewHolder(binding.root) { 37 | fun bind(post: Post) { 38 | binding.run { 39 | this.post = post 40 | root.setOnClickListener { 41 | onPostClick(post) 42 | } 43 | } 44 | } 45 | } 46 | 47 | override fun getItemId(position: Int) = getItem(position).id 48 | 49 | override fun getItemCount() = currentList.count() 50 | 51 | companion object { 52 | private val diffUtil = object : DiffUtil.ItemCallback() { 53 | override fun areContentsTheSame(oldItem: Post, newItem: Post) = 54 | oldItem == newItem 55 | 56 | override fun areItemsTheSame(oldItem: Post, newItem: Post) = 57 | oldItem.id == newItem.id 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/main/fragment/list/ListFragment.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [TaskingFragment.kt] created by Ji Sungbin on 22. 1. 18. 오후 9:08 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.main.fragment.list 11 | 12 | import android.location.Geocoder 13 | import android.location.Location 14 | import android.os.Bundle 15 | import android.view.View 16 | import androidx.fragment.app.activityViewModels 17 | import com.birjuvachhani.locus.Locus 18 | import io.github.jisungbin.logeukes.LoggerType 19 | import io.github.jisungbin.logeukes.logeukes 20 | import team.dahaeng.android.R 21 | import team.dahaeng.android.activity.base.BaseFragment 22 | import team.dahaeng.android.activity.main.MainViewModel 23 | import team.dahaeng.android.databinding.FragmentListBinding 24 | import team.dahaeng.android.util.test.TestUtil 25 | import java.util.Locale 26 | 27 | class ListFragment : BaseFragment(R.layout.fragment_list) { 28 | 29 | override val vm: MainViewModel by activityViewModels() 30 | private var lastestAddress = "" 31 | 32 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 33 | super.onViewCreated(view, savedInstanceState) 34 | lastestAddress = getString(R.string.fragment_list_loading_location) 35 | 36 | if (vm.lastLocate.isEmpty()) { 37 | Locus.startLocationUpdates(this) { result -> 38 | result.location?.let { location -> 39 | vm.lastLocate = location.tryParseAddress() 40 | binding.tvLocate.text = vm.lastLocate 41 | Locus.stopLocationUpdates() 42 | } 43 | result.error?.let { exception -> 44 | logeukes(type = LoggerType.E) { exception } 45 | } 46 | } 47 | } else { 48 | binding.tvLocate.text = vm.lastLocate 49 | } 50 | 51 | binding.rvPost.run { 52 | setHasFixedSize(true) 53 | setItemViewCacheSize(10) 54 | adapter = ListAdapter { post -> 55 | logeukes { "Post clicked: $post" } 56 | }.apply { 57 | submitList(TestUtil.posts()) 58 | } 59 | } 60 | 61 | binding.tilSesarchContainer.setEndIconOnClickListener { 62 | // TODO 63 | } 64 | } 65 | 66 | private fun Location.tryParseAddress() = try { 67 | val geoCoder = Geocoder(requireContext().applicationContext, Locale.KOREA) 68 | val addressList = geoCoder.getFromLocation(latitude, longitude, 2) 69 | val address = addressList[1].getAddressLine(0).replace("대한민국 ", "") 70 | lastestAddress = address 71 | address 72 | } catch (exception: Exception) { 73 | logeukes(type = LoggerType.E) { exception } 74 | lastestAddress 75 | } 76 | 77 | override fun onPause() { 78 | Locus.stopLocationUpdates() 79 | super.onPause() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/main/fragment/schedule/DetailScheduleFragment.kt: -------------------------------------------------------------------------------- 1 | package team.dahaeng.android.activity.main.fragment.schedule 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.activityViewModels 6 | import team.dahaeng.android.R 7 | import team.dahaeng.android.activity.base.BaseFragment 8 | import team.dahaeng.android.activity.main.MainViewModel 9 | import team.dahaeng.android.databinding.FragmentDeatilScheduleBinding 10 | 11 | class DetailScheduleFragment : BaseFragment( 12 | R.layout.fragment_deatil_schedule 13 | ){ 14 | override val vm: MainViewModel by activityViewModels() 15 | 16 | 17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 18 | super.onViewCreated(view, savedInstanceState) 19 | } 20 | } -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/main/fragment/schedule/ScheduleAdapter.kt: -------------------------------------------------------------------------------- 1 | package team.dahaeng.android.activity.main.fragment.schedule 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.DiffUtil 7 | import androidx.recyclerview.widget.ListAdapter 8 | import androidx.recyclerview.widget.RecyclerView 9 | import team.dahaeng.android.databinding.LayoutScheduleBinding 10 | import team.dahaeng.android.domain.community.model.schedule.Schedule 11 | 12 | class ScheduleAdapter( 13 | private val onMoreClick: (View, Schedule) -> Unit, 14 | private val onClick: (View, Schedule) -> Unit 15 | ) : 16 | ListAdapter(diffUtil) { 17 | 18 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 19 | val binding = 20 | LayoutScheduleBinding.inflate(LayoutInflater.from(parent.context), parent, false) 21 | return ViewHolder(binding, onMoreClick, onClick) 22 | } 23 | 24 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 25 | holder.bind(getItem(position)) 26 | } 27 | 28 | class ViewHolder( 29 | private val binding: LayoutScheduleBinding, 30 | private val onMoreClick: (View, Schedule) -> Unit, 31 | private val onClick: (View, Schedule) -> Unit 32 | ) : RecyclerView.ViewHolder(binding.root) { 33 | fun bind(schedule: Schedule) { 34 | binding.schedule = schedule 35 | binding.ivMore.setOnClickListener { view -> 36 | onMoreClick(view, schedule) 37 | } 38 | binding.tvTitle.setOnClickListener { view -> 39 | onClick(view, schedule) 40 | } 41 | } 42 | } 43 | 44 | override fun getItemId(position: Int) = getItem(position).id 45 | 46 | override fun getItemCount() = currentList.count() 47 | 48 | private companion object { 49 | val diffUtil = object : DiffUtil.ItemCallback() { 50 | override fun areContentsTheSame(oldItem: Schedule, newItem: Schedule) = 51 | oldItem.id == newItem.id 52 | 53 | override fun areItemsTheSame(oldItem: Schedule, newItem: Schedule) = 54 | oldItem == newItem 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/main/fragment/schedule/ScheduleFragment.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [TaskingFragment.kt] created by Ji Sungbin on 22. 1. 17. 오전 9:56 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.main.fragment.schedule 11 | 12 | import android.content.Intent 13 | import android.os.Bundle 14 | import android.view.View 15 | import androidx.appcompat.widget.PopupMenu 16 | import androidx.fragment.app.activityViewModels 17 | import androidx.navigation.fragment.findNavController 18 | import io.github.jisungbin.logeukes.logeukes 19 | import team.dahaeng.android.R 20 | import team.dahaeng.android.activity.base.BaseFragment 21 | import team.dahaeng.android.activity.createschedule.CreateScheduleActivity 22 | import team.dahaeng.android.activity.main.MainViewModel 23 | import team.dahaeng.android.activity.modifyschedule.ModifyScheduleActivity 24 | import team.dahaeng.android.data.DataStore 25 | import team.dahaeng.android.databinding.FragmentScheduleBinding 26 | import team.dahaeng.android.domain.community.model.schedule.Schedule 27 | import team.dahaeng.android.util.extensions.collectWithLifecycle 28 | 29 | class ScheduleFragment : BaseFragment( 30 | R.layout.fragment_schedule 31 | ) { 32 | 33 | private val adapter by lazy { 34 | ScheduleAdapter(onClick = { _, _ -> 35 | findNavController().navigate(R.id.detailScheduleFragment) 36 | }, onMoreClick = { view, schedule -> 37 | openMorePopup(view, schedule) 38 | }) 39 | } 40 | override val vm: MainViewModel by activityViewModels() 41 | 42 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 43 | super.onViewCreated(view, savedInstanceState) 44 | 45 | binding.rvSchedule.run { 46 | setHasFixedSize(true) 47 | setItemViewCacheSize(10) 48 | adapter = this@ScheduleFragment.adapter.apply { 49 | submitList(DataStore.schedules) 50 | } 51 | } 52 | 53 | vm.schedules.collectWithLifecycle(viewLifecycleOwner) { scheduleList -> 54 | logeukes { scheduleList } 55 | adapter.submitList(scheduleList) 56 | } 57 | 58 | binding.fabNewSchedule.setOnClickListener { 59 | startActivity(Intent(context, CreateScheduleActivity::class.java)) 60 | } 61 | } 62 | 63 | private fun openMorePopup(view: View, schedule: Schedule) { 64 | PopupMenu(requireActivity(), view).apply { 65 | setOnMenuItemClickListener { item -> 66 | when (item.itemId) { 67 | R.id.menu_share -> { 68 | // TODO: 카카오톡 공유 69 | logeukes { "공유" } 70 | true 71 | } 72 | R.id.menu_modify -> { 73 | val intent = Intent(context, ModifyScheduleActivity::class.java) 74 | intent.putExtra("modifyschedule", schedule) 75 | startActivity(intent) 76 | true 77 | } 78 | R.id.menu_delete -> { 79 | vm.deleteSchedule(schedule) 80 | true 81 | } 82 | else -> false 83 | } 84 | } 85 | inflate(R.menu.schedule_menu) 86 | show() 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/modifyschedule/ModifyCourseActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [ModifyCourseActivity.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.modifyschedule 11 | 12 | import android.app.Activity 13 | import android.content.Intent 14 | import android.os.Bundle 15 | import androidx.activity.viewModels 16 | import androidx.recyclerview.widget.DividerItemDecoration 17 | import androidx.recyclerview.widget.ItemTouchHelper 18 | import androidx.recyclerview.widget.RecyclerView 19 | import dagger.hilt.android.AndroidEntryPoint 20 | import team.dahaeng.android.R 21 | import team.dahaeng.android.activity.base.BaseActivity 22 | import team.dahaeng.android.databinding.ActivityModifyCourseBinding 23 | import team.dahaeng.android.domain.community.model.travel.CourseList 24 | 25 | @AndroidEntryPoint 26 | class ModifyCourseActivity : BaseActivity( 27 | R.layout.activity_modify_course 28 | ) { 29 | 30 | override val vm: ModifyScheduleViewModel by viewModels() 31 | 32 | override fun onCreate(savedInstanceState: Bundle?) { 33 | super.onCreate(savedInstanceState) 34 | 35 | val courseList = intent.getSerializableExtra("modifycourselist") as CourseList 36 | 37 | binding.rvModifyCourse.run { 38 | 39 | setHasFixedSize(true) 40 | setItemViewCacheSize(10) 41 | addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) 42 | adapter = ModifyCourseAdapter( 43 | ).apply { 44 | submitList(courseList.courses) 45 | } 46 | setItemTouchCallback() 47 | } 48 | 49 | binding.btnComplete.setOnClickListener { 50 | val intent = Intent(applicationContext, ModifyScheduleActivity::class.java).apply { 51 | val list = 52 | (binding.rvModifyCourse.adapter as ModifyCourseAdapter).currentList.toMutableList() 53 | courseList.courses = list 54 | putExtra("modifycourselistresult", courseList) 55 | } 56 | setResult(Activity.RESULT_OK, intent) 57 | finish() 58 | } 59 | } 60 | 61 | private fun setItemTouchCallback() { 62 | val itemTouchCallback = object : ItemTouchHelper.SimpleCallback( 63 | ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT 64 | ) { 65 | override fun onMove( 66 | recyclerView: RecyclerView, 67 | viewHolder: RecyclerView.ViewHolder, 68 | target: RecyclerView.ViewHolder 69 | ): Boolean { 70 | val fromPos: Int = viewHolder.absoluteAdapterPosition 71 | val toPos: Int = target.absoluteAdapterPosition 72 | (binding.rvModifyCourse.adapter as ModifyCourseAdapter).swapItem(fromPos, toPos) 73 | return true 74 | } 75 | 76 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { 77 | (binding.rvModifyCourse.adapter as ModifyCourseAdapter).removeItem(viewHolder.layoutPosition) 78 | } 79 | 80 | } 81 | ItemTouchHelper(itemTouchCallback).attachToRecyclerView(binding.rvModifyCourse) 82 | } 83 | } -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/modifyschedule/ModifyCourseAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [ModifyCourseAdapter.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.modifyschedule 11 | 12 | import android.view.LayoutInflater 13 | import android.view.ViewGroup 14 | import androidx.recyclerview.widget.DiffUtil 15 | import androidx.recyclerview.widget.ListAdapter 16 | import androidx.recyclerview.widget.RecyclerView 17 | import team.dahaeng.android.databinding.LayoutModifyCourseBinding 18 | import team.dahaeng.android.domain.community.model.travel.Course 19 | import java.util.* 20 | 21 | class ModifyCourseAdapter : 22 | ListAdapter(diffUtil) { 23 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 24 | val binding = 25 | LayoutModifyCourseBinding.inflate(LayoutInflater.from(parent.context), parent, false) 26 | return ViewHolder(binding) 27 | } 28 | 29 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 30 | holder.bind(getItem(position)) 31 | // TODO: imageview glide 32 | // TODO: buttons image set 33 | // TODO: buttons onclickListener 34 | 35 | } 36 | 37 | fun swapItem(fromPos : Int, toPos : Int){ 38 | val list = currentList.toMutableList() 39 | Collections.swap(list, fromPos, toPos) 40 | submitList(list) 41 | } 42 | 43 | fun removeItem(position: Int) { 44 | val list = currentList.toMutableList() 45 | list.removeAt(position) 46 | submitList(list) 47 | } 48 | 49 | class ViewHolder( 50 | private val binding: LayoutModifyCourseBinding 51 | ) : RecyclerView.ViewHolder(binding.root) { 52 | fun bind(course : Course){ 53 | binding.course = course 54 | } 55 | } 56 | 57 | private companion object { 58 | val diffUtil = object : DiffUtil.ItemCallback() { 59 | override fun areContentsTheSame(oldItem: Course, newItem: Course) = 60 | oldItem == newItem 61 | 62 | override fun areItemsTheSame(oldItem: Course, newItem: Course) = 63 | oldItem == newItem 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/modifyschedule/ModifyScheduleActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [ModifyScheduleActivity.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.modifyschedule 11 | 12 | 13 | import android.app.Activity 14 | import android.content.Intent 15 | import android.os.Bundle 16 | import android.widget.ArrayAdapter 17 | import androidx.activity.result.contract.ActivityResultContracts 18 | import androidx.activity.viewModels 19 | import androidx.core.util.Pair 20 | import com.google.android.material.datepicker.MaterialDatePicker 21 | import dagger.hilt.android.AndroidEntryPoint 22 | import team.dahaeng.android.R 23 | import team.dahaeng.android.activity.base.BaseActivity 24 | import team.dahaeng.android.databinding.ActivityModifyScheduleBinding 25 | import team.dahaeng.android.domain.community.model.schedule.Schedule 26 | import team.dahaeng.android.domain.community.model.travel.CourseList 27 | import team.dahaeng.android.domain.community.model.travel.Period 28 | import team.dahaeng.android.domain.community.model.travel.Theme 29 | import team.dahaeng.android.domain.community.model.travel.Travel 30 | import java.text.SimpleDateFormat 31 | import java.util.* 32 | 33 | @AndroidEntryPoint 34 | class ModifyScheduleActivity : BaseActivity( 35 | R.layout.activity_modify_schedule 36 | ) { 37 | 38 | override val vm: ModifyScheduleViewModel by viewModels() 39 | 40 | override fun onCreate(savedInstanceState: Bundle?) { 41 | super.onCreate(savedInstanceState) 42 | 43 | binding.schedule = intent.getSerializableExtra("modifyschedule") as Schedule 44 | val schedule = binding.schedule 45 | var changeCourseList = CourseList() 46 | binding.period = schedule!!.travel.totalPeriod 47 | 48 | binding.snTheme.adapter = ArrayAdapter.createFromResource( 49 | this, 50 | R.array.modify_schedule_theme_array, 51 | android.R.layout.simple_dropdown_item_1line 52 | ) 53 | 54 | val startForResult = 55 | registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> 56 | if (result.resultCode == Activity.RESULT_OK) { 57 | val intent = result.data 58 | changeCourseList = 59 | (intent!!.getSerializableExtra("modifycourselistresult") as CourseList) 60 | } 61 | } 62 | 63 | binding.rvModifySchedule.run { 64 | setHasFixedSize(true) 65 | setItemViewCacheSize(10) 66 | adapter = ModifyScheduleAdapter( 67 | onEditClick = { _, courseList -> 68 | val intent = Intent(context, ModifyCourseActivity::class.java) 69 | intent.putExtra("modifycourselist", courseList) 70 | startForResult.launch(intent) 71 | courseList.courses = changeCourseList.courses 72 | } 73 | ).apply { 74 | submitList(schedule.travel.courseLists) 75 | } 76 | } 77 | binding.snTheme.adapter = ArrayAdapter.createFromResource( 78 | this, 79 | R.array.modify_schedule_theme_array, 80 | android.R.layout.simple_dropdown_item_1line 81 | ) 82 | binding.snTheme.setSelection(getThemePosition(schedule.travel.theme.value)) 83 | binding.tvTotalperiodselect.setOnClickListener { 84 | showDatePicker() 85 | } 86 | binding.btnComplete.setOnClickListener { 87 | schedule.title = binding.etTitle.text.toString() 88 | schedule.travel = Travel( 89 | theme = Theme( 90 | binding.snTheme.selectedItem.toString() 91 | ), 92 | commonAddress = binding.etCommonaddress.text.toString(), 93 | totalPeriod = binding.period as Period, 94 | totalPrice = binding.etTotalprice.text.toString().toInt(), 95 | courseLists = (binding.rvModifySchedule.adapter as ModifyScheduleAdapter).currentList 96 | ) 97 | 98 | // Todo : change schedule in firestore 99 | // vm.changeSchedule(schedule) 100 | finish() 101 | } 102 | } 103 | 104 | private fun getThemePosition(theme: String): Int { 105 | when (theme) { 106 | "펜션" -> { 107 | return 0 108 | } 109 | "캠핑" -> { 110 | return 1 111 | } 112 | "스포츠" -> { 113 | return 2 114 | } 115 | "힐링" -> { 116 | return 3 117 | } 118 | "바다" -> { 119 | return 4 120 | } 121 | "산" -> { 122 | return 5 123 | } 124 | "음식" -> { 125 | return 6 126 | } 127 | "명소" -> { 128 | return 7 129 | } 130 | } 131 | return 0 132 | } 133 | 134 | private fun showDatePicker() { 135 | val dateRangePicker = MaterialDatePicker.Builder.dateRangePicker().apply { 136 | setTitleText("기간을 선택하세요") 137 | setSelection( 138 | Pair( 139 | MaterialDatePicker.thisMonthInUtcMilliseconds(), 140 | MaterialDatePicker.todayInUtcMilliseconds() 141 | ) 142 | ) 143 | }.build() 144 | dateRangePicker.apply { 145 | show(supportFragmentManager, "date_picker") 146 | addOnNegativeButtonClickListener { dateRangePicker.dismiss() } 147 | addOnPositiveButtonClickListener { 148 | val startDate = SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(it.first) 149 | val endDate = SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(it.second) 150 | binding.period = Period(endDate.toLong(), startDate.toLong()) 151 | 152 | } 153 | } 154 | } 155 | } -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/modifyschedule/ModifyScheduleAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [ModifyScheduleAdapter.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.modifyschedule 11 | 12 | import android.view.LayoutInflater 13 | import android.view.View 14 | import android.view.ViewGroup 15 | import androidx.recyclerview.widget.DiffUtil 16 | import androidx.recyclerview.widget.ListAdapter 17 | import androidx.recyclerview.widget.RecyclerView 18 | import team.dahaeng.android.databinding.LayoutRvModfiyScheduleItemBinding 19 | import team.dahaeng.android.domain.community.model.travel.CourseList 20 | 21 | class ModifyScheduleAdapter( 22 | private val onEditClick: (View, CourseList) -> Unit 23 | ) : 24 | ListAdapter(diffUtil) { 25 | 26 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 27 | val binding = 28 | LayoutRvModfiyScheduleItemBinding.inflate( 29 | LayoutInflater.from(parent.context), 30 | parent, 31 | false 32 | ) 33 | return ViewHolder(binding, onEditClick) 34 | } 35 | 36 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 37 | holder.bind(getItem(position)) 38 | holder.coursepositionTextview.text = "코스 " + (position + 1) 39 | } 40 | 41 | class ViewHolder( 42 | private val binding: LayoutRvModfiyScheduleItemBinding, 43 | private val onEditClick: (View, CourseList) -> Unit 44 | ) : RecyclerView.ViewHolder(binding.root) { 45 | val coursepositionTextview = binding.tvCourse 46 | 47 | fun bind(courseList: CourseList) { 48 | binding.rvHorizontal.adapter = PlaceAdapter().apply { 49 | submitList(courseList.courses) 50 | } 51 | binding.ibEdit.setOnClickListener { view -> 52 | onEditClick(view, courseList) 53 | } 54 | } 55 | } 56 | 57 | 58 | override fun getItemCount() = currentList.count() 59 | 60 | private companion object { 61 | val diffUtil = object : DiffUtil.ItemCallback() { 62 | override fun areContentsTheSame(oldItem: CourseList, newItem: CourseList) = 63 | oldItem == newItem 64 | 65 | override fun areItemsTheSame(oldItem: CourseList, newItem: CourseList) = 66 | oldItem == newItem 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/modifyschedule/ModifyScheduleViewModel.kt: -------------------------------------------------------------------------------- 1 | package team.dahaeng.android.activity.modifyschedule 2 | 3 | import team.dahaeng.android.activity.base.BaseViewModel 4 | 5 | class ModifyScheduleViewModel : BaseViewModel() { 6 | } -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/modifyschedule/PlaceAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [PlaceAdapter.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.modifyschedule 11 | 12 | import android.view.LayoutInflater 13 | import android.view.ViewGroup 14 | import androidx.recyclerview.widget.DiffUtil 15 | import androidx.recyclerview.widget.ListAdapter 16 | import androidx.recyclerview.widget.RecyclerView 17 | import team.dahaeng.android.databinding.LayoutModifyScheduleBinding 18 | 19 | import team.dahaeng.android.domain.community.model.travel.Course 20 | 21 | class PlaceAdapter : 22 | ListAdapter(diffUtil) { 23 | 24 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 25 | val binding = 26 | LayoutModifyScheduleBinding.inflate(LayoutInflater.from(parent.context), parent, false) 27 | return ViewHolder(binding) 28 | } 29 | 30 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 31 | holder.bind(getItem(position)) 32 | // photoIv glide 33 | 34 | // transportationIv, accommodationIv image set 35 | // transportationIv, accommodationIv onclick 36 | 37 | } 38 | 39 | class ViewHolder( 40 | private val binding: LayoutModifyScheduleBinding, 41 | ) : RecyclerView.ViewHolder(binding.root) { 42 | 43 | val photoIv = binding.ivPhoto 44 | val transportationIv = binding.ivTransportation 45 | val accommodationIv = binding.ivAccommodation 46 | 47 | fun bind(course : Course) { 48 | binding.course = course 49 | } 50 | } 51 | 52 | 53 | override fun getItemCount() = currentList.count() 54 | 55 | private companion object { 56 | val diffUtil = object : DiffUtil.ItemCallback() { 57 | override fun areContentsTheSame(oldItem: Course, newItem: Course) = 58 | oldItem == newItem 59 | 60 | override fun areItemsTheSame(oldItem: Course, newItem: Course) = 61 | oldItem == newItem 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/tasking/TaskingActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [TaskingActivity.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.tasking 11 | 12 | import androidx.activity.viewModels 13 | import dagger.hilt.android.AndroidEntryPoint 14 | import team.dahaeng.android.R 15 | import team.dahaeng.android.activity.base.BaseActivity 16 | import team.dahaeng.android.databinding.ActivityTaskingBinding 17 | 18 | @AndroidEntryPoint 19 | class TaskingActivity : BaseActivity( 20 | R.layout.activity_tasking 21 | ) { 22 | override val vm: TaskingViewModel by viewModels() 23 | 24 | } 25 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/activity/tasking/TaskingViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [TaskingViewModel.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.activity.tasking 11 | 12 | import dagger.hilt.android.lifecycle.HiltViewModel 13 | import team.dahaeng.android.activity.base.BaseViewModel 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | class TaskingViewModel @Inject constructor() : BaseViewModel() 18 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/data/DataStore.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Store.kt] created by Ji Sungbin on 22. 1. 13. 오후 7:51 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.data 11 | 12 | import team.dahaeng.android.domain.aouth.model.User 13 | import team.dahaeng.android.domain.community.model.post.Post 14 | import team.dahaeng.android.domain.community.model.schedule.Schedule 15 | 16 | // TODO: 이게 맞나?? ㅋㅋ 17 | @Suppress("ObjectPropertyName") 18 | object DataStore { 19 | lateinit var me: User 20 | 21 | private val _posts = mutableListOf() 22 | val posts: List get() = _posts 23 | 24 | private val _schedules = mutableListOf() 25 | val schedules: List get() = _schedules 26 | 27 | fun updatePosts(posts: List) { 28 | _posts.addAll(posts) 29 | } 30 | 31 | fun updateSchedules(schedules: List) { 32 | _schedules.addAll(schedules) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/di/common/SharedPreferencesModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [SharedPreferenceModule.kt] created by Ji Sungbin on 22. 1. 18. 오후 6:04 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.di.common 11 | 12 | import android.content.Context 13 | import android.content.SharedPreferences 14 | import dagger.Module 15 | import dagger.Provides 16 | import dagger.hilt.InstallIn 17 | import dagger.hilt.android.qualifiers.ApplicationContext 18 | import dagger.hilt.components.SingletonComponent 19 | import javax.inject.Singleton 20 | 21 | @Module 22 | @InstallIn(SingletonComponent::class) 23 | object SharedPreferencesModule { 24 | private const val SharedPreferencesKey = "dahaeng-pref" 25 | 26 | @Provides 27 | @Singleton 28 | fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences = 29 | context.getSharedPreferences(SharedPreferencesKey, Context.MODE_PRIVATE) 30 | } 31 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/di/repository/RepositoryModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [RepositoryModule.kt] created by Ji Sungbin on 22. 1. 6. 오전 3:01 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.di.repository 11 | 12 | import dagger.Module 13 | import dagger.Provides 14 | import dagger.hilt.InstallIn 15 | import dagger.hilt.android.components.ViewModelComponent 16 | import dagger.hilt.android.scopes.ViewModelScoped 17 | import team.dahaeng.android.data.aouth.repository.AouthRepositoryImpl 18 | import team.dahaeng.android.data.community.repository.FirebaseRepositoryImpl 19 | import team.dahaeng.android.domain.aouth.repository.AouthRepository 20 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 21 | 22 | @Module 23 | @InstallIn(ViewModelComponent::class) 24 | object RepositoryModule { 25 | @Provides 26 | @ViewModelScoped 27 | fun provideAouthRepository(): AouthRepository = AouthRepositoryImpl() 28 | 29 | @Provides 30 | @ViewModelScoped 31 | fun provideFirebaseRepository(): FirebaseRepository = FirebaseRepositoryImpl() 32 | } 33 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/di/usecase/AouthUseCaseModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [AouthUseCaseModule.kt] created by Ji Sungbin on 22. 1. 6. 오전 4:21 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.di.usecase 11 | 12 | import android.content.Context 13 | import dagger.Module 14 | import dagger.Provides 15 | import dagger.hilt.InstallIn 16 | import dagger.hilt.android.components.ViewModelComponent 17 | import dagger.hilt.android.qualifiers.ApplicationContext 18 | import dagger.hilt.android.scopes.ViewModelScoped 19 | import team.dahaeng.android.domain.aouth.repository.AouthRepository 20 | import team.dahaeng.android.domain.aouth.usecase.KakaoLoginUseCase 21 | 22 | @Module 23 | @InstallIn(ViewModelComponent::class) 24 | object AouthUseCaseModule { 25 | @Provides 26 | @ViewModelScoped 27 | fun provideKakaoLoginUseCase( 28 | repository: AouthRepository, 29 | @ApplicationContext context: Context, 30 | ): KakaoLoginUseCase = KakaoLoginUseCase(repository, context) 31 | } 32 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/di/usecase/CommunityUseCaseModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [CommunityUseCaseModule.kt] created by 210202 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.di.usecase 11 | 12 | import dagger.Module 13 | import dagger.Provides 14 | import dagger.hilt.InstallIn 15 | import dagger.hilt.android.components.ViewModelComponent 16 | import dagger.hilt.android.scopes.ViewModelScoped 17 | import team.dahaeng.android.domain.community.repository.FirebaseRepository 18 | import team.dahaeng.android.domain.community.usecase.post.ImportPostsUseCase 19 | import team.dahaeng.android.domain.community.usecase.post.UploadImageUseCase 20 | import team.dahaeng.android.domain.community.usecase.post.UploadPostUseCase 21 | import team.dahaeng.android.domain.community.usecase.schedule.ChangeScheduleUseCase 22 | import team.dahaeng.android.domain.community.usecase.schedule.DeleteScheduleUseCase 23 | import team.dahaeng.android.domain.community.usecase.schedule.ImportScheduleUseCase 24 | import team.dahaeng.android.domain.community.usecase.schedule.UploadScheduleUseCase 25 | 26 | @Module 27 | @InstallIn(ViewModelComponent::class) 28 | object CommunityUseCaseModule { 29 | @Provides 30 | @ViewModelScoped 31 | fun provideUploadImageUseCase(repository: FirebaseRepository): UploadImageUseCase = 32 | UploadImageUseCase(repository) 33 | 34 | @Provides 35 | @ViewModelScoped 36 | fun provideUploadPostUseCase(repository: FirebaseRepository): UploadPostUseCase = 37 | UploadPostUseCase(repository) 38 | 39 | @Provides 40 | @ViewModelScoped 41 | fun provideImportPostsUseCaseToViewModel(repository: FirebaseRepository): ImportPostsUseCase = 42 | ImportPostsUseCase(repository) 43 | 44 | @Provides 45 | @ViewModelScoped 46 | fun provideUploadScheduleUseCase(reposiotry: FirebaseRepository): UploadScheduleUseCase = 47 | UploadScheduleUseCase(reposiotry) 48 | 49 | @Provides 50 | @ViewModelScoped 51 | fun provideImportScheduleUseCase(repository: FirebaseRepository): ImportScheduleUseCase = 52 | ImportScheduleUseCase(repository) 53 | 54 | @Provides 55 | @ViewModelScoped 56 | fun provideDeleteScheduleUseCase(reposiotry: FirebaseRepository): DeleteScheduleUseCase = 57 | DeleteScheduleUseCase(reposiotry) 58 | 59 | @Provides 60 | @ViewModelScoped 61 | fun provideChangeScheduleUseCase(repository: FirebaseRepository): ChangeScheduleUseCase = 62 | ChangeScheduleUseCase(repository) 63 | } 64 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/ui/databinding/ImageViewAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [ImageViewAdapter.kt] created by Ji Sungbin on 22. 1. 19. 오후 2:06 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.ui.databinding 11 | 12 | import android.widget.ImageView 13 | import androidx.databinding.BindingAdapter 14 | import androidx.lifecycle.findViewTreeLifecycleOwner 15 | import coil.loadAny 16 | import coil.request.CachePolicy 17 | import team.dahaeng.android.R 18 | 19 | @BindingAdapter("with_coil_small") 20 | fun loadSrcWithCoilSmallSize(view: ImageView, src: Any) { 21 | view.loadAny(src) { 22 | crossfade(true) 23 | placeholder(R.drawable.ic_round_airplane_ticket_24) 24 | lifecycle(view.findViewTreeLifecycleOwner()) 25 | allowRgb565(true) 26 | diskCachePolicy(CachePolicy.ENABLED) 27 | } 28 | } 29 | 30 | @BindingAdapter("with_coil_big") 31 | fun loadSrcWithCoilBigSize(view: ImageView, src: Any) { 32 | view.loadAny(src) { 33 | crossfade(true) 34 | placeholder(R.drawable.ic_round_airplane_ticket_24) 35 | lifecycle(view.findViewTreeLifecycleOwner()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/util/NetworkUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [NetworkUtil.kt] created by Ji Sungbin on 22. 1. 19. 오전 11:48 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.util 11 | 12 | import android.annotation.SuppressLint 13 | import android.content.Context 14 | import android.net.ConnectivityManager 15 | import android.net.NetworkCapabilities 16 | 17 | object NetworkUtil { 18 | @SuppressLint("MissingPermission") 19 | fun isNetworkAvailable(context: Context) = 20 | (context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).run { 21 | getNetworkCapabilities(activeNetwork)?.run { 22 | hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || 23 | hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || 24 | hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) 25 | } ?: false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/util/constants/Key.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [SharedPreferences.kt] created by Ji Sungbin on 22. 1. 18. 오후 6:12 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.util.constants 11 | 12 | object Key { 13 | object User { 14 | const val KakaoProfile = "user-kakao-profile" 15 | } 16 | 17 | object Intent { 18 | const val Error = "intent-error" 19 | const val NoInternet = "intent-no-internet" 20 | const val Exception = "intent-exception" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/util/extensions/Flow.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [Flow.kt] created by Ji Sungbin on 22. 1. 13. 오후 8:20 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.util.extensions 11 | 12 | import androidx.lifecycle.LifecycleOwner 13 | import androidx.lifecycle.flowWithLifecycle 14 | import androidx.lifecycle.lifecycleScope 15 | import kotlinx.coroutines.flow.Flow 16 | 17 | fun Flow.collectWithLifecycle( 18 | lifecycleOwner: LifecycleOwner, 19 | action: suspend (T) -> Unit, 20 | ) { 21 | lifecycleOwner.lifecycleScope.launchWhenCreated { 22 | flowWithLifecycle(lifecycleOwner.lifecycle).collect { value -> 23 | action(value) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/util/extensions/LifeCycle.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [LifeCycle.kt] created by Ji Sungbin on 22. 1. 5. 오후 9:20 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.util.extensions 11 | 12 | import androidx.lifecycle.LifecycleOwner 13 | import androidx.lifecycle.lifecycleScope 14 | import kotlinx.coroutines.delay 15 | 16 | fun LifecycleOwner.doDelayed(ms: Long, action: suspend () -> Unit) { 17 | lifecycleScope.launchWhenCreated { 18 | delay(ms) 19 | action() 20 | } 21 | } 22 | 23 | fun LifecycleOwner.launchedWhenCreated(action: suspend () -> Unit) { 24 | lifecycleScope.launchWhenCreated { 25 | action() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/util/extensions/SharedPreferences.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [SharedPreferences.kt] created by Ji Sungbin on 22. 1. 18. 오후 6:09 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.util.extensions 11 | 12 | import android.content.SharedPreferences 13 | import androidx.core.content.edit 14 | 15 | operator fun SharedPreferences.get(name: String, default: String? = null) = getString(name, default) 16 | 17 | operator fun SharedPreferences.set(name: String, value: String) = edit { putString(name, value) } 18 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/util/extensions/json.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [json.kt] created by Ji Sungbin on 22. 1. 18. 오후 6:57 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.util.extensions 11 | 12 | import com.fasterxml.jackson.databind.DeserializationFeature 13 | import com.fasterxml.jackson.databind.ObjectMapper 14 | import com.fasterxml.jackson.module.kotlin.registerKotlinModule 15 | 16 | @PublishedApi 17 | internal val mapper by lazy { 18 | ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 19 | .registerKotlinModule() 20 | } 21 | 22 | inline fun String.toModel(): T = mapper.readValue(this, T::class.java) 23 | ?: throw Exception("문자열을 json 모델로 바꾸는데 오류가 발생했어요.\n\n($this)") 24 | 25 | fun Any.toJsonString() = mapper.writeValueAsString(this) 26 | ?: throw Exception("json 모델을 문자열로 바꾸는데 오류가 발생했어요.\n\n($this)") 27 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/util/extensions/toast.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [toast.kt] created by Ji Sungbin 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.util.extensions 11 | 12 | import android.app.Activity 13 | import android.content.Context 14 | import android.widget.Toast 15 | import androidx.fragment.app.Fragment 16 | 17 | fun toast(context: Context, message: String, length: Int) { 18 | Toast.makeText(context, message, length).show() 19 | } 20 | 21 | fun toast(activity: Activity, message: String, length: Int) { 22 | activity.runOnUiThread { 23 | Toast.makeText(activity, message, length).show() 24 | } 25 | } 26 | 27 | fun Fragment.toast(message: String, length: Int = Toast.LENGTH_SHORT) { 28 | toast(requireActivity(), message, length) 29 | } 30 | 31 | @JvmName("ActivityToastExtension") 32 | fun Activity.toast(message: String, length: Int = Toast.LENGTH_SHORT) { 33 | toast(this, message, length) 34 | } 35 | -------------------------------------------------------------------------------- /presentation/src/main/kotlin/team/dahaeng/android/util/test/TestUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dahaeng © 2022 Ji Sungbin, 210202. all rights reserved. 3 | * Dahaeng license is under the MIT. 4 | * 5 | * [TestUtil.kt] created by Ji Sungbin on 22. 1. 19. 오후 5:13 6 | * 7 | * Please see: https://github.com/dahaeng/dahaeng-android/blob/main/LICENSE. 8 | */ 9 | 10 | package team.dahaeng.android.util.test 11 | 12 | import team.dahaeng.android.domain.community.model.post.Post 13 | import team.dahaeng.android.domain.community.model.travel.Period 14 | import team.dahaeng.android.domain.community.model.travel.Target 15 | import team.dahaeng.android.domain.community.model.travel.Theme 16 | import team.dahaeng.android.domain.community.model.travel.Transportation 17 | import team.dahaeng.android.domain.community.model.travel.Travel 18 | import team.dahaeng.android.domain.community.model.schedule.Schedule 19 | import java.util.Date 20 | import kotlin.random.Random 21 | 22 | object TestUtil { 23 | private const val TestImageUrl = 24 | "https://images.unsplash.com/photo-1523906834658-6e24ef2386f9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1283&q=80" 25 | 26 | /* fun posts(count: Int = 10) = List(count) { index -> 27 | Post( 28 | title = "여행지 - $index", 29 | content = "이것은 아름다운 ${index}번째 여행지 입니다.", 30 | travel = Travel( 31 | totalPrice = Random.nextLong(10_000, 1_000_000_000), 32 | transportation = Transportation.Random, 33 | period = Period(from = "어제", to = "내일"), 34 | places = listOf(), 35 | photos = listOf(TestImageUrl), 36 | theme = Theme.Random, 37 | target = Target.Random, 38 | commonAddress = "서울시 한강로" 39 | ), 40 | createdAt = Date().time 41 | ) 42 | } 43 | 44 | fun schedules(count: Int = 10) = List(count) { index -> 45 | Schedule( 46 | participant = listOf(), 47 | title = "제주 레포츠렌드 - $index", 48 | theme = "", 49 | totalPrice = 0, 50 | transportation = "", 51 | period = Period(from = "", to = ""), 52 | photos = listOf(), 53 | places = listOf(), 54 | accommodation = "" 55 | ) 56 | } 57 | */ 58 | } 59 | 60 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/bg_intro_thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dahaeng/dahaeng-android/8c61b8b5ae5f82dfde97f60795fc65b9abb1aceb/presentation/src/main/res/drawable/bg_intro_thumbnail.png -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/bg_rounded_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/bg_rounded_lightblue.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_baseline_edit_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_baseline_more_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_baseline_post_add_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_baseline_reorder_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_outline_location_on_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_round_airplane_ticket_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_round_card_travel_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_round_castle_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_round_favorite_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_round_filter_list_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_round_search_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_round_star_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/drawable/ic_splash_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dahaeng/dahaeng-android/8c61b8b5ae5f82dfde97f60795fc65b9abb1aceb/presentation/src/main/res/drawable/ic_splash_logo.png -------------------------------------------------------------------------------- /presentation/src/main/res/font/nanumbarungothic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dahaeng/dahaeng-android/8c61b8b5ae5f82dfde97f60795fc65b9abb1aceb/presentation/src/main/res/font/nanumbarungothic.ttf -------------------------------------------------------------------------------- /presentation/src/main/res/font/nanumbarunpen_b.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dahaeng/dahaeng-android/8c61b8b5ae5f82dfde97f60795fc65b9abb1aceb/presentation/src/main/res/font/nanumbarunpen_b.ttf -------------------------------------------------------------------------------- /presentation/src/main/res/layout/activity_error.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 37 | 38 | 46 | 47 | 55 | 56 | 62 | 63 | 64 | 65 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | 15 | 16 | 21 | 22 | 29 | 30 | 37 | 38 | 49 | 50 | 61 | 62 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 33 | 34 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/activity_modify_course.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 31 | 32 |