├── .firebaserc
├── .github
├── ISSUE_TEMPLATE
│ ├── ✨-기능-추가.md
│ └── 🐞-버그-신고.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── ucmcCI.yml
├── .gitignore
├── README.md
├── app
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── gta
│ │ └── ucmc
│ │ ├── LoggerInitializer.kt
│ │ └── UCMCApplication.kt
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── mipmap
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── xml
│ ├── backup_rules.xml
│ └── data_extraction_rules.xml
├── build.gradle.kts
├── buildSrc
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── com
│ └── gta
│ └── buildsrc
│ ├── Configuration.kt
│ └── Dependencies.kt
├── data
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── gta
│ │ └── data
│ │ ├── di
│ │ ├── FirebaseModule.kt
│ │ ├── LicenseModule.kt
│ │ ├── LoginModule.kt
│ │ ├── MessageTokenModule.kt
│ │ ├── MyPageModule.kt
│ │ ├── NetworkModule.kt
│ │ ├── NicknameModule.kt
│ │ ├── NotificationModule.kt
│ │ ├── PinkSlipModule.kt
│ │ ├── Qualifiers.kt
│ │ ├── RepositoryModule.kt
│ │ ├── ReservationModule.kt
│ │ └── ReviewModule.kt
│ │ ├── model
│ │ ├── AddressResult.kt
│ │ ├── Car.kt
│ │ ├── MessageResult.kt
│ │ ├── NotificationMessage.kt
│ │ ├── SearchResult.kt
│ │ └── UserInfo.kt
│ │ ├── repository
│ │ ├── CarRepositoryImpl.kt
│ │ ├── LicenseRepositoryImpl.kt
│ │ ├── LoginRepositoryImpl.kt
│ │ ├── MapRepositoryImpl.kt
│ │ ├── MessageTokenRepositoryImpl.kt
│ │ ├── MyPageRepositoryImpl.kt
│ │ ├── NicknameRepositoryImpl.kt
│ │ ├── NotificationRepositoryImpl.kt
│ │ ├── PinkSlipRepositoryImpl.kt
│ │ ├── ReportRepositoryImpl.kt
│ │ ├── ReservationRepositoryImpl.kt
│ │ ├── ReviewRepositoryImpl.kt
│ │ ├── TransactionRepositoryImpl.kt
│ │ └── UserRepositoryImpl.kt
│ │ ├── service
│ │ ├── AddressSearchService.kt
│ │ └── CloudMessageService.kt
│ │ └── source
│ │ ├── CarDataSource.kt
│ │ ├── LicenseDataSource.kt
│ │ ├── LoginDataSource.kt
│ │ ├── MapDataSource.kt
│ │ ├── MessageTokenDataSource.kt
│ │ ├── MyPageDataSource.kt
│ │ ├── NicknameDataSource.kt
│ │ ├── NotificationDataSource.kt
│ │ ├── NotificationPagingSource.kt
│ │ ├── PinkSlipDataSource.kt
│ │ ├── ReservationDataSource.kt
│ │ ├── ReviewDataSource.kt
│ │ ├── StorageDataSource.kt
│ │ ├── TransactionDataSource.kt
│ │ ├── TransactionPagingSource.kt
│ │ └── UserDataSource.kt
│ └── test
│ └── java
│ └── com
│ └── gta
│ └── data
│ ├── CarRepositoryUnitTest.kt
│ ├── LicenseRepositoryUnitTest.kt
│ ├── LoginRepositoryUnitTest.kt
│ ├── MapFragmentUnitTest.kt
│ ├── MessageTokenRepositoryUnitTest.kt
│ ├── MyPageRepositoryUnitTest.kt
│ ├── NicknameRepositoryUnitTest.kt
│ ├── NotificationRepositoryUnitTest.kt
│ ├── PinkSlipRepositoryUnitTest.kt
│ ├── ReportRepositoryUnitTest.kt
│ ├── ReservationRepositoryUnitTest.kt
│ ├── ReviewRepositoryUnitTest.kt
│ ├── TestUtil.kt
│ └── UserRepositoryUnitTest.kt
├── domain
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── com
│ └── gta
│ └── domain
│ ├── model
│ ├── AvailableDate.kt
│ ├── CarDetail.kt
│ ├── CarRentInfo.kt
│ ├── Coordinate.kt
│ ├── DrivingLicense.kt
│ ├── InsuranceOption.kt
│ ├── LocationInfo.kt
│ ├── NicknameState.kt
│ ├── Notification.kt
│ ├── NotificationInfo.kt
│ ├── PinkSlip.kt
│ ├── Reservation.kt
│ ├── ReviewDTO.kt
│ ├── ReviewType.kt
│ ├── SimpleCar.kt
│ ├── SimpleReservation.kt
│ ├── Transaction.kt
│ ├── UCMCException.kt
│ ├── UCMCResult.kt
│ ├── UpdateCar.kt
│ ├── UserProfile.kt
│ └── UserReview.kt
│ ├── repository
│ ├── CarRepository.kt
│ ├── LicenseRepository.kt
│ ├── LoginRepository.kt
│ ├── MapRepository.kt
│ ├── MessageTokenRepository.kt
│ ├── MyPageRepository.kt
│ ├── NicknameRepository.kt
│ ├── NotificationRepository.kt
│ ├── PinkSlipRepository.kt
│ ├── ReportRepository.kt
│ ├── ReservationRepository.kt
│ ├── ReviewRepository.kt
│ ├── TransactionRepository.kt
│ └── UserRepository.kt
│ └── usecase
│ ├── SendNotificationUseCase.kt
│ ├── car
│ └── GetSimpleCarUseCase.kt
│ ├── cardetail
│ ├── GetCarDetailDataUseCase.kt
│ ├── GetNowRentCarUseCase.kt
│ ├── GetOwnerCarsUseCase.kt
│ ├── GetOwnerInfoUseCase.kt
│ ├── GetUseStateAboutCarUseCase.kt
│ ├── RemoveCarUseCase.kt
│ └── edit
│ │ ├── DeleteImagesUseCase.kt
│ │ ├── GetCoordinateLocationUseCase.kt
│ │ ├── SetCarImagesUseCase.kt
│ │ ├── UpdateCarDetailDataUseCase.kt
│ │ └── UploadCarImagesUseCase.kt
│ ├── license
│ ├── GetLicenseFromDatabaseUseCase.kt
│ ├── GetLicenseFromImageUseCase.kt
│ └── SetLicenseUseCase.kt
│ ├── login
│ ├── CheckCurrentUserUseCase.kt
│ ├── SignUpUseCase.kt
│ └── UpdateUserMessageTokenUseCase.kt
│ ├── map
│ ├── GetAllCarsUseCase.kt
│ ├── GetNearCarsUseCase.kt
│ └── GetSearchAddressUseCase.kt
│ ├── mypage
│ └── SetThumbnailUseCase.kt
│ ├── nickname
│ ├── CheckNicknameStateUseCase.kt
│ └── UpdateNicknameUseCase.kt
│ ├── notification
│ ├── GetNotificationsInfoUseCase.kt
│ └── SetMessageTokenUseCase.kt
│ ├── pinkslip
│ ├── GetPinkSlipUseCase.kt
│ └── SetPinkSlipUseCase.kt
│ ├── reservation
│ ├── CreateReservationUseCase.kt
│ ├── FinishReservationUseCase.kt
│ ├── GetCarRentInfoUseCase.kt
│ └── GetReservationUseCase.kt
│ ├── returncar
│ └── ReturnCarUseCase.kt
│ ├── review
│ ├── AddReviewUseCase.kt
│ └── GetReviewDTOUseCase.kt
│ ├── transaction
│ └── GetTransactionsUseCase.kt
│ └── user
│ ├── GetUserProfileUseCase.kt
│ └── ReportUserUseCase.kt
├── firebase.json
├── functions
├── .eslintrc.js
├── .gitignore
├── index.js
├── package-lock.json
└── package.json
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── presentation
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── gta
│ │ └── presentation
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── terms_of_privacy.txt
│ │ └── terms_of_service.txt
│ ├── java
│ │ └── com
│ │ │ └── gta
│ │ │ └── presentation
│ │ │ ├── NotificationService.kt
│ │ │ ├── di
│ │ │ ├── FirebaseAuthModule.kt
│ │ │ ├── FirebaseSigninModule.kt
│ │ │ ├── StreamModule.kt
│ │ │ └── UtilModule.kt
│ │ │ ├── model
│ │ │ ├── DateType.kt
│ │ │ ├── TransactionState.kt
│ │ │ └── TransactionUserState.kt
│ │ │ ├── ui
│ │ │ ├── BindingAdapter.kt
│ │ │ ├── GlideModule.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainViewModel.kt
│ │ │ ├── base
│ │ │ │ ├── BaseActivity.kt
│ │ │ │ ├── BaseFragment.kt
│ │ │ │ └── CameraGuideFragment.kt
│ │ │ ├── cardetail
│ │ │ │ ├── CarDetailFragment.kt
│ │ │ │ ├── CarDetailViewModel.kt
│ │ │ │ ├── CarImagePagerAdapter.kt
│ │ │ │ ├── CarListAdapter.kt
│ │ │ │ ├── OwnerProfileFragment.kt
│ │ │ │ ├── OwnerProfileViewModel.kt
│ │ │ │ └── edit
│ │ │ │ │ ├── CarEditFragment.kt
│ │ │ │ │ ├── CarEditImagesAdapter.kt
│ │ │ │ │ ├── CarEditMapFragment.kt
│ │ │ │ │ └── CarEditViewModel.kt
│ │ │ ├── chatting
│ │ │ │ ├── chat
│ │ │ │ │ ├── ChattingFragment.kt
│ │ │ │ │ └── ChattingViewModel.kt
│ │ │ │ └── list
│ │ │ │ │ ├── ChattingListFragment.kt
│ │ │ │ │ └── ChattingListViewHolder.kt
│ │ │ ├── license
│ │ │ │ ├── guide
│ │ │ │ │ └── LicenseGuideFragment.kt
│ │ │ │ └── registration
│ │ │ │ │ ├── LicenseRegistrationFragment.kt
│ │ │ │ │ └── LicenseRegistrationViewModel.kt
│ │ │ ├── login
│ │ │ │ ├── LoginActivity.kt
│ │ │ │ └── LoginViewModel.kt
│ │ │ ├── map
│ │ │ │ ├── AutoCompleteAdapter.kt
│ │ │ │ ├── MapFragment.kt
│ │ │ │ └── MapViewModel.kt
│ │ │ ├── mypage
│ │ │ │ ├── MyPageFragment.kt
│ │ │ │ ├── MyPageTermsFragment.kt
│ │ │ │ ├── MyPageViewModel.kt
│ │ │ │ ├── license
│ │ │ │ │ ├── MyPageLicenseFragment.kt
│ │ │ │ │ └── MyPageLicenseViewModel.kt
│ │ │ │ └── mycars
│ │ │ │ │ ├── MyCarsFragment.kt
│ │ │ │ │ ├── MyCarsListAdapter.kt
│ │ │ │ │ ├── MyCarsViewModel.kt
│ │ │ │ │ └── OnItemClickListener.kt
│ │ │ ├── nickname
│ │ │ │ ├── NicknameFragment.kt
│ │ │ │ └── NicknameViewModel.kt
│ │ │ ├── notification
│ │ │ │ ├── NotificationListAdapter.kt
│ │ │ │ ├── NotificationListFragment.kt
│ │ │ │ └── NotificationListViewModel.kt
│ │ │ ├── pinkslip
│ │ │ │ ├── guide
│ │ │ │ │ └── PinkSlipGuideFragment.kt
│ │ │ │ └── registration
│ │ │ │ │ ├── PinkSlipRegistrationFragment.kt
│ │ │ │ │ └── PinkSlipRegistrationViewModel.kt
│ │ │ ├── reservation
│ │ │ │ ├── ReservationFragment.kt
│ │ │ │ ├── ReservationViewModel.kt
│ │ │ │ └── check
│ │ │ │ │ ├── ReservationCheckFragment.kt
│ │ │ │ │ └── ReservationCheckViewModel.kt
│ │ │ ├── returncar
│ │ │ │ ├── ReturnCarFragment.kt
│ │ │ │ └── ReturnCarViewModel.kt
│ │ │ ├── review
│ │ │ │ ├── ReviewFragment.kt
│ │ │ │ └── ReviewViewModel.kt
│ │ │ └── transaction
│ │ │ │ ├── TransactionFragment.kt
│ │ │ │ ├── TransactionListAdapter.kt
│ │ │ │ ├── TransactionListFragment.kt
│ │ │ │ ├── TransactionListViewModel.kt
│ │ │ │ └── TransactionPagerAdapter.kt
│ │ │ └── util
│ │ │ ├── DateUtil.kt
│ │ │ ├── DateValidator.kt
│ │ │ ├── EventFlow.kt
│ │ │ ├── FirebaseUtil.kt
│ │ │ ├── ImageUtil.kt
│ │ │ └── RepeatOnStarted.kt
│ └── res
│ │ ├── drawable
│ │ ├── bg_bottom_sheet.xml
│ │ ├── bg_reservation_tag.xml
│ │ ├── bg_textview_round.xml
│ │ ├── bg_transaction_state_tag.xml
│ │ ├── edittext_style.xml
│ │ ├── ic_add.xml
│ │ ├── ic_arrow_right_on_background.xml
│ │ ├── ic_bottom_nav_car.xml
│ │ ├── ic_bottom_nav_chatting.xml
│ │ ├── ic_bottom_nav_my.xml
│ │ ├── ic_broken_image.xml
│ │ ├── ic_camera.xml
│ │ ├── ic_check.xml
│ │ ├── ic_close.xml
│ │ ├── ic_delete.xml
│ │ ├── ic_logo.xml
│ │ ├── ic_mypage_car.xml
│ │ ├── ic_mypage_deal.xml
│ │ ├── ic_mypage_deal_inverse.xml
│ │ ├── ic_mypage_edit.xml
│ │ ├── ic_mypage_license.xml
│ │ ├── ic_mypage_thumb.xml
│ │ ├── ic_notification.xml
│ │ ├── ic_right_arrow.xml
│ │ ├── ic_search.xml
│ │ ├── ic_splash.xml
│ │ ├── img_driving_license.png
│ │ ├── img_pink_slip.jpg
│ │ └── selector_background.xml
│ │ ├── layout
│ │ ├── activity_login.xml
│ │ ├── activity_main.xml
│ │ ├── fragment_camera_guide.xml
│ │ ├── fragment_car_detail.xml
│ │ ├── fragment_car_edit.xml
│ │ ├── fragment_car_edit_map.xml
│ │ ├── fragment_chatting.xml
│ │ ├── fragment_chatting_list.xml
│ │ ├── fragment_license_registration.xml
│ │ ├── fragment_map.xml
│ │ ├── fragment_my_cars.xml
│ │ ├── fragment_mypage.xml
│ │ ├── fragment_mypage_license.xml
│ │ ├── fragment_mypage_terms.xml
│ │ ├── fragment_nickname.xml
│ │ ├── fragment_notification.xml
│ │ ├── fragment_notification_list.xml
│ │ ├── fragment_owner_profile.xml
│ │ ├── fragment_pink_slip_registration.xml
│ │ ├── fragment_reservation.xml
│ │ ├── fragment_reservation_check.xml
│ │ ├── fragment_return_car.xml
│ │ ├── fragment_review.xml
│ │ ├── fragment_transaction.xml
│ │ ├── fragment_transaction_list.xml
│ │ ├── include_car_summary.xml
│ │ ├── include_owner_profile.xml
│ │ ├── include_wait_loading.xml
│ │ ├── item_car_detail_image.xml
│ │ ├── item_car_edit_image.xml
│ │ ├── item_chatting_list.xml
│ │ ├── item_map_search.xml
│ │ ├── item_mypage_carlist.xml
│ │ ├── item_notification_list.xml
│ │ ├── item_owner_car.xml
│ │ └── item_transaction.xml
│ │ ├── menu
│ │ ├── menu_bottom_nav.xml
│ │ ├── menu_main_activity.xml
│ │ └── menu_mycars_item_click.xml
│ │ ├── navigation
│ │ └── nav_main.xml
│ │ ├── raw
│ │ └── lottie_car.json
│ │ ├── values-night
│ │ └── themes.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ ├── styles.xml
│ │ └── themes.xml
│ │ └── xml
│ │ └── file_paths.xml
│ └── test
│ └── java
│ └── com
│ └── gta
│ └── presentation
│ └── ExampleUnitTest.kt
└── settings.gradle.kts
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "boostcamp-ucmc"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/✨-기능-추가.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "✨ 기능 추가"
3 | about: 기능 추가 작업 사항을 입력해주세요.
4 | title: ''
5 | labels: "✨ Feature"
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Feature : [기능 이름]
11 |
12 | ## 설명
13 | - 설명을 작성해주세요.
14 |
15 | ## 해야할 일
16 | - [ ] todo
17 |
18 | ## 기타
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/🐞-버그-신고.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F41E 버그 신고"
3 | about: 버그 내용을 입력해주세요.
4 | title: ''
5 | labels: "\U0001F41E Fix"
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Bug : [기능 이름]
11 |
12 | ## 설명
13 | - 버그에 대한 설명을 작성해주세요.
14 |
15 | ## 버그가 일어나는 상황
16 | 1. 버그가 일어나는 사용자 동작을 설명해주세요.
17 | 2. 버그가 일어나는 사용자 동작을 설명해주세요.
18 | 3. 버그가 일어나는 사용자 동작을 설명해주세요.
19 |
20 | ## 예상 동작
21 | - 어떻게 앱이 동작해야하는지 설명해주세요.
22 |
23 | ## 버그 화면
24 |
25 | ## 기타
26 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## 개요
2 | 내용을 적어주세요.
3 |
4 | ## 작업사항
5 | - 내용을 적어주세요.
6 |
7 | ## 변경된 부분
8 | - 내용을 적어주세요.
9 |
10 | ## 실행 화면
11 |
12 |
13 | ## 특이사항
14 | - 내용을 적어주세요.
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | # release/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Log Files
31 | *.log
32 |
33 | # Android Studio Navigation editor temp files
34 | .navigation/
35 |
36 | # Android Studio captures folder
37 | captures/
38 |
39 | # IntelliJ
40 | *.iml
41 | .idea/workspace.xml
42 | .idea/tasks.xml
43 | .idea/gradle.xml
44 | .idea/assetWizardSettings.xml
45 | .idea/dictionaries
46 | .idea/libraries
47 | # Android Studio 3 in .gitignore file.
48 | .idea/caches
49 | .idea/modules.xml
50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
51 | .idea/navEditor.xml
52 |
53 | # Keystore files
54 | # Uncomment the following lines if you do not want to check your keystore files in.
55 | #*.jks
56 | #*.keystore
57 |
58 | # External native build folder generated in Android Studio 2.2 and later
59 | .externalNativeBuild
60 | .cxx/
61 |
62 | # Google Services (e.g. APIs or Firebase)
63 | google-services.json
64 |
65 | # Freeline
66 | freeline.py
67 | freeline/
68 | freeline_project_description.json
69 |
70 | # fastlane
71 | fastlane/report.xml
72 | fastlane/Preview.html
73 | fastlane/screenshots
74 | fastlane/test_output
75 | fastlane/readme.md
76 |
77 | # Version control
78 | vcs.xml
79 |
80 | # lint
81 | lint/intermediates/
82 | lint/generated/
83 | lint/outputs/
84 | lint/tmp/
85 | # lint/reports/
86 | /.idea/
87 | /app/release/output-metadata.json
88 |
89 | # Mac
90 | *.DS_Store
91 | /presentation/src/main/java/com/gta/presentation/secret/Secrets.kt
92 | /data/src/main/java/com/gta/data/secret/
93 |
94 | /functions/node_modules
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.gta.buildsrc.Configuration
2 |
3 | plugins {
4 | id("com.android.application")
5 | kotlin("android")
6 | kotlin("kapt")
7 | id("dagger.hilt.android.plugin")
8 | id("com.google.firebase.crashlytics")
9 | }
10 |
11 | android {
12 | namespace = "com.gta.ucmc"
13 | compileSdk = Configuration.compileSdk
14 |
15 | defaultConfig {
16 | applicationId = "com.gta.ucmc"
17 | minSdk = Configuration.minSdk
18 | targetSdk = Configuration.targetSdk
19 | versionCode = Configuration.versionCode
20 | versionName = Configuration.versionName
21 |
22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | isMinifyEnabled = false
28 | setProguardFiles(
29 | listOf(
30 | getDefaultProguardFile("proguard-android-optimize.txt"),
31 | "proguard-rules.pro"
32 | )
33 | )
34 | }
35 | }
36 |
37 | compileOptions {
38 | sourceCompatibility = JavaVersion.VERSION_1_8
39 | targetCompatibility = JavaVersion.VERSION_1_8
40 | }
41 | kotlinOptions {
42 | jvmTarget = JavaVersion.VERSION_1_8.toString()
43 | }
44 | buildFeatures {
45 | viewBinding = true
46 | dataBinding = true
47 | }
48 | }
49 |
50 | dependencies {
51 | implementation(project(":data"))
52 | implementation(project(":domain"))
53 | implementation(project(":presentation"))
54 | debugImplementation("com.squareup.leakcanary:leakcanary-android:2.10")
55 |
56 | implementation(Dependencies.Libraries.appLibraries)
57 | kapt(Dependencies.Libraries.appKaptLibraries)
58 | }
59 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
17 |
18 |
21 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/gta/ucmc/LoggerInitializer.kt:
--------------------------------------------------------------------------------
1 | package com.gta.ucmc
2 |
3 | import android.content.Context
4 | import androidx.startup.Initializer
5 | import timber.log.Timber
6 |
7 | class LoggerInitializer : Initializer {
8 | override fun create(context: Context) {
9 | if (BuildConfig.DEBUG) {
10 | Timber.plant(Timber.DebugTree())
11 | }
12 | }
13 |
14 | override fun dependencies(): List>> = emptyList()
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/gta/ucmc/UCMCApplication.kt:
--------------------------------------------------------------------------------
1 | package com.gta.ucmc
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.HiltAndroidApp
5 |
6 | @HiltAndroidApp
7 | class UCMCApplication : Application()
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/android01-UCMC/d326bfea3c8f199fae80f046cf6f9d2b03ca46ca/app/src/main/res/mipmap/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/android01-UCMC/d326bfea3c8f199fae80f046cf6f9d2b03ca46ca/app/src/main/res/mipmap/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
20 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath(Dependencies.Classpaths.GOOGLE_SERVICES)
10 | classpath(Dependencies.Classpaths.NAVIGATION)
11 | classpath(Dependencies.Classpaths.HILT)
12 | classpath(Dependencies.Classpaths.CRASHLYTICS)
13 | classpath(Dependencies.Classpaths.JUNIT5)
14 | classpath(Dependencies.Classpaths.JACOCO)
15 | }
16 | }
17 |
18 | plugins {
19 | id("com.android.application") version Dependencies.Versions.ANDROID apply false
20 | id("com.android.library") version Dependencies.Versions.ANDROID apply false
21 | id("org.jetbrains.kotlin.android") version Dependencies.Versions.KOTLIN apply false
22 | id("org.jetbrains.kotlin.jvm") version Dependencies.Versions.KOTLIN apply false
23 | }
24 |
25 | subprojects {
26 | val ktlint by configurations.creating
27 |
28 | dependencies {
29 | ktlint("com.pinterest:ktlint:0.47.1")
30 | }
31 |
32 | tasks.register("ktlint") {
33 | group = "verification"
34 | description = "Check Kotlin code style."
35 | mainClass.set("com.pinterest.ktlint.Main")
36 | classpath = ktlint
37 | args("--android", "src/**/*.kt")
38 | }
39 |
40 | tasks.register("ktlintFormat") {
41 | group = "formatting"
42 | description = "Fix Kotlin code style deviations."
43 | mainClass.set("com.pinterest.ktlint.Main")
44 | classpath = ktlint
45 | args("--android", "-F", "src/**/*.kt")
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/buildSrc/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | }
4 |
5 | repositories {
6 | google()
7 | mavenCentral()
8 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/gta/buildsrc/Configuration.kt:
--------------------------------------------------------------------------------
1 | package com.gta.buildsrc
2 |
3 | object Configuration {
4 | const val compileSdk = 33
5 | const val minSdk = 23
6 | const val targetSdk = 33
7 | const val versionCode = 1
8 | const val versionName = "0.2.1"
9 | }
10 |
--------------------------------------------------------------------------------
/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | src/main/java/com/gta/data/secret/Secrets.kt
--------------------------------------------------------------------------------
/data/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/android01-UCMC/d326bfea3c8f199fae80f046cf6f9d2b03ca46ca/data/consumer-rules.pro
--------------------------------------------------------------------------------
/data/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/FirebaseModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import com.google.firebase.firestore.ktx.firestore
5 | import com.google.firebase.ktx.Firebase
6 | import com.google.firebase.storage.StorageReference
7 | import com.google.firebase.storage.ktx.storage
8 | import dagger.Module
9 | import dagger.Provides
10 | import dagger.hilt.InstallIn
11 | import dagger.hilt.components.SingletonComponent
12 | import javax.inject.Singleton
13 |
14 | @Module
15 | @InstallIn(SingletonComponent::class)
16 | object FirebaseModule {
17 |
18 | @Singleton
19 | @Provides
20 | fun provideFireStore(): FirebaseFirestore = Firebase.firestore
21 |
22 | @Singleton
23 | @Provides
24 | fun provideStorageReference(): StorageReference = Firebase.storage.reference
25 | }
26 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/LicenseModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import com.gta.data.repository.LicenseRepositoryImpl
5 | import com.gta.data.source.LicenseDataSource
6 | import com.gta.data.source.StorageDataSource
7 | import com.gta.data.source.UserDataSource
8 | import com.gta.domain.repository.LicenseRepository
9 | import dagger.Module
10 | import dagger.Provides
11 | import dagger.hilt.InstallIn
12 | import dagger.hilt.components.SingletonComponent
13 | import javax.inject.Singleton
14 |
15 | @Module
16 | @InstallIn(SingletonComponent::class)
17 | object LicenseModule {
18 | @Singleton
19 | @Provides
20 | fun provideLicenseDataSource(fireStore: FirebaseFirestore): LicenseDataSource =
21 | LicenseDataSource(fireStore)
22 |
23 | @Singleton
24 | @Provides
25 | fun provideLicenseRepository(
26 | userDataSource: UserDataSource,
27 | licenseDataSource: LicenseDataSource,
28 | storageDataSource: StorageDataSource
29 | ): LicenseRepository = LicenseRepositoryImpl(userDataSource, licenseDataSource, storageDataSource)
30 | }
31 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/LoginModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import com.gta.data.repository.LoginRepositoryImpl
5 | import com.gta.data.source.LoginDataSource
6 | import com.gta.data.source.MessageTokenDataSource
7 | import com.gta.data.source.UserDataSource
8 | import com.gta.domain.repository.LoginRepository
9 | import dagger.Module
10 | import dagger.Provides
11 | import dagger.hilt.InstallIn
12 | import dagger.hilt.components.SingletonComponent
13 | import javax.inject.Singleton
14 |
15 | @Module
16 | @InstallIn(SingletonComponent::class)
17 | object LoginModule {
18 |
19 | @Singleton
20 | @Provides
21 | fun provideLoginDataSource(fireStore: FirebaseFirestore): LoginDataSource =
22 | LoginDataSource(fireStore)
23 |
24 | @Singleton
25 | @Provides
26 | fun provideLoginRepository(
27 | userDataSource: UserDataSource,
28 | loginDataSource: LoginDataSource,
29 | messageTokenDataSource: MessageTokenDataSource
30 | ): LoginRepository = LoginRepositoryImpl(userDataSource, loginDataSource, messageTokenDataSource)
31 | }
32 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/MessageTokenModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import com.gta.data.repository.MessageTokenRepositoryImpl
4 | import com.gta.data.source.MessageTokenDataSource
5 | import com.gta.domain.repository.MessageTokenRepository
6 | import dagger.Module
7 | import dagger.Provides
8 | import dagger.hilt.InstallIn
9 | import dagger.hilt.components.SingletonComponent
10 | import javax.inject.Singleton
11 |
12 | @Module
13 | @InstallIn(SingletonComponent::class)
14 | class MessageTokenModule {
15 | @Singleton
16 | @Provides
17 | fun provideMessageTokenRepository(messageTokenDataSource: MessageTokenDataSource): MessageTokenRepository =
18 | MessageTokenRepositoryImpl(messageTokenDataSource)
19 | }
20 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/MyPageModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import com.gta.data.repository.MyPageRepositoryImpl
5 | import com.gta.data.source.MyPageDataSource
6 | import com.gta.data.source.StorageDataSource
7 | import com.gta.domain.repository.MyPageRepository
8 | import dagger.Module
9 | import dagger.Provides
10 | import dagger.hilt.InstallIn
11 | import dagger.hilt.components.SingletonComponent
12 | import javax.inject.Singleton
13 |
14 | @Module
15 | @InstallIn(SingletonComponent::class)
16 | object MyPageModule {
17 |
18 | @Singleton
19 | @Provides
20 | fun provideMyPageRepository(
21 | myPageDataSource: MyPageDataSource,
22 | storageDataSource: StorageDataSource
23 | ): MyPageRepository = MyPageRepositoryImpl(myPageDataSource, storageDataSource)
24 |
25 | @Singleton
26 | @Provides
27 | fun provideMyPageDataSource(
28 | fireStore: FirebaseFirestore
29 | ): MyPageDataSource = MyPageDataSource(fireStore)
30 | }
31 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/NicknameModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import com.gta.data.repository.NicknameRepositoryImpl
4 | import com.gta.data.source.NicknameDataSource
5 | import com.gta.domain.repository.NicknameRepository
6 | import dagger.Module
7 | import dagger.Provides
8 | import dagger.hilt.InstallIn
9 | import dagger.hilt.components.SingletonComponent
10 | import javax.inject.Singleton
11 |
12 | @Module
13 | @InstallIn(SingletonComponent::class)
14 | object NicknameModule {
15 |
16 | @Singleton
17 | @Provides
18 | fun provideNicknameRepository(dataSource: NicknameDataSource): NicknameRepository =
19 | NicknameRepositoryImpl(dataSource)
20 | }
21 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/NotificationModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import com.gta.data.repository.NotificationRepositoryImpl
4 | import com.gta.data.source.CarDataSource
5 | import com.gta.data.source.NotificationDataSource
6 | import com.gta.data.source.ReservationDataSource
7 | import com.gta.data.source.UserDataSource
8 | import com.gta.domain.repository.NotificationRepository
9 | import dagger.Module
10 | import dagger.Provides
11 | import dagger.hilt.InstallIn
12 | import dagger.hilt.components.SingletonComponent
13 | import javax.inject.Singleton
14 |
15 | @Module
16 | @InstallIn(SingletonComponent::class)
17 | object NotificationModule {
18 | @Singleton
19 | @Provides
20 | fun provideNotificationRepository(
21 | notificationDataSource: NotificationDataSource,
22 | reservationDataSource: ReservationDataSource,
23 | userDataSource: UserDataSource,
24 | carDataSource: CarDataSource
25 | ): NotificationRepository =
26 | NotificationRepositoryImpl(
27 | notificationDataSource,
28 | reservationDataSource,
29 | userDataSource,
30 | carDataSource
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/PinkSlipModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import com.gta.data.repository.PinkSlipRepositoryImpl
5 | import com.gta.data.source.CarDataSource
6 | import com.gta.data.source.PinkSlipDataSource
7 | import com.gta.data.source.UserDataSource
8 | import com.gta.domain.repository.PinkSlipRepository
9 | import dagger.Module
10 | import dagger.Provides
11 | import dagger.hilt.InstallIn
12 | import dagger.hilt.components.SingletonComponent
13 | import javax.inject.Singleton
14 |
15 | @Module
16 | @InstallIn(SingletonComponent::class)
17 | object PinkSlipModule {
18 | @Singleton
19 | @Provides
20 | fun providePinkSlipRepository(
21 | carDataSource: CarDataSource,
22 | userDataSource: UserDataSource,
23 | pinkSlipDataSource: PinkSlipDataSource
24 | ): PinkSlipRepository = PinkSlipRepositoryImpl(carDataSource, userDataSource, pinkSlipDataSource)
25 |
26 | @Singleton
27 | @Provides
28 | fun providePinkSlipDataSource(fireStore: FirebaseFirestore): PinkSlipDataSource =
29 | PinkSlipDataSource(fireStore)
30 | }
31 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/Qualifiers.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import javax.inject.Qualifier
4 |
5 | object Qualifiers {
6 | @Qualifier
7 | @Retention(AnnotationRetention.BINARY)
8 | annotation class SearchRetrofit
9 |
10 | @Qualifier
11 | @Retention(AnnotationRetention.BINARY)
12 | annotation class SearchOkHttpClient
13 |
14 | @Qualifier
15 | @Retention(AnnotationRetention.BINARY)
16 | annotation class SearchInterceptor
17 |
18 | @Qualifier
19 | @Retention(AnnotationRetention.BINARY)
20 | annotation class CloudMessageRetrofit
21 |
22 | @Qualifier
23 | @Retention(AnnotationRetention.BINARY)
24 | annotation class CloudMessageOkHttpClient
25 |
26 | @Qualifier
27 | @Retention(AnnotationRetention.BINARY)
28 | annotation class CloudMessageInterceptor
29 | }
30 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/ReservationModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import com.gta.data.repository.ReservationRepositoryImpl
4 | import com.gta.data.source.CarDataSource
5 | import com.gta.data.source.ReservationDataSource
6 | import com.gta.domain.repository.ReservationRepository
7 | import dagger.Module
8 | import dagger.Provides
9 | import dagger.hilt.InstallIn
10 | import dagger.hilt.components.SingletonComponent
11 | import javax.inject.Singleton
12 |
13 | @Module
14 | @InstallIn(SingletonComponent::class)
15 | object ReservationModule {
16 | @Provides
17 | @Singleton
18 | fun providesReservationRepository(reservationDataSource: ReservationDataSource, carDataSource: CarDataSource): ReservationRepository {
19 | return ReservationRepositoryImpl(reservationDataSource, carDataSource)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/di/ReviewModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.di
2 |
3 | import com.gta.data.repository.ReviewRepositoryImpl
4 | import com.gta.data.source.CarDataSource
5 | import com.gta.data.source.ReservationDataSource
6 | import com.gta.data.source.ReviewDataSource
7 | import com.gta.data.source.UserDataSource
8 | import com.gta.domain.repository.ReviewRepository
9 | import dagger.Module
10 | import dagger.Provides
11 | import dagger.hilt.InstallIn
12 | import dagger.hilt.components.SingletonComponent
13 | import javax.inject.Singleton
14 |
15 | @Module
16 | @InstallIn(SingletonComponent::class)
17 | object ReviewModule {
18 |
19 | @Singleton
20 | @Provides
21 | fun provideReviewRepository(
22 | reviewDataSource: ReviewDataSource,
23 | carDataSource: CarDataSource,
24 | reservationDataSource: ReservationDataSource,
25 | userDataSource: UserDataSource
26 | ): ReviewRepository = ReviewRepositoryImpl(
27 | reviewDataSource,
28 | carDataSource,
29 | reservationDataSource,
30 | userDataSource
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/model/AddressResult.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | data class AddressResult(
6 | var meta: Meta2,
7 | var documents: List
8 | )
9 |
10 | data class Meta2(
11 | @SerializedName("total_count") var totalCount: Int
12 | )
13 |
14 | data class Document(
15 | @SerializedName("read_address") var readAddress: RoadAddress,
16 | @SerializedName("address") var addressName: Address
17 | )
18 |
19 | data class Address(
20 | @SerializedName("address_name") var addressName: String,
21 | @SerializedName("region_1depth_name") var region1DepthName: String,
22 | @SerializedName("region_2depth_name") var region2DepthName: String,
23 | @SerializedName("region_3depth_name") var region3DepthName: String,
24 | @SerializedName("mountain_yn") var mountainYn: String,
25 | @SerializedName("main_building_no") var mainBuildingNo: String,
26 | @SerializedName("sub_building_no") var subBuildingNo: String
27 |
28 | )
29 |
30 | data class RoadAddress(
31 | @SerializedName("address_name") var addressName: String,
32 | @SerializedName("region_1depth_name") var region1DepthName: String,
33 | @SerializedName("region_2depth_name") var region2DepthName: String,
34 | @SerializedName("region_3depth_name") var region3DepthName: String,
35 | @SerializedName("road_name") var RoadName: String,
36 | @SerializedName("underground_yn") var undergroundYn: String,
37 | @SerializedName("main_building_no") var mainBuildingNo: String,
38 | @SerializedName("sub_building_no") var subBuildingNo: String,
39 | @SerializedName("building_name") var buildingName: String?,
40 | @SerializedName("zone_no") var zoneNo: String?
41 | )
42 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/model/MessageResult.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | data class MessageResult(
6 | val success: Int,
7 | val failure: Int,
8 | val results: List
9 | )
10 |
11 | data class MessageIdList(@SerializedName("message_id") val messageId: String)
12 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/model/NotificationMessage.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.gta.domain.model.Notification
5 |
6 | data class NotificationMessage(
7 | @SerializedName("to") val to: String,
8 | @SerializedName("priority") val priority: String = "high",
9 | @SerializedName("data") val data: Notification
10 | )
11 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/model/SearchResult.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.gta.domain.model.LocationInfo
5 |
6 | data class SearchResult(
7 | var meta: Meta,
8 | var documents: List
9 | )
10 |
11 | data class Meta(
12 | @SerializedName("total_count") var totalCount: Int,
13 | @SerializedName("pageable_count") var pageableCount: Int,
14 | @SerializedName("is_end") var isEnd: Boolean
15 | )
16 |
17 | data class Place(
18 | @SerializedName("address_name") var addressName: String,
19 | @SerializedName("place_name") var placeName: String?,
20 | @SerializedName("y") var latitude: String,
21 | @SerializedName("x") var longitude: String
22 | )
23 |
24 | fun Place.toLocationInfo(): LocationInfo {
25 | return LocationInfo(this.addressName, this.placeName, this.latitude.toDouble(), this.longitude.toDouble())
26 | }
27 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/model/UserInfo.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.model
2 |
3 | import com.gta.domain.model.DrivingLicense
4 | import com.gta.domain.model.UserProfile
5 |
6 | data class UserInfo(
7 | val nickname: String = "이동훈",
8 | val icon: String = "",
9 | val temperature: Float = 36.5f,
10 | val license: DrivingLicense? = null,
11 | val myCars: List = emptyList(),
12 | val reportCount: Int = 0,
13 | val messageToken: String = ""
14 | )
15 |
16 | fun UserInfo.toProfile(id: String): UserProfile = UserProfile(
17 | id = id,
18 | name = nickname,
19 | temp = temperature,
20 | image = icon
21 | )
22 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/repository/LoginRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.repository
2 |
3 | import android.content.res.Resources.NotFoundException
4 | import com.gta.data.model.toProfile
5 | import com.gta.data.source.LoginDataSource
6 | import com.gta.data.source.MessageTokenDataSource
7 | import com.gta.data.source.UserDataSource
8 | import com.gta.domain.model.FirestoreException
9 | import com.gta.domain.model.UCMCResult
10 | import com.gta.domain.model.UserProfile
11 | import com.gta.domain.repository.LoginRepository
12 | import kotlinx.coroutines.flow.first
13 | import javax.inject.Inject
14 |
15 | class LoginRepositoryImpl @Inject constructor(
16 | private val userDataSource: UserDataSource,
17 | private val loginDataSource: LoginDataSource,
18 | private val messageTokenDataSource: MessageTokenDataSource
19 | ) : LoginRepository {
20 |
21 | override suspend fun checkCurrentUser(uid: String): UCMCResult =
22 | userDataSource.getUser(uid).first()?.let { user ->
23 | UCMCResult.Success(user.toProfile(uid))
24 | } ?: UCMCResult.Error(NotFoundException())
25 |
26 | override suspend fun signUp(uid: String): UCMCResult =
27 | userDataSource.getUser(uid).first()?.let { user ->
28 | UCMCResult.Success(user.toProfile(uid))
29 | } ?: createUser(uid)
30 |
31 | override suspend fun updateUserMessageToken(uid: String): Boolean {
32 | val messageToken = messageTokenDataSource.getMessageToken().first()
33 | return userDataSource.updateUserMessageToken(uid, messageToken).first()
34 | }
35 |
36 | private suspend fun createUser(uid: String): UCMCResult {
37 | val messageToken = messageTokenDataSource.getMessageToken().first()
38 | return loginDataSource.createUser(uid, messageToken).first()?.let { userInfo ->
39 | UCMCResult.Success(userInfo.toProfile(uid))
40 | } ?: UCMCResult.Error(FirestoreException())
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/repository/MapRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.repository
2 |
3 | import com.gta.data.model.toLocationInfo
4 | import com.gta.data.source.MapDataSource
5 | import com.gta.domain.model.LocationInfo
6 | import com.gta.domain.model.NotFoundDataException
7 | import com.gta.domain.model.UCMCResult
8 | import com.gta.domain.repository.MapRepository
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.catch
11 | import kotlinx.coroutines.flow.flow
12 | import javax.inject.Inject
13 |
14 | class MapRepositoryImpl @Inject constructor(private val mapDataSource: MapDataSource) :
15 | MapRepository {
16 | override fun getSearchAddressList(query: String): Flow>> = flow>> {
17 | val addressResult = mapDataSource.getSearchAddressList(query)
18 | val keywordResult = mapDataSource.getSearchKeywordList(query)
19 | val result = mutableListOf()
20 |
21 | addressResult.documents
22 | .map {
23 | it.toLocationInfo()
24 | }.also {
25 | result.addAll(it)
26 | }
27 |
28 | keywordResult.documents
29 | .map {
30 | it.toLocationInfo()
31 | }.also {
32 | result.addAll(it)
33 | }
34 | emit(UCMCResult.Success(result))
35 | }.catch { e ->
36 | emit(UCMCResult.Error(Exception(e.message)))
37 | }
38 |
39 | override fun getSearchCoordinate(
40 | longitude: String,
41 | latitude: String
42 | ): Flow> = flow {
43 | val location = mapDataSource.getSearchCoordinate(
44 | longitude,
45 | latitude
46 | ).documents[0].addressName.addressName
47 |
48 | emit(
49 | if (location != "") {
50 | UCMCResult.Success(location)
51 | } else {
52 | UCMCResult.Error(NotFoundDataException())
53 | }
54 | )
55 | }.catch { e ->
56 | emit(UCMCResult.Error(Exception(e.message)))
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/repository/MessageTokenRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.repository
2 |
3 | import com.gta.data.source.MessageTokenDataSource
4 | import com.gta.domain.repository.MessageTokenRepository
5 | import javax.inject.Inject
6 |
7 | class MessageTokenRepositoryImpl @Inject constructor(private val messageTokenDataSource: MessageTokenDataSource) : MessageTokenRepository {
8 | override suspend fun setMessageToken(token: String): Boolean {
9 | return messageTokenDataSource.setMessageToken(token)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/repository/MyPageRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.repository
2 |
3 | import com.gta.data.source.MyPageDataSource
4 | import com.gta.data.source.StorageDataSource
5 | import com.gta.domain.model.FirestoreException
6 | import com.gta.domain.model.UCMCResult
7 | import com.gta.domain.repository.MyPageRepository
8 | import kotlinx.coroutines.flow.first
9 | import javax.inject.Inject
10 |
11 | class MyPageRepositoryImpl @Inject constructor(
12 | private val myPageDataSource: MyPageDataSource,
13 | private val storageDataSource: StorageDataSource
14 | ) : MyPageRepository {
15 | override suspend fun setThumbnail(uid: String, uri: String): UCMCResult {
16 | val result = storageDataSource.uploadPicture("users/$uid/thumbnail", uri).first() ?: ""
17 | return if (result.isNotEmpty() && myPageDataSource.setThumbnail(uid, result).first()) {
18 | UCMCResult.Success(uri)
19 | } else {
20 | UCMCResult.Error(FirestoreException())
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/repository/NicknameRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.repository
2 |
3 | import com.gta.data.source.NicknameDataSource
4 | import com.gta.domain.model.FirestoreException
5 | import com.gta.domain.model.NicknameState
6 | import com.gta.domain.model.UCMCResult
7 | import com.gta.domain.repository.NicknameRepository
8 | import kotlinx.coroutines.flow.first
9 | import javax.inject.Inject
10 |
11 | class NicknameRepositoryImpl @Inject constructor(
12 | private val dataSource: NicknameDataSource
13 | ) : NicknameRepository {
14 | override fun checkNicknameState(nickname: String): NicknameState =
15 | when {
16 | nickname.length < MIN_LENGTH -> NicknameState.SHORT_LENGTH
17 | SYMBOL_REGEX.containsMatchIn(nickname) -> NicknameState.CONTAIN_SYMBOL
18 | else -> NicknameState.GREAT
19 | }
20 |
21 | override suspend fun updateNickname(uid: String, nickname: String): UCMCResult =
22 | if (dataSource.updateNickname(uid, nickname).first()) {
23 | UCMCResult.Success(Unit)
24 | } else {
25 | UCMCResult.Error(FirestoreException())
26 | }
27 |
28 | companion object {
29 | private const val MIN_LENGTH = 2
30 | private val SYMBOL_REGEX = "[^ㄱ-ㅎㅏ-ㅣ가-힣\\w]+".toRegex()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/repository/PinkSlipRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.repository
2 |
3 | import com.gta.data.model.Car
4 | import com.gta.data.source.CarDataSource
5 | import com.gta.data.source.PinkSlipDataSource
6 | import com.gta.data.source.UserDataSource
7 | import com.gta.domain.model.DuplicatedItemException
8 | import com.gta.domain.model.FirestoreException
9 | import com.gta.domain.model.PinkSlip
10 | import com.gta.domain.model.UCMCResult
11 | import com.gta.domain.repository.PinkSlipRepository
12 | import kotlinx.coroutines.flow.first
13 | import java.nio.ByteBuffer
14 | import javax.inject.Inject
15 |
16 | class PinkSlipRepositoryImpl @Inject constructor(
17 | private val carDataSource: CarDataSource,
18 | private val userDataSource: UserDataSource,
19 | private val pinkSlipDataSource: PinkSlipDataSource
20 | ) : PinkSlipRepository {
21 |
22 | override fun getPinkSlip(buffer: ByteBuffer): PinkSlip =
23 | pinkSlipDataSource.getRandomPinkSlip()
24 |
25 | override suspend fun setPinkSlip(uid: String, pinkSlip: PinkSlip): UCMCResult {
26 | /*
27 | 1. 유저 정보 가져오기
28 | 2. 리스트 뒤에 새로운 차의 ID 붙이고 업데이트
29 | 3. 차 테이블에 새로운 차 추가
30 | */
31 | return userDataSource.getUser(uid).first()?.let { userInfo ->
32 | if (carDataSource.getCar(pinkSlip.informationNumber).first() != null) {
33 | UCMCResult.Error(DuplicatedItemException())
34 | } else {
35 | val updatedCars = userInfo.myCars.plus(pinkSlip.informationNumber)
36 | updateCars(uid, updatedCars, pinkSlip)
37 | }
38 | } ?: UCMCResult.Error(FirestoreException())
39 | }
40 |
41 | private suspend fun updateCars(uid: String, cars: List, pinkSlip: PinkSlip): UCMCResult {
42 | val car = Car(ownerId = uid, pinkSlip = pinkSlip)
43 | val result = pinkSlipDataSource.updateCars(uid, cars).first() && carDataSource.createCar(pinkSlip.informationNumber, car).first()
44 | return if (result) {
45 | UCMCResult.Success(Unit)
46 | } else {
47 | UCMCResult.Error(FirestoreException())
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/repository/ReportRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.repository
2 |
3 | import com.gta.data.source.UserDataSource
4 | import com.gta.domain.model.CoolDownException
5 | import com.gta.domain.model.FirestoreException
6 | import com.gta.domain.model.UCMCResult
7 | import com.gta.domain.repository.ReportRepository
8 | import kotlinx.coroutines.flow.first
9 | import javax.inject.Inject
10 |
11 | class ReportRepositoryImpl @Inject constructor(
12 | private val userDataSource: UserDataSource
13 | ) : ReportRepository {
14 |
15 | private var lastReportedTime = 0L
16 |
17 | override suspend fun reportUser(
18 | uid: String,
19 | currentTime: Long
20 | ): UCMCResult {
21 | val cooldown = REPORT_COOL_DOWN - getTimeAfterReporting(currentTime)
22 | return if (cooldown > 0) {
23 | UCMCResult.Error(CoolDownException(cooldown / 1000 + 1))
24 | } else {
25 | addReportCount(uid, currentTime)
26 | }
27 | }
28 |
29 | private suspend fun addReportCount(uid: String, currentTime: Long): UCMCResult {
30 | return userDataSource.getUser(uid).first()?.let { user ->
31 | val result = userDataSource.addReportCount(uid, user.reportCount + 1).first()
32 | if (result) {
33 | lastReportedTime = currentTime
34 | UCMCResult.Success(Unit)
35 | } else {
36 | UCMCResult.Error(FirestoreException())
37 | }
38 | } ?: UCMCResult.Error(FirestoreException())
39 | }
40 |
41 | private fun getTimeAfterReporting(currentTime: Long): Long =
42 | (currentTime - lastReportedTime)
43 |
44 | companion object {
45 | private const val REPORT_COOL_DOWN = 10000
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/repository/TransactionRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.repository
2 |
3 | import androidx.paging.Pager
4 | import androidx.paging.PagingConfig
5 | import androidx.paging.PagingData
6 | import com.gta.data.source.TransactionDataSource
7 | import com.gta.data.source.TransactionPagingSource
8 | import com.gta.domain.model.SimpleReservation
9 | import com.gta.domain.repository.TransactionRepository
10 | import kotlinx.coroutines.flow.Flow
11 | import javax.inject.Inject
12 |
13 | class TransactionRepositoryImpl @Inject constructor(private val transactionDataSource: TransactionDataSource) : TransactionRepository {
14 | override fun getTransactions(userId: String, isLender: Boolean): Flow> {
15 | return Pager(PagingConfig(transactionDataSource.pagingSize.toInt())) {
16 | TransactionPagingSource(userId, isLender, transactionDataSource)
17 | }.flow
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/repository/UserRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.repository
2 |
3 | import com.gta.data.model.toProfile
4 | import com.gta.data.source.ReservationDataSource
5 | import com.gta.data.source.UserDataSource
6 | import com.gta.domain.model.FirestoreException
7 | import com.gta.domain.model.SimpleReservation
8 | import com.gta.domain.model.UCMCResult
9 | import com.gta.domain.model.UserProfile
10 | import com.gta.domain.repository.UserRepository
11 | import kotlinx.coroutines.channels.awaitClose
12 | import kotlinx.coroutines.flow.Flow
13 | import kotlinx.coroutines.flow.callbackFlow
14 | import kotlinx.coroutines.flow.first
15 | import kotlinx.coroutines.flow.map
16 | import javax.inject.Inject
17 |
18 | class UserRepositoryImpl @Inject constructor(
19 | private val userDataSource: UserDataSource,
20 | private val reservationDataSource: ReservationDataSource
21 | ) : UserRepository {
22 |
23 | override fun getUserProfile(uid: String): Flow> = callbackFlow {
24 | userDataSource.getUser(uid).first()?.let { profile ->
25 | trySend(UCMCResult.Success(profile.toProfile(uid)))
26 | } ?: trySend(UCMCResult.Error(FirestoreException()))
27 | awaitClose()
28 | }
29 |
30 | // 현재 유저가 이 차에 대한 대여중인 예약 정보 (실시간)
31 | override fun getNowReservation(
32 | uid: String,
33 | carId: String
34 | ): Flow> {
35 | return reservationDataSource.getRentingStateReservations(uid).map {
36 | UCMCResult.Success(
37 | it.find { reservation -> reservation.carId == carId }
38 | ?: SimpleReservation()
39 | )
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/service/AddressSearchService.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.service
2 |
3 | import com.gta.data.model.AddressResult
4 | import com.gta.data.model.SearchResult
5 | import retrofit2.http.GET
6 | import retrofit2.http.Query
7 |
8 | interface AddressSearchService {
9 | @GET("v2/local/search/address.json")
10 | suspend fun requestAddressList(
11 | @Query("query") query: String
12 | ): SearchResult
13 |
14 | @GET("v2/local/search/keyword.json")
15 | suspend fun requestKeywordList(
16 | @Query("query") query: String
17 | ): SearchResult
18 |
19 | @GET("v2/local/geo/coord2address.json")
20 | suspend fun requestLocation(
21 | @Query("x") longitude: String,
22 | @Query("y") latitude: String
23 | ): AddressResult
24 | }
25 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/service/CloudMessageService.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.service
2 |
3 | import com.gta.data.model.MessageResult
4 | import com.gta.data.model.NotificationMessage
5 | import retrofit2.http.Body
6 | import retrofit2.http.POST
7 |
8 | interface CloudMessageService {
9 | @POST("fcm/send")
10 | suspend fun sendNotificationMessage(
11 | @Body message: NotificationMessage
12 | ): MessageResult
13 | }
14 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/LicenseDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import com.gta.domain.model.DrivingLicense
5 | import kotlinx.coroutines.channels.awaitClose
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.callbackFlow
8 | import javax.inject.Inject
9 |
10 | class LicenseDataSource @Inject constructor(
11 | private val fireStore: FirebaseFirestore
12 | ) {
13 | fun registerLicense(uid: String, license: DrivingLicense): Flow = callbackFlow {
14 | fireStore.collection("users").document(uid).update("license", license)
15 | .addOnCompleteListener {
16 | trySend(it.isSuccessful)
17 | }
18 | awaitClose()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/LoginDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import com.gta.data.model.UserInfo
5 | import kotlinx.coroutines.channels.awaitClose
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.callbackFlow
8 | import javax.inject.Inject
9 |
10 | class LoginDataSource @Inject constructor(
11 | private val fireStore: FirebaseFirestore
12 | ) {
13 | fun createUser(uid: String, messageToken: String): Flow = callbackFlow {
14 | val userInfo = UserInfo(messageToken = messageToken)
15 | fireStore
16 | .collection("users")
17 | .document(uid)
18 | .set(userInfo)
19 | .addOnCompleteListener {
20 | if (it.isSuccessful) {
21 | trySend(userInfo)
22 | } else {
23 | trySend(null)
24 | }
25 | }
26 | awaitClose()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/MapDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import com.gta.data.model.AddressResult
4 | import com.gta.data.model.SearchResult
5 | import com.gta.data.service.AddressSearchService
6 | import javax.inject.Inject
7 |
8 | class MapDataSource @Inject constructor(private val service: AddressSearchService) {
9 | suspend fun getSearchAddressList(query: String): SearchResult {
10 | return service.requestAddressList(query)
11 | }
12 |
13 | suspend fun getSearchKeywordList(query: String): SearchResult {
14 | return service.requestKeywordList(query)
15 | }
16 |
17 | suspend fun getSearchCoordinate(longitude: String, latitude: String): AddressResult {
18 | return service.requestLocation(longitude, latitude)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/MessageTokenDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import android.content.Context
4 | import androidx.datastore.core.DataStore
5 | import androidx.datastore.preferences.core.Preferences
6 | import androidx.datastore.preferences.core.edit
7 | import androidx.datastore.preferences.core.stringPreferencesKey
8 | import androidx.datastore.preferences.preferencesDataStore
9 | import dagger.hilt.android.qualifiers.ApplicationContext
10 | import kotlinx.coroutines.flow.Flow
11 | import kotlinx.coroutines.flow.map
12 | import javax.inject.Inject
13 |
14 | private val Context.datastore: DataStore by preferencesDataStore(name = "preferences")
15 |
16 | class MessageTokenDataSource @Inject constructor(@ApplicationContext private val context: Context) {
17 | private val tokenKey = stringPreferencesKey(name = "token")
18 |
19 | suspend fun setMessageToken(token: String): Boolean {
20 | return kotlin.runCatching {
21 | context.datastore.edit { preferences ->
22 | preferences[tokenKey] = token
23 | }
24 | }.isSuccess
25 | }
26 |
27 | fun getMessageToken(): Flow {
28 | return context.datastore.data.map { preferences ->
29 | preferences[tokenKey] ?: ""
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/MyPageDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import kotlinx.coroutines.channels.awaitClose
5 | import kotlinx.coroutines.flow.Flow
6 | import kotlinx.coroutines.flow.callbackFlow
7 | import javax.inject.Inject
8 |
9 | class MyPageDataSource @Inject constructor(
10 | private val fireStore: FirebaseFirestore
11 | ) {
12 | fun setThumbnail(uid: String, downloadUrl: String): Flow = callbackFlow {
13 | fireStore.collection("users").document(uid).update("icon", downloadUrl).addOnCompleteListener {
14 | trySend(it.isSuccessful)
15 | }
16 | awaitClose()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/NicknameDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import kotlinx.coroutines.channels.awaitClose
5 | import kotlinx.coroutines.flow.Flow
6 | import kotlinx.coroutines.flow.callbackFlow
7 | import javax.inject.Inject
8 |
9 | class NicknameDataSource @Inject constructor(
10 | private val fireStore: FirebaseFirestore
11 | ) {
12 | fun updateNickname(uid: String, nickname: String): Flow = callbackFlow {
13 | fireStore.collection("users").document(uid).update("nickname", nickname).addOnCompleteListener {
14 | trySend(it.isSuccessful)
15 | }
16 | awaitClose()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/NotificationDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import com.google.firebase.firestore.DocumentSnapshot
4 | import com.google.firebase.firestore.FirebaseFirestore
5 | import com.google.firebase.firestore.Query
6 | import com.google.firebase.firestore.QuerySnapshot
7 | import com.gta.data.model.NotificationMessage
8 | import com.gta.data.service.CloudMessageService
9 | import com.gta.domain.model.Notification
10 | import kotlinx.coroutines.channels.awaitClose
11 | import kotlinx.coroutines.flow.Flow
12 | import kotlinx.coroutines.flow.callbackFlow
13 | import kotlinx.coroutines.tasks.await
14 | import javax.inject.Inject
15 |
16 | class NotificationDataSource @Inject constructor(
17 | private val cloudMessageService: CloudMessageService,
18 | private val fireStore: FirebaseFirestore
19 | ) {
20 | suspend fun sendNotification(notification: Notification, to: String): Boolean {
21 | return kotlin.runCatching {
22 | cloudMessageService.sendNotificationMessage(
23 | NotificationMessage(
24 | to = to,
25 | data = notification
26 | )
27 | )
28 | }.isSuccess
29 | }
30 |
31 | fun saveNotification(
32 | notification: Notification,
33 | userId: String,
34 | notificationId: String
35 | ): Flow =
36 | callbackFlow {
37 | fireStore.document("users/$userId/notifications/$notificationId").set(notification)
38 | .addOnCompleteListener {
39 | trySend(it.isSuccessful)
40 | }
41 | awaitClose()
42 | }
43 |
44 | private val ITEMS_PER_PAGE = 10L
45 |
46 | suspend fun getNotificationInfoCurrentItem(userId: String): QuerySnapshot {
47 | return fireStore.collection("users/$userId/notifications")
48 | .orderBy("timestamp", Query.Direction.DESCENDING)
49 | .limit(ITEMS_PER_PAGE)
50 | .get()
51 | .await()
52 | }
53 |
54 | suspend fun getNotificationInfoNextItem(userId: String, doc: DocumentSnapshot): QuerySnapshot {
55 | return fireStore.collection("users/$userId/notifications")
56 | .orderBy("timestamp", Query.Direction.DESCENDING)
57 | .limit(ITEMS_PER_PAGE)
58 | .startAfter(doc)
59 | .get()
60 | .await()
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/NotificationPagingSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import androidx.paging.PagingSource
4 | import androidx.paging.PagingState
5 | import com.google.firebase.firestore.QuerySnapshot
6 | import com.gta.data.repository.NotificationRepositoryImpl
7 | import com.gta.domain.model.Notification
8 | import com.gta.domain.model.NotificationInfo
9 | import com.gta.domain.model.toInfo
10 |
11 | class NotificationPagingSource(
12 | private val userId: String,
13 | private val dataSource: NotificationDataSource,
14 | private val repository: NotificationRepositoryImpl
15 | ) : PagingSource() {
16 | override fun getRefreshKey(state: PagingState): QuerySnapshot? =
17 | null
18 |
19 | override suspend fun load(params: LoadParams): LoadResult {
20 | return try {
21 | val currentPage = params.key ?: dataSource.getNotificationInfoCurrentItem(userId)
22 |
23 | val nextPage: QuerySnapshot? =
24 | if (currentPage.size() > 0) {
25 | val lastDocumentSnapshot = currentPage.documents[currentPage.size() - 1]
26 | dataSource.getNotificationInfoNextItem(userId, lastDocumentSnapshot)
27 | } else {
28 | null
29 | }
30 |
31 | LoadResult.Page(
32 | data = currentPage.map {
33 | repository.getNotificationInfoDetailItem(
34 | it.toObject(
35 | Notification::class.java
36 | ).toInfo(it.id)
37 | )
38 | },
39 | prevKey = null,
40 | nextKey = nextPage
41 | )
42 | } catch (e: Exception) {
43 | LoadResult.Error(e)
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/ReviewDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import com.gta.domain.model.UserReview
5 | import kotlinx.coroutines.channels.awaitClose
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.callbackFlow
8 | import javax.inject.Inject
9 |
10 | class ReviewDataSource @Inject constructor(
11 | private val fireStore: FirebaseFirestore
12 | ) {
13 | fun isExistReview(opponentId: String, reservationId: String): Flow = callbackFlow {
14 | fireStore
15 | .collection("users")
16 | .document(opponentId)
17 | .collection("reviews")
18 | .document(reservationId)
19 | .get()
20 | .addOnCompleteListener {
21 | trySend(it.isSuccessful && it.result.exists())
22 | }
23 | awaitClose()
24 | }
25 |
26 | fun addReview(opponentId: String, reservationId: String, review: UserReview): Flow = callbackFlow {
27 | fireStore
28 | .collection("users")
29 | .document(opponentId)
30 | .collection("reviews")
31 | .document(reservationId)
32 | .set(review)
33 | .addOnCompleteListener {
34 | trySend(it.isSuccessful)
35 | }
36 | awaitClose()
37 | }
38 |
39 | fun updateTemperature(opponentId: String, temperature: Float): Flow = callbackFlow {
40 | fireStore
41 | .collection("users")
42 | .document(opponentId)
43 | .update("temperature", temperature)
44 | .addOnCompleteListener {
45 | trySend(it.isSuccessful)
46 | }
47 | awaitClose()
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/StorageDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import android.net.Uri
4 | import com.google.firebase.storage.StorageReference
5 | import kotlinx.coroutines.channels.awaitClose
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.callbackFlow
8 | import javax.inject.Inject
9 |
10 | class StorageDataSource @Inject constructor(
11 | private val storageReference: StorageReference
12 | ) {
13 | fun uploadPicture(path: String, uri: String): Flow = callbackFlow {
14 | val image = Uri.parse(uri)
15 | val ref = storageReference.child(path)
16 | ref.putFile(image).continueWithTask {
17 | ref.downloadUrl
18 | }.addOnCompleteListener {
19 | if (it.isSuccessful) {
20 | trySend(it.result.toString())
21 | } else {
22 | trySend(null)
23 | }
24 | }
25 | awaitClose()
26 | }
27 |
28 | fun deletePicture(path: String): Flow = callbackFlow {
29 | storageReference.storage.getReferenceFromUrl(path).delete().addOnCompleteListener {
30 | trySend(it.isSuccessful)
31 | }
32 | awaitClose()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/TransactionDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import com.google.firebase.firestore.DocumentSnapshot
4 | import com.google.firebase.firestore.FirebaseFirestore
5 | import com.google.firebase.firestore.QuerySnapshot
6 | import kotlinx.coroutines.tasks.await
7 | import javax.inject.Inject
8 |
9 | class TransactionDataSource @Inject constructor(private val fireStore: FirebaseFirestore) {
10 | val pagingSize = 10L
11 |
12 | suspend fun getYourCarTransactions(uid: String): QuerySnapshot {
13 | return fireStore.collection("reservations")
14 | .whereEqualTo("lenderId", uid)
15 | .limit(pagingSize)
16 | .get()
17 | .await()
18 | }
19 |
20 | suspend fun getYourCarTransactionsFromCursor(
21 | uid: String,
22 | docCursor: DocumentSnapshot
23 | ): QuerySnapshot {
24 | return fireStore.collection("reservations")
25 | .whereEqualTo("lenderId", uid)
26 | .limit(pagingSize)
27 | .startAfter(docCursor)
28 | .get()
29 | .await()
30 | }
31 |
32 | suspend fun getMyCarTransactions(uid: String): QuerySnapshot {
33 | return fireStore.collection("reservations")
34 | .whereEqualTo("ownerId", uid)
35 | .limit(pagingSize)
36 | .get()
37 | .await()
38 | }
39 |
40 | suspend fun getMyCarTransactionsFromCursor(
41 | uid: String,
42 | docCursor: DocumentSnapshot
43 | ): QuerySnapshot {
44 | return fireStore.collection("reservations")
45 | .whereEqualTo("ownerId", uid)
46 | .limit(pagingSize)
47 | .startAfter(docCursor)
48 | .get()
49 | .await()
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/TransactionPagingSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import androidx.paging.PagingSource
4 | import androidx.paging.PagingState
5 | import com.google.firebase.firestore.DocumentSnapshot
6 | import com.google.firebase.firestore.QuerySnapshot
7 | import com.gta.domain.model.Reservation
8 | import com.gta.domain.model.SimpleReservation
9 | import com.gta.domain.model.toSimpleReservation
10 | import kotlin.reflect.KSuspendFunction1
11 | import kotlin.reflect.KSuspendFunction2
12 |
13 | class TransactionPagingSource(
14 | private val userId: String,
15 | isLender: Boolean,
16 | dataSource: TransactionDataSource
17 | ) : PagingSource() {
18 |
19 | private val getTransactions: KSuspendFunction1 =
20 | if (isLender) dataSource::getYourCarTransactions else dataSource::getMyCarTransactions
21 |
22 | private val getTransactionsNext: KSuspendFunction2 =
23 | if (isLender) dataSource::getYourCarTransactionsFromCursor else dataSource::getMyCarTransactionsFromCursor
24 |
25 | override fun getRefreshKey(state: PagingState): QuerySnapshot? =
26 | null
27 |
28 | override suspend fun load(params: LoadParams): LoadResult {
29 | return try {
30 | val currentPage = params.key ?: getTransactions(userId)
31 | val nextPage: QuerySnapshot? =
32 | if (currentPage.size() > 0) {
33 | val lastDocumentSnapshot = currentPage.documents[currentPage.size() - 1]
34 | getTransactionsNext(userId, lastDocumentSnapshot)
35 | } else {
36 | null
37 | }
38 |
39 | LoadResult.Page(
40 | data = currentPage.map { snapshot ->
41 | snapshot.toObject(Reservation::class.java).toSimpleReservation(snapshot.id)
42 | },
43 | prevKey = null,
44 | nextKey = nextPage
45 | )
46 | } catch (e: Exception) {
47 | LoadResult.Error(e)
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/data/src/main/java/com/gta/data/source/UserDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data.source
2 |
3 | import com.google.firebase.firestore.FirebaseFirestore
4 | import com.gta.data.model.UserInfo
5 | import kotlinx.coroutines.channels.awaitClose
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.callbackFlow
8 | import kotlinx.coroutines.tasks.await
9 | import javax.inject.Inject
10 |
11 | class UserDataSource @Inject constructor(
12 | private val fireStore: FirebaseFirestore
13 | ) {
14 | fun getUser(uid: String): Flow = callbackFlow {
15 | fireStore.collection("users").document(uid).get().addOnCompleteListener {
16 | if (it.isSuccessful) {
17 | trySend(it.result.toObject(UserInfo::class.java))
18 | } else {
19 | trySend(null)
20 | }
21 | }
22 | awaitClose()
23 | }
24 |
25 | suspend fun getSuspendUser(uid: String): UserInfo? {
26 | return fireStore.collection("users")
27 | .document(uid)
28 | .get()
29 | .await()
30 | .toObject(UserInfo::class.java)
31 | }
32 |
33 | fun removeCar(uid: String, newCars: List): Flow = callbackFlow {
34 | fireStore.collection("users").document(uid).update("myCars", newCars)
35 | .addOnCompleteListener {
36 | trySend(it.isSuccessful)
37 | }
38 | awaitClose()
39 | }
40 |
41 | fun updateUserMessageToken(uid: String, token: String) = callbackFlow {
42 | fireStore.collection("users").document(uid).update("messageToken", token)
43 | .addOnCompleteListener {
44 | trySend(it.isSuccessful)
45 | }
46 | awaitClose()
47 | }
48 |
49 | fun addReportCount(uid: String, count: Int): Flow = callbackFlow {
50 | fireStore.collection("users").document(uid).update("reportCount", count).addOnCompleteListener {
51 | trySend(it.isSuccessful)
52 | }
53 | awaitClose()
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/data/src/test/java/com/gta/data/MessageTokenRepositoryUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data
2 |
3 | import com.gta.data.repository.MessageTokenRepositoryImpl
4 | import com.gta.data.source.MessageTokenDataSource
5 | import com.gta.domain.repository.MessageTokenRepository
6 | import kotlinx.coroutines.runBlocking
7 | import org.junit.jupiter.api.Assertions
8 | import org.junit.jupiter.api.DisplayName
9 | import org.junit.jupiter.api.Test
10 | import org.junit.jupiter.api.extension.ExtendWith
11 | import org.mockito.Mock
12 | import org.mockito.Mockito.anyString
13 | import org.mockito.Mockito.`when`
14 | import org.mockito.junit.jupiter.MockitoExtension
15 |
16 | @ExtendWith(MockitoExtension::class)
17 | class MessageTokenRepositoryUnitTest(@Mock private val messageTokenDataSource: MessageTokenDataSource) {
18 | private val repository: MessageTokenRepository =
19 | MessageTokenRepositoryImpl(messageTokenDataSource)
20 | private val token = anyString()
21 |
22 | @Test
23 | @DisplayName("setMessageToken: 토큰이 정상적으로 저장되면 경우 true를 반환한다.")
24 | fun Should_True_When_storeTokenSuccess() = runBlocking {
25 | `when`(messageTokenDataSource.setMessageToken(token)).thenReturn(true)
26 |
27 | val result = repository.setMessageToken(token)
28 |
29 | Assertions.assertEquals(true, result)
30 | }
31 |
32 | @Test
33 | @DisplayName("setMessageToken: 토큰이 정상적으로 저장되지 않으면 false를 반환한다.")
34 | fun Should_False_When_storeTokenFail() = runBlocking {
35 | `when`(messageTokenDataSource.setMessageToken(token)).thenReturn(false)
36 |
37 | val result = repository.setMessageToken(token)
38 |
39 | Assertions.assertEquals(false, result)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/data/src/test/java/com/gta/data/TestUtil.kt:
--------------------------------------------------------------------------------
1 | package com.gta.data
2 |
3 | const val GOOD_UID = "GoodDonghoon"
4 | const val BAD_UID = "BadDonghoon"
5 | const val EXCEPTION_UID = "ErrorDonghoon"
6 | const val GOOD_URI = "GoodUri"
7 | const val BAD_URI = "BadUri"
8 | const val GOOD_RESERVATION = "goodReservation"
9 | const val BAD_RESERVATION = "badReservation"
10 | const val DUPLICATED_RESERVATION = "duplicatedReservation"
11 | const val GOOD_CAR = "goodCar"
12 | const val BAD_CAR = "badCar"
13 | const val DUPLICATED_CAR = "duplicatedCar"
14 |
--------------------------------------------------------------------------------
/domain/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/domain/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-library")
3 | kotlin("jvm")
4 | }
5 |
6 | java {
7 | sourceCompatibility = JavaVersion.VERSION_1_8
8 | targetCompatibility = JavaVersion.VERSION_1_8
9 | }
10 |
11 | dependencies {
12 | implementation(Dependencies.Libraries.domainLibraries)
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/AvailableDate.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class AvailableDate(
4 | val start: Long = 0,
5 | val end: Long = 0
6 | )
7 |
8 | fun AvailableDate.toPair(): Pair {
9 | return start to end
10 | }
11 |
12 | fun List.toPairList(): List> {
13 | return this.map { it.toPair() }
14 | }
15 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/CarDetail.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | enum class RentState(val string: String) {
4 | AVAILABLE("대여 가능"), UNAVAILABLE("대여 불가능"), RENTED("대여중")
5 | }
6 |
7 | data class CarDetail(
8 | val id: String = "정보 없음",
9 | val carType: String = "정보 없음",
10 | val model: String = "정보 없음",
11 | val year: Int = 0,
12 | val licensePlate: String = "정보 없음",
13 | val price: Int = 0,
14 | val location: String = "정보 없음",
15 | val rentState: RentState = RentState.UNAVAILABLE,
16 | val comment: String = "",
17 | val availableDate: AvailableDate = AvailableDate(),
18 | val images: List = emptyList(),
19 | val owner: UserProfile = UserProfile(),
20 | val coordinate: Coordinate = Coordinate()
21 | )
22 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/CarRentInfo.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class CarRentInfo(
4 | val images: List = emptyList(),
5 | val model: String = "",
6 | val price: Int = 10000,
7 | val comment: String = "차였어요",
8 | val availableDate: AvailableDate = AvailableDate(),
9 | val reservationDates: List = emptyList(),
10 | val ownerId: String = "정보 없음"
11 | )
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/Coordinate.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class Coordinate(
4 | val latitude: Double = 37.3588798,
5 | val longitude: Double = 127.1051933
6 | ) : java.io.Serializable
7 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/DrivingLicense.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class DrivingLicense(
4 | val id: String = "",
5 | val name: String = "",
6 | val uri: String = "",
7 | val residentNumberFront: String = "",
8 | val residentNumberBack: String = "",
9 | val aptitudeTestDate: String = "",
10 | val expireDate: String = ""
11 | )
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/InsuranceOption.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | enum class InsuranceOption(val price: Int) {
4 | LOW(6310), MEDIUM(5210), HIGH(3870)
5 | }
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/LocationInfo.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class LocationInfo(
4 | val address: String,
5 | val name: String?,
6 | val latitude: Double,
7 | val longitude: Double
8 | )
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/NicknameState.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | enum class NicknameState {
4 | IDLE,
5 | GREAT,
6 | SHORT_LENGTH,
7 | CONTAIN_SYMBOL
8 | }
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/Notification.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class Notification(
4 | val type: String = "",
5 | val message: String = "",
6 | val reservationId: String = "정보 없음",
7 | val fromId: String = "정보 없음",
8 | val timestamp: Long = 0
9 | )
10 |
11 | enum class NotificationType(val title: String, val msg: String) {
12 | REQUEST_RESERVATION(
13 | "예약 요청",
14 | "자동차 대여 예약 요청이 도착했습니다."
15 | ),
16 | ACCEPT_RESERVATION(
17 | "예약 수락",
18 | "자동차 대여 예약이 완료됐습니다."
19 | ),
20 | DECLINE_RESERVATION(
21 | "예약 거절",
22 | "자동차 대여 예약이 거절됐습니다."
23 | ),
24 | RETURN_CAR(
25 | "차량 반납",
26 | "대여자가 차를 반납했습니다."
27 | )
28 | }
29 |
30 | fun Notification.toInfo(id: String): NotificationInfo = NotificationInfo(
31 | id = id,
32 | type = NotificationType.values().filter { it.title == type }[0],
33 | reservationId = reservationId,
34 | fromId = fromId,
35 | fromNickName = fromId,
36 | carImage = "",
37 | licensePlate = "정보 없음",
38 | date = id.substringBefore("-")
39 | )
40 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/NotificationInfo.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class NotificationInfo(
4 | val id: String,
5 | val type: NotificationType,
6 | val reservationId: String,
7 | val fromId: String,
8 | var fromNickName: String,
9 | var carImage: String?,
10 | var licensePlate: String,
11 | var date: String
12 | )
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/PinkSlip.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class PinkSlip(
4 | val id: String = "",
5 | val informationNumber: String = "",
6 | val owner: String = "",
7 | val type: String = "",
8 | val model: String = "",
9 | val year: Int = 0
10 | )
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/Reservation.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | enum class ReservationState(val state: Int) {
4 | CANCEL(-1), PENDING(0), ACCEPT(1), RENTING(2), DONE(-2)
5 | }
6 |
7 | data class Reservation(
8 | val carId: String = "정보 없음",
9 | val lenderId: String = "정보 없음",
10 | val ownerId: String = "정보 없음",
11 | val state: Int = ReservationState.PENDING.state,
12 | val reservationDate: AvailableDate = AvailableDate(),
13 | val price: Long = 0,
14 | val insuranceOption: String = InsuranceOption.LOW.name
15 | )
16 |
17 | fun Reservation.toSimpleReservation(reservationId: String): SimpleReservation = SimpleReservation(
18 | reservationId = reservationId,
19 | carId = carId,
20 | reservationState = state,
21 | reservationDate = reservationDate
22 | )
23 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/ReviewDTO.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class ReviewDTO(
4 | val reviewType: ReviewType = ReviewType.LENDER_TO_OWNER,
5 | val opponent: UserProfile = UserProfile(),
6 | val carImage: String = "",
7 | val carModel: String = ""
8 | )
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/ReviewType.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | enum class ReviewType {
4 | OWNER_TO_LENDER,
5 | LENDER_TO_OWNER
6 | }
7 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/SimpleCar.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class SimpleCar(
4 | val id: String = "정보 없음",
5 | val image: String = "",
6 | val carType: String = "정보 없음",
7 | val model: String = "정보 없음",
8 | val year: Int = 0,
9 | val price: Int = 0,
10 | val coordinate: Coordinate = Coordinate(),
11 | val licensePlate: String = "정보 없음"
12 | )
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/SimpleReservation.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class SimpleReservation(
4 | val reservationId: String = "",
5 | val carId: String = "",
6 | val reservationState: Int = ReservationState.PENDING.state,
7 | val reservationDate: AvailableDate = AvailableDate()
8 | )
9 |
10 | fun SimpleReservation.toTransaction(simpleCar: SimpleCar): Transaction {
11 | return Transaction(
12 | reservationId = reservationId,
13 | reservationState = ReservationState.values().find { it.state == reservationState } ?: ReservationState.RENTING,
14 | reservationDate = reservationDate,
15 | carModel = simpleCar.model,
16 | thumbnailImg = simpleCar.image
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/Transaction.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class Transaction(
4 | val reservationId: String = "",
5 | val reservationState: ReservationState = ReservationState.PENDING,
6 | val reservationDate: AvailableDate = AvailableDate(),
7 | val carModel: String = "",
8 | val thumbnailImg: String = ""
9 | )
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/UCMCException.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | class CoolDownException(val cooldown: Long) : Exception()
4 | class FirestoreException : Exception()
5 | class DuplicatedItemException : Exception()
6 | class DeleteFailException : Exception()
7 | class UserNotFoundException : Exception()
8 | class ExpiredItemException : Exception()
9 | class UpdateFailException : Exception()
10 | class NotFoundDataException : Exception()
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/UCMCResult.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | sealed class UCMCResult {
4 | data class Success(val data: T) : UCMCResult()
5 | data class Error(val e: Exception) : UCMCResult()
6 | }
7 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/UpdateCar.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class UpdateCar(
4 | val images: List = emptyList(),
5 | val price: Int,
6 | val comment: String = "",
7 | val rentState: RentState,
8 | val availableDate: AvailableDate,
9 | val location: String,
10 | val coordinate: Coordinate
11 | )
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/UserProfile.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class UserProfile(
4 | val id: String = "정보 없음",
5 | val name: String = "정보 없음",
6 | val temp: Float = 0.0f,
7 | val image: String? = ""
8 | )
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/model/UserReview.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.model
2 |
3 | data class UserReview(
4 | val from: String = "정보 없음",
5 | val comment: String = "",
6 | val rating: Float = 5.0f
7 | )
8 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/CarRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.CarDetail
4 | import com.gta.domain.model.CarRentInfo
5 | import com.gta.domain.model.Coordinate
6 | import com.gta.domain.model.RentState
7 | import com.gta.domain.model.SimpleCar
8 | import com.gta.domain.model.UCMCResult
9 | import com.gta.domain.model.UpdateCar
10 | import kotlinx.coroutines.flow.Flow
11 |
12 | interface CarRepository {
13 | fun getOwnerId(carId: String): Flow>
14 | fun getCarRentState(carId: String): Flow>
15 | fun getCarData(carId: String): Flow>
16 | fun updateCarDetail(carId: String, update: UpdateCar): Flow
17 | fun getCarRentInfo(carId: String): Flow>
18 | fun getSimpleCar(carId: String): Flow
19 | fun getSimpleCarList(ownerId: String): Flow>>
20 | fun getAllCars(): Flow>
21 | fun getNearCars(min: Coordinate, max: Coordinate): Flow>>
22 | suspend fun removeCar(userId: String, carId: String): UCMCResult
23 | fun setCarImagesStorage(carId: String, images: List): Flow>>
24 | fun deleteImagesStorage(images: List): Flow
25 | }
26 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/LicenseRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.DrivingLicense
4 | import com.gta.domain.model.UCMCResult
5 | import java.nio.ByteBuffer
6 |
7 | interface LicenseRepository {
8 | fun getLicenseFromImage(buffer: ByteBuffer): DrivingLicense
9 | suspend fun getLicenseFromDatabase(uid: String): UCMCResult
10 | suspend fun setLicense(uid: String, license: DrivingLicense, uri: String): UCMCResult
11 | }
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/LoginRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.model.UserProfile
5 |
6 | interface LoginRepository {
7 | suspend fun checkCurrentUser(uid: String): UCMCResult
8 | suspend fun signUp(uid: String): UCMCResult
9 | suspend fun updateUserMessageToken(uid: String): Boolean
10 | }
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/MapRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.LocationInfo
4 | import com.gta.domain.model.UCMCResult
5 | import kotlinx.coroutines.flow.Flow
6 |
7 | interface MapRepository {
8 | fun getSearchCoordinate(longitude: String, latitude: String): Flow>
9 | fun getSearchAddressList(query: String): Flow>>
10 | }
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/MessageTokenRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | interface MessageTokenRepository {
4 | suspend fun setMessageToken(token: String): Boolean
5 | }
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/MyPageRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.UCMCResult
4 |
5 | interface MyPageRepository {
6 | suspend fun setThumbnail(uid: String, uri: String): UCMCResult
7 | }
8 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/NicknameRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.NicknameState
4 | import com.gta.domain.model.UCMCResult
5 |
6 | interface NicknameRepository {
7 | fun checkNicknameState(nickname: String): NicknameState
8 | suspend fun updateNickname(uid: String, nickname: String): UCMCResult
9 | }
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/NotificationRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import androidx.paging.PagingData
4 | import com.gta.domain.model.Notification
5 | import com.gta.domain.model.NotificationInfo
6 | import com.gta.domain.model.UCMCResult
7 | import kotlinx.coroutines.flow.Flow
8 |
9 | interface NotificationRepository {
10 | suspend fun sendNotification(notification: Notification, receiverId: String): UCMCResult
11 | suspend fun saveNotification(notification: Notification, userId: String): UCMCResult
12 | fun getNotificationInfoList(userId: String): Flow>
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/PinkSlipRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.PinkSlip
4 | import com.gta.domain.model.UCMCResult
5 | import java.nio.ByteBuffer
6 |
7 | interface PinkSlipRepository {
8 | fun getPinkSlip(buffer: ByteBuffer): PinkSlip
9 | suspend fun setPinkSlip(uid: String, pinkSlip: PinkSlip): UCMCResult
10 | }
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/ReportRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.UCMCResult
4 |
5 | interface ReportRepository {
6 | suspend fun reportUser(
7 | uid: String,
8 | currentTime: Long = System.currentTimeMillis()
9 | ): UCMCResult
10 | }
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/ReservationRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.Reservation
4 | import com.gta.domain.model.ReservationState
5 | import com.gta.domain.model.UCMCResult
6 | import kotlinx.coroutines.flow.Flow
7 |
8 | interface ReservationRepository {
9 | suspend fun createReservation(reservation: Reservation): UCMCResult
10 | fun getReservationInfo(reservationId: String): Flow>
11 | fun getReservationCar(reservationId: String): Flow
12 | suspend fun updateReservationState(reservationId: String, state: ReservationState): UCMCResult
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/ReviewRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.ReviewDTO
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.model.UserReview
6 |
7 | interface ReviewRepository {
8 | suspend fun addReview(opponentId: String, reservationId: String, review: UserReview): UCMCResult
9 | suspend fun getReviewDTO(uid: String, reservationId: String): UCMCResult
10 | }
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/TransactionRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import androidx.paging.PagingData
4 | import com.gta.domain.model.SimpleReservation
5 | import kotlinx.coroutines.flow.Flow
6 |
7 | interface TransactionRepository {
8 | fun getTransactions(userId: String, isLender: Boolean): Flow>
9 | }
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/repository/UserRepository.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.repository
2 |
3 | import com.gta.domain.model.SimpleReservation
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.model.UserProfile
6 | import kotlinx.coroutines.flow.Flow
7 |
8 | interface UserRepository {
9 | fun getUserProfile(uid: String): Flow>
10 | fun getNowReservation(uid: String, carId: String): Flow>
11 | }
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/SendNotificationUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase
2 |
3 | import com.gta.domain.model.Notification
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.NotificationRepository
6 | import javax.inject.Inject
7 |
8 | class SendNotificationUseCase @Inject constructor(private val notificationRepository: NotificationRepository) {
9 | suspend operator fun invoke(notification: Notification, receiverId: String): UCMCResult {
10 | val saveResult = notificationRepository.saveNotification(notification, receiverId)
11 | return if (saveResult is UCMCResult.Success) {
12 | notificationRepository.sendNotification(notification, receiverId)
13 | } else {
14 | saveResult
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/car/GetSimpleCarUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.car
2 |
3 | import com.gta.domain.model.SimpleCar
4 | import com.gta.domain.repository.CarRepository
5 | import kotlinx.coroutines.flow.Flow
6 | import javax.inject.Inject
7 |
8 | class GetSimpleCarUseCase @Inject constructor(
9 | private val carRepository: CarRepository
10 | ) {
11 | operator fun invoke(carId: String): Flow =
12 | carRepository.getSimpleCar(carId)
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/GetCarDetailDataUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail
2 |
3 | import com.gta.domain.model.CarDetail
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.CarRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetCarDetailDataUseCase @Inject constructor(
10 | private val carRepository: CarRepository
11 | ) {
12 | operator fun invoke(carId: String): Flow> {
13 | return carRepository.getCarData(carId)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/GetNowRentCarUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail
2 |
3 | import com.gta.domain.model.SimpleReservation
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.UserRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetNowRentCarUseCase @Inject constructor(
10 | private val userRepository: UserRepository
11 | ) {
12 | operator fun invoke(uid: String, carId: String): Flow> {
13 | return userRepository.getNowReservation(uid, carId)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/GetOwnerCarsUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail
2 |
3 | import com.gta.domain.model.SimpleCar
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.CarRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetOwnerCarsUseCase @Inject constructor(
10 | private val carRepository: CarRepository
11 | ) {
12 | operator fun invoke(ownerId: String): Flow>> {
13 | return carRepository.getSimpleCarList(ownerId)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/GetOwnerInfoUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.model.UserProfile
5 | import com.gta.domain.repository.UserRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetOwnerInfoUseCase @Inject constructor(
10 | private val userRepository: UserRepository
11 | ) {
12 | operator fun invoke(ownerId: String): Flow> {
13 | return userRepository.getUserProfile(ownerId)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/GetUseStateAboutCarUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail
2 |
3 | import com.gta.domain.model.FirestoreException
4 | import com.gta.domain.model.RentState
5 | import com.gta.domain.model.UCMCResult
6 | import com.gta.domain.repository.CarRepository
7 | import kotlinx.coroutines.flow.Flow
8 | import kotlinx.coroutines.flow.combine
9 | import kotlinx.coroutines.flow.first
10 | import javax.inject.Inject
11 |
12 | enum class UseState {
13 | OWNER, NOW_RENT_USER, USER, UNAVAILABLE
14 | }
15 |
16 | class GetUseStateAboutCarUseCase @Inject constructor(
17 | private val carRepository: CarRepository,
18 | private val getNowRentCarUseCase: GetNowRentCarUseCase
19 | ) {
20 | operator fun invoke(uid: String, carId: String): Flow> {
21 | return getNowRentCarUseCase(uid, carId).combine(carRepository.getCarRentState(carId)) { reservation, carRentState ->
22 | val ownerId = carRepository.getOwnerId(carId).first()
23 | if (reservation is UCMCResult.Success && carRentState is UCMCResult.Success && ownerId is UCMCResult.Success) {
24 | when {
25 | // 내 아이디와 차주의 아이디가 같을 때 (first)
26 | uid == ownerId.data -> {
27 | UCMCResult.Success(UseState.OWNER)
28 | }
29 | // 차 아이디와 현재 대여중인 예약의 차 아이디가 같을 때 (실시간)
30 | carId == reservation.data.carId -> {
31 | UCMCResult.Success(UseState.NOW_RENT_USER)
32 | }
33 | // 현재 차 상태가 대여 불가능 일때 (실시간)
34 | RentState.UNAVAILABLE == carRentState.data -> {
35 | UCMCResult.Success(UseState.UNAVAILABLE)
36 | }
37 | else -> {
38 | UCMCResult.Success(UseState.USER)
39 | }
40 | }
41 | } else {
42 | UCMCResult.Error(FirestoreException())
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/RemoveCarUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.repository.CarRepository
5 | import javax.inject.Inject
6 |
7 | class RemoveCarUseCase @Inject constructor(
8 | private val carRepository: CarRepository
9 | ) {
10 | suspend operator fun invoke(userId: String, carId: String): UCMCResult {
11 | return carRepository.removeCar(userId, carId)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/edit/DeleteImagesUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail.edit
2 |
3 | import com.gta.domain.repository.CarRepository
4 | import kotlinx.coroutines.flow.Flow
5 | import javax.inject.Inject
6 |
7 | class DeleteImagesUseCase @Inject constructor(
8 | private val carRepository: CarRepository
9 | ) {
10 | operator fun invoke(images: List): Flow {
11 | return carRepository.deleteImagesStorage(images)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/edit/GetCoordinateLocationUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail.edit
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.repository.MapRepository
5 | import kotlinx.coroutines.flow.Flow
6 | import javax.inject.Inject
7 |
8 | class GetCoordinateLocationUseCase @Inject constructor(
9 | private val mapRepository: MapRepository
10 | ) {
11 | operator fun invoke(longitude: Double, latitude: Double): Flow> {
12 | return mapRepository.getSearchCoordinate(longitude.toString(), latitude.toString())
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/edit/SetCarImagesUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail.edit
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.repository.CarRepository
5 | import kotlinx.coroutines.flow.Flow
6 | import javax.inject.Inject
7 |
8 | class SetCarImagesUseCase @Inject constructor(
9 | private val carRepository: CarRepository
10 | ) {
11 | operator fun invoke(carId: String, images: List): Flow>> {
12 | return carRepository.setCarImagesStorage(carId, images)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/edit/UpdateCarDetailDataUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail.edit
2 |
3 | import com.gta.domain.model.AvailableDate
4 | import com.gta.domain.model.Coordinate
5 | import com.gta.domain.model.RentState
6 | import com.gta.domain.model.UpdateCar
7 | import com.gta.domain.repository.CarRepository
8 | import kotlinx.coroutines.flow.Flow
9 | import javax.inject.Inject
10 |
11 | class UpdateCarDetailDataUseCase @Inject constructor(
12 | private val carRepository: CarRepository
13 | ) {
14 | operator fun invoke(
15 | carId: String,
16 | image: List?,
17 | price: Int,
18 | comment: String,
19 | rentState: RentState,
20 | availableDate: AvailableDate,
21 | location: String,
22 | coordinate: Coordinate
23 | ): Flow {
24 | return carRepository.updateCarDetail(
25 | carId,
26 | UpdateCar(
27 | image ?: emptyList(),
28 | price,
29 | comment,
30 | rentState,
31 | availableDate,
32 | location,
33 | coordinate
34 | )
35 | )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/cardetail/edit/UploadCarImagesUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.cardetail.edit
2 |
3 | import com.gta.domain.model.DeleteFailException
4 | import com.gta.domain.model.UCMCResult
5 | import kotlinx.coroutines.flow.Flow
6 | import kotlinx.coroutines.flow.combine
7 | import javax.inject.Inject
8 |
9 | class UploadCarImagesUseCase @Inject constructor(
10 | private val deleteImagesUseCase: DeleteImagesUseCase,
11 | private val setCarImagesUseCase: SetCarImagesUseCase
12 | ) {
13 | operator fun invoke(
14 | carId: String,
15 | oldData: List,
16 | newData: List
17 | ): Flow>> {
18 | val deleteData = oldData.filter { !newData.contains(it) }
19 | val updateData = newData.filter { !oldData.contains(it) }
20 |
21 | /*
22 | 1. 삭제된 이미지는 저장소에서 삭제
23 | 2. 새로 추가된 이미지만 저장소에 새로 추가
24 | 3. 기존에 있던 이미지 + 새로 추가된 이미지 주소값 return
25 | */
26 |
27 | return deleteImagesUseCase(deleteData).combine(setCarImagesUseCase(carId, updateData)) { deleteResult, list ->
28 | if (!deleteResult) {
29 | oldData.filter { o -> newData.contains(o) }.map { UCMCResult.Success(it) } + list + UCMCResult.Error(DeleteFailException())
30 | } else {
31 | oldData.filter { o -> newData.contains(o) }.map { UCMCResult.Success(it) } + list
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/license/GetLicenseFromDatabaseUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.license
2 |
3 | import com.gta.domain.model.DrivingLicense
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.LicenseRepository
6 | import javax.inject.Inject
7 |
8 | class GetLicenseFromDatabaseUseCase @Inject constructor(
9 | private val repository: LicenseRepository
10 | ) {
11 | suspend operator fun invoke(uid: String): UCMCResult =
12 | repository.getLicenseFromDatabase(uid)
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/license/GetLicenseFromImageUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.license
2 |
3 | import com.gta.domain.model.DrivingLicense
4 | import com.gta.domain.repository.LicenseRepository
5 | import java.nio.ByteBuffer
6 | import javax.inject.Inject
7 |
8 | class GetLicenseFromImageUseCase @Inject constructor(
9 | private val repository: LicenseRepository
10 | ) {
11 | operator fun invoke(buffer: ByteBuffer): DrivingLicense = repository.getLicenseFromImage(buffer)
12 | }
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/license/SetLicenseUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.license
2 |
3 | import com.gta.domain.model.DrivingLicense
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.LicenseRepository
6 | import javax.inject.Inject
7 |
8 | class SetLicenseUseCase @Inject constructor(
9 | private val repository: LicenseRepository
10 | ) {
11 | suspend operator fun invoke(uid: String, license: DrivingLicense, uri: String): UCMCResult =
12 | repository.setLicense(uid, license, uri)
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/login/CheckCurrentUserUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.login
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.model.UserProfile
5 | import com.gta.domain.repository.LoginRepository
6 | import javax.inject.Inject
7 |
8 | class CheckCurrentUserUseCase @Inject constructor(
9 | private val repository: LoginRepository
10 | ) {
11 | suspend operator fun invoke(uid: String): UCMCResult =
12 | repository.checkCurrentUser(uid)
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/login/SignUpUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.login
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.model.UserProfile
5 | import com.gta.domain.repository.LoginRepository
6 | import javax.inject.Inject
7 |
8 | class SignUpUseCase @Inject constructor(
9 | private val repository: LoginRepository
10 | ) {
11 | suspend operator fun invoke(uid: String): UCMCResult = repository.signUp(uid)
12 | }
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/login/UpdateUserMessageTokenUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.login
2 |
3 | import com.gta.domain.repository.LoginRepository
4 | import javax.inject.Inject
5 |
6 | class UpdateUserMessageTokenUseCase @Inject constructor(private val loginRepository: LoginRepository) {
7 | suspend operator fun invoke(uid: String): Boolean = loginRepository.updateUserMessageToken(uid)
8 | }
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/map/GetAllCarsUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.map
2 |
3 | import com.gta.domain.model.SimpleCar
4 | import com.gta.domain.repository.CarRepository
5 | import kotlinx.coroutines.flow.Flow
6 | import javax.inject.Inject
7 |
8 | class GetAllCarsUseCase @Inject constructor(private val carRepository: CarRepository) {
9 | operator fun invoke(): Flow> {
10 | return carRepository.getAllCars()
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/map/GetNearCarsUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.map
2 |
3 | import com.gta.domain.model.Coordinate
4 | import com.gta.domain.model.SimpleCar
5 | import com.gta.domain.model.UCMCResult
6 | import com.gta.domain.repository.CarRepository
7 | import kotlinx.coroutines.flow.Flow
8 | import javax.inject.Inject
9 |
10 | class GetNearCarsUseCase @Inject constructor(private val carRepository: CarRepository) {
11 | operator fun invoke(min: Coordinate, max: Coordinate): Flow>> {
12 | return carRepository.getNearCars(min, max)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/map/GetSearchAddressUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.map
2 |
3 | import com.gta.domain.model.LocationInfo
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.MapRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetSearchAddressUseCase @Inject constructor(private val mapRepository: MapRepository) {
10 | operator fun invoke(query: String): Flow>> {
11 | return mapRepository.getSearchAddressList(query)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/mypage/SetThumbnailUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.mypage
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.repository.MyPageRepository
5 | import javax.inject.Inject
6 |
7 | class SetThumbnailUseCase @Inject constructor(
8 | private val repository: MyPageRepository
9 | ) {
10 | suspend operator fun invoke(uid: String, uri: String): UCMCResult =
11 | repository.setThumbnail(uid, uri)
12 | }
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/nickname/CheckNicknameStateUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.nickname
2 |
3 | import com.gta.domain.model.NicknameState
4 | import com.gta.domain.repository.NicknameRepository
5 | import javax.inject.Inject
6 |
7 | class CheckNicknameStateUseCase @Inject constructor(
8 | private val repository: NicknameRepository
9 | ) {
10 | operator fun invoke(nickname: String): NicknameState =
11 | repository.checkNicknameState(nickname)
12 | }
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/nickname/UpdateNicknameUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.nickname
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.repository.NicknameRepository
5 | import javax.inject.Inject
6 |
7 | class UpdateNicknameUseCase @Inject constructor(
8 | private val repository: NicknameRepository
9 | ) {
10 | suspend operator fun invoke(uid: String, nickname: String): UCMCResult =
11 | repository.updateNickname(uid, nickname)
12 | }
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/notification/GetNotificationsInfoUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.notification
2 |
3 | import androidx.paging.PagingData
4 | import com.gta.domain.model.NotificationInfo
5 | import com.gta.domain.repository.NotificationRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetNotificationsInfoUseCase @Inject constructor(
10 | private val notificationRepository: NotificationRepository
11 | ) {
12 | operator fun invoke(userId: String): Flow> {
13 | return notificationRepository.getNotificationInfoList(userId)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/notification/SetMessageTokenUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.notification
2 |
3 | import com.gta.domain.repository.MessageTokenRepository
4 | import javax.inject.Inject
5 |
6 | class SetMessageTokenUseCase @Inject constructor(private val messageTokenRepository: MessageTokenRepository) {
7 | suspend operator fun invoke(string: String): Boolean = messageTokenRepository.setMessageToken(string)
8 | }
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/pinkslip/GetPinkSlipUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.pinkslip
2 |
3 | import com.gta.domain.model.PinkSlip
4 | import com.gta.domain.repository.PinkSlipRepository
5 | import java.nio.ByteBuffer
6 | import javax.inject.Inject
7 |
8 | class GetPinkSlipUseCase @Inject constructor(
9 | private val repository: PinkSlipRepository
10 | ) {
11 | operator fun invoke(buffer: ByteBuffer): PinkSlip = repository.getPinkSlip(buffer)
12 | }
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/pinkslip/SetPinkSlipUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.pinkslip
2 |
3 | import com.gta.domain.model.PinkSlip
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.PinkSlipRepository
6 | import javax.inject.Inject
7 |
8 | class SetPinkSlipUseCase @Inject constructor(
9 | private val repository: PinkSlipRepository
10 | ) {
11 | suspend operator fun invoke(uid: String, pinkSlip: PinkSlip): UCMCResult =
12 | repository.setPinkSlip(uid, pinkSlip)
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/reservation/CreateReservationUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.reservation
2 |
3 | import com.gta.domain.model.Notification
4 | import com.gta.domain.model.NotificationType
5 | import com.gta.domain.model.Reservation
6 | import com.gta.domain.model.UCMCResult
7 | import com.gta.domain.repository.ReservationRepository
8 | import com.gta.domain.usecase.SendNotificationUseCase
9 | import javax.inject.Inject
10 |
11 | class CreateReservationUseCase @Inject constructor(
12 | private val repository: ReservationRepository,
13 | private val notificationUseCase: SendNotificationUseCase
14 | ) {
15 | suspend operator fun invoke(reservation: Reservation): UCMCResult {
16 | return when (val result = repository.createReservation(reservation)) {
17 | is UCMCResult.Success -> {
18 | return notificationUseCase(
19 | Notification(
20 | type = NotificationType.REQUEST_RESERVATION.title,
21 | message = NotificationType.REQUEST_RESERVATION.msg,
22 | reservationId = result.data,
23 | fromId = reservation.lenderId,
24 | timestamp = System.currentTimeMillis()
25 | ),
26 | reservation.ownerId
27 | )
28 | }
29 | is UCMCResult.Error -> {
30 | result
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/reservation/FinishReservationUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.reservation
2 |
3 | import com.gta.domain.model.FirestoreException
4 | import com.gta.domain.model.Notification
5 | import com.gta.domain.model.NotificationType
6 | import com.gta.domain.model.Reservation
7 | import com.gta.domain.model.ReservationState
8 | import com.gta.domain.model.UCMCResult
9 | import com.gta.domain.repository.ReservationRepository
10 | import com.gta.domain.usecase.SendNotificationUseCase
11 | import javax.inject.Inject
12 |
13 | class FinishReservationUseCase @Inject constructor(
14 | private val reservationRepository: ReservationRepository,
15 | private val notificationUseCase: SendNotificationUseCase
16 | ) {
17 | suspend operator fun invoke(
18 | accepted: Boolean,
19 | reservation: Reservation,
20 | reservationId: String
21 | ): UCMCResult {
22 | val notification = Notification(
23 | type = if (accepted) NotificationType.ACCEPT_RESERVATION.title else NotificationType.DECLINE_RESERVATION.title,
24 | message = if (accepted) NotificationType.ACCEPT_RESERVATION.msg else NotificationType.DECLINE_RESERVATION.msg,
25 | reservationId = reservationId,
26 | fromId = reservation.ownerId,
27 | timestamp = System.currentTimeMillis()
28 | )
29 |
30 | return if (reservationRepository.updateReservationState(
31 | reservationId,
32 | if (accepted) ReservationState.ACCEPT else ReservationState.CANCEL
33 | ) is UCMCResult.Success
34 | ) {
35 | notificationUseCase(notification, reservation.lenderId)
36 | } else {
37 | UCMCResult.Error(FirestoreException())
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/reservation/GetCarRentInfoUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.reservation
2 |
3 | import com.gta.domain.model.CarRentInfo
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.CarRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetCarRentInfoUseCase @Inject constructor(private val repository: CarRepository) {
10 | operator fun invoke(carId: String): Flow> {
11 | return repository.getCarRentInfo(carId)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/reservation/GetReservationUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.reservation
2 |
3 | import com.gta.domain.model.Reservation
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.ReservationRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetReservationUseCase @Inject constructor(
10 | private val repository: ReservationRepository
11 | ) {
12 | operator fun invoke(reservationId: String): Flow> {
13 | return repository.getReservationInfo(reservationId)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/returncar/ReturnCarUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.returncar
2 |
3 | import com.gta.domain.model.FirestoreException
4 | import com.gta.domain.model.Notification
5 | import com.gta.domain.model.NotificationType
6 | import com.gta.domain.model.ReservationState
7 | import com.gta.domain.model.UCMCResult
8 | import com.gta.domain.repository.CarRepository
9 | import com.gta.domain.repository.ReservationRepository
10 | import com.gta.domain.usecase.SendNotificationUseCase
11 | import kotlinx.coroutines.flow.first
12 | import javax.inject.Inject
13 |
14 | class ReturnCarUseCase @Inject constructor(
15 | private val carRepository: CarRepository,
16 | private val reservationRepository: ReservationRepository,
17 | private val sendNotificationUseCase: SendNotificationUseCase
18 | ) {
19 | suspend operator fun invoke(reservationId: String, carId: String, userId: String): UCMCResult {
20 | val userResult = carRepository.getOwnerId(carId).first()
21 | val notification = Notification(
22 | type = NotificationType.RETURN_CAR.title,
23 | message = NotificationType.RETURN_CAR.msg,
24 | reservationId = reservationId,
25 | fromId = userId,
26 | timestamp = System.currentTimeMillis()
27 | )
28 |
29 | // userId가 있고 -> 예약 상태 업데이트 성공하고 -> Notification 보내기
30 | return if (userResult is UCMCResult.Success) {
31 | val updateResult = reservationRepository.updateReservationState(reservationId, ReservationState.DONE)
32 | return if (updateResult is UCMCResult.Success) {
33 | sendNotificationUseCase(notification, userResult.data)
34 | // TODO: 트랜잭션 처리
35 | } else {
36 | updateResult
37 | }
38 | } else {
39 | UCMCResult.Error(FirestoreException())
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/review/AddReviewUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.review
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.model.UserReview
5 | import com.gta.domain.repository.ReviewRepository
6 | import javax.inject.Inject
7 |
8 | class AddReviewUseCase @Inject constructor(
9 | private val repository: ReviewRepository
10 | ) {
11 | suspend operator fun invoke(
12 | opponentId: String,
13 | reservationId: String,
14 | review: UserReview
15 | ): UCMCResult = repository.addReview(opponentId, reservationId, review)
16 | }
17 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/review/GetReviewDTOUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.review
2 |
3 | import com.gta.domain.model.ReviewDTO
4 | import com.gta.domain.model.UCMCResult
5 | import com.gta.domain.repository.ReviewRepository
6 | import javax.inject.Inject
7 |
8 | class GetReviewDTOUseCase @Inject constructor(
9 | private val repository: ReviewRepository
10 | ) {
11 | suspend operator fun invoke(uid: String, reservationId: String): UCMCResult =
12 | repository.getReviewDTO(uid, reservationId)
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/transaction/GetTransactionsUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.transaction
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.filter
5 | import androidx.paging.map
6 | import com.gta.domain.model.Transaction
7 | import com.gta.domain.model.toTransaction
8 | import com.gta.domain.repository.CarRepository
9 | import com.gta.domain.repository.TransactionRepository
10 | import kotlinx.coroutines.flow.Flow
11 | import kotlinx.coroutines.flow.first
12 | import kotlinx.coroutines.flow.map
13 | import javax.inject.Inject
14 |
15 | class GetTransactionsUseCase @Inject constructor(
16 | private val carRepository: CarRepository,
17 | private val transactionRepository: TransactionRepository
18 | ) {
19 | private val tradingCondition =
20 | { transaction: Transaction -> transaction.reservationState.state >= 0 }
21 | private val completedCondition =
22 | { transaction: Transaction -> transaction.reservationState.state < 0 }
23 |
24 | operator fun invoke(
25 | uid: String,
26 | isLender: Boolean,
27 | isTrading: Boolean
28 | ): Flow> {
29 | val transactionPagingData = transactionRepository.getTransactions(uid, isLender)
30 |
31 | val filterCondition = if (isTrading) tradingCondition else completedCondition
32 |
33 | return transactionPagingData.map { pagingData ->
34 | pagingData.map { reservation ->
35 | val simpleCar = carRepository.getSimpleCar(reservation.carId).first()
36 | reservation.toTransaction(simpleCar)
37 | }.filter(filterCondition)
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/user/GetUserProfileUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.user
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.model.UserProfile
5 | import com.gta.domain.repository.UserRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetUserProfileUseCase @Inject constructor(
10 | private val repository: UserRepository
11 | ) {
12 | operator fun invoke(uid: String): Flow> = repository.getUserProfile(uid)
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/gta/domain/usecase/user/ReportUserUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.gta.domain.usecase.user
2 |
3 | import com.gta.domain.model.UCMCResult
4 | import com.gta.domain.repository.ReportRepository
5 | import javax.inject.Inject
6 |
7 | class ReportUserUseCase @Inject constructor(
8 | private val repository: ReportRepository
9 | ) {
10 | suspend operator fun invoke(uid: String): UCMCResult =
11 | repository.reportUser(uid)
12 | }
13 |
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "functions": [
3 | {
4 | "source": "functions",
5 | "codebase": "default",
6 | "ignore": [
7 | "node_modules",
8 | ".git",
9 | "firebase-debug.log",
10 | "firebase-debug.*.log"
11 | ],
12 | "predeploy": [
13 | "npm --prefix \"$RESOURCE_DIR\" run lint"
14 | ]
15 | }
16 | ],
17 | "emulators": {
18 | "functions": {
19 | "port": 5001
20 | },
21 | "ui": {
22 | "enabled": true
23 | },
24 | "singleProjectMode": true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/functions/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "root": true,
3 | "env": {
4 | es6: true,
5 | node: true,
6 | },
7 | "extends": [
8 | "eslint:recommended",
9 | "google",
10 | ],
11 | "rules": {
12 | quotes: ["error", "double"],
13 | },
14 | "parserOptions": {
15 | "ecmaVersion": 2020,
16 | "sourceType": "module",
17 | "ecmaFeatures": {
18 | "jsx": true,
19 | "experimentalObjectRestSpread": true,
20 | },
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/functions/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "description": "Cloud Functions for Firebase",
4 | "scripts": {
5 | "lint": "eslint",
6 | "serve": "firebase emulators:start --only functions",
7 | "shell": "firebase functions:shell",
8 | "start": "npm run shell",
9 | "deploy": "firebase deploy --only functions",
10 | "logs": "firebase functions:log"
11 | },
12 | "engines": {
13 | "node": "16"
14 | },
15 | "main": "index.js",
16 | "dependencies": {
17 | "firebase": "^9.14.0",
18 | "firebase-admin": "^10.0.2",
19 | "firebase-functions": "^3.18.0"
20 | },
21 | "devDependencies": {
22 | "eslint": "8.22.0",
23 | "eslint-config-google": "^0.14.0",
24 | "firebase-functions-test": "^0.2.0"
25 | },
26 | "private": true
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/android01-UCMC/d326bfea3c8f199fae80f046cf6f9d2b03ca46ca/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Nov 07 11:40:32 KST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/presentation/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/presentation/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.gta.buildsrc.Configuration
2 |
3 | plugins {
4 | id("com.android.library")
5 | kotlin("android")
6 | kotlin("kapt")
7 | id("androidx.navigation.safeargs.kotlin")
8 | id("dagger.hilt.android.plugin")
9 | id("kotlin-parcelize")
10 | }
11 |
12 | android {
13 | namespace = "com.gta.presentation"
14 | compileSdk = Configuration.compileSdk
15 |
16 | defaultConfig {
17 | minSdk = Configuration.minSdk
18 | targetSdk = Configuration.targetSdk
19 |
20 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
21 | consumerProguardFiles("consumer-rules.pro")
22 | }
23 |
24 | buildTypes {
25 | release {
26 | isMinifyEnabled = false
27 | setProguardFiles(
28 | listOf(
29 | getDefaultProguardFile("proguard-android-optimize.txt"),
30 | "proguard-rules.pro"
31 | )
32 | )
33 | }
34 | }
35 | compileOptions {
36 | sourceCompatibility = JavaVersion.VERSION_1_8
37 | targetCompatibility = JavaVersion.VERSION_1_8
38 | }
39 | kotlinOptions {
40 | jvmTarget = JavaVersion.VERSION_1_8.toString()
41 | }
42 | buildFeatures {
43 | viewBinding = true
44 | dataBinding = true
45 | }
46 | }
47 |
48 | dependencies {
49 | implementation(project(":domain"))
50 |
51 | implementation(platform(Dependencies.Libraries.Firebase.PLATFORM))
52 |
53 | implementation(Dependencies.Libraries.presentationLibraries)
54 | kapt(Dependencies.Libraries.presentationKaptLibraries)
55 |
56 | androidTestImplementation(Dependencies.Libraries.AndroidTest.ESPRESSO_CORE)
57 | }
58 |
--------------------------------------------------------------------------------
/presentation/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/android01-UCMC/d326bfea3c8f199fae80f046cf6f9d2b03ca46ca/presentation/consumer-rules.pro
--------------------------------------------------------------------------------
/presentation/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/presentation/src/androidTest/java/com/gta/presentation/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.gta.presentation.test", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/di/FirebaseAuthModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.di
2 |
3 | import com.google.firebase.auth.FirebaseAuth
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.hilt.InstallIn
7 | import dagger.hilt.components.SingletonComponent
8 | import java.util.Locale
9 | import javax.inject.Singleton
10 |
11 | @Module
12 | @InstallIn(SingletonComponent::class)
13 | object FirebaseAuthModule {
14 |
15 | @Singleton
16 | @Provides
17 | fun provideFirebaseAuth(): FirebaseAuth = FirebaseAuth.getInstance().apply {
18 | setLanguageCode(Locale.getDefault().language)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/di/FirebaseSigninModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.di
2 |
3 | import android.content.Context
4 | import com.google.android.gms.auth.api.signin.GoogleSignIn
5 | import com.google.android.gms.auth.api.signin.GoogleSignInClient
6 | import com.google.android.gms.auth.api.signin.GoogleSignInOptions
7 | import com.gta.presentation.secret.FIREBASE_CLIENT_ID
8 | import dagger.Module
9 | import dagger.Provides
10 | import dagger.hilt.InstallIn
11 | import dagger.hilt.android.components.ActivityComponent
12 | import dagger.hilt.android.qualifiers.ActivityContext
13 |
14 | @Module
15 | @InstallIn(ActivityComponent::class)
16 | class FirebaseSigninModule {
17 | @Provides
18 | fun provideGoogleSignInOptions(): GoogleSignInOptions = GoogleSignInOptions
19 | .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
20 | .requestIdToken(FIREBASE_CLIENT_ID)
21 | .requestEmail()
22 | .build()
23 |
24 | @Provides
25 | fun provideGoogleSignInClient(
26 | @ActivityContext context: Context,
27 | options: GoogleSignInOptions
28 | ): GoogleSignInClient = GoogleSignIn.getClient(context, options)
29 | }
30 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/di/UtilModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.di
2 |
3 | import android.content.Context
4 | import com.gta.presentation.util.ImageUtil
5 | import dagger.Module
6 | import dagger.Provides
7 | import dagger.hilt.InstallIn
8 | import dagger.hilt.android.qualifiers.ApplicationContext
9 | import dagger.hilt.components.SingletonComponent
10 | import javax.inject.Singleton
11 |
12 | @Module
13 | @InstallIn(SingletonComponent::class)
14 | object UtilModule {
15 |
16 | @Singleton
17 | @Provides
18 | fun provideImageUtil(@ApplicationContext context: Context): ImageUtil =
19 | ImageUtil(context.contentResolver)
20 | }
21 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/model/DateType.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.model
2 |
3 | enum class DateType {
4 | RANGE, DAY_COUNT
5 | }
6 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/model/TransactionState.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.model
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | enum class TransactionState : Parcelable {
8 | TRADING, COMPLETED
9 | }
10 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/model/TransactionUserState.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.model
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | enum class TransactionUserState : Parcelable {
8 | LENDER, OWNER
9 | }
10 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/GlideModule.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui
2 |
3 | import com.bumptech.glide.module.AppGlideModule
4 |
5 | @com.bumptech.glide.annotation.GlideModule
6 | class GlideModule : AppGlideModule()
7 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/MainViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.google.firebase.auth.FirebaseAuth
6 | import com.gta.domain.usecase.login.UpdateUserMessageTokenUseCase
7 | import com.gta.presentation.util.FirebaseUtil
8 | import dagger.hilt.android.lifecycle.HiltViewModel
9 | import kotlinx.coroutines.flow.MutableSharedFlow
10 | import kotlinx.coroutines.flow.SharedFlow
11 | import kotlinx.coroutines.launch
12 | import javax.inject.Inject
13 |
14 | @HiltViewModel
15 | class MainViewModel @Inject constructor(
16 | private val auth: FirebaseAuth,
17 | private val updateUserMessageTokenUseCase: UpdateUserMessageTokenUseCase
18 | ) : ViewModel() {
19 |
20 | private val _changeAuthStateEvent = MutableSharedFlow()
21 | val changeAuthStateEvent: SharedFlow get() = _changeAuthStateEvent
22 |
23 | private val authStateListener by lazy {
24 | FirebaseAuth.AuthStateListener {
25 | viewModelScope.launch {
26 | _changeAuthStateEvent.emit(it.currentUser == null)
27 | }
28 | }
29 | }
30 |
31 | init {
32 | auth.addAuthStateListener(authStateListener)
33 | updateMessageToken()
34 | }
35 |
36 | private fun updateMessageToken() {
37 | viewModelScope.launch {
38 | updateUserMessageTokenUseCase(FirebaseUtil.uid)
39 | }
40 | }
41 |
42 | override fun onCleared() {
43 | auth.removeAuthStateListener(authStateListener)
44 | super.onCleared()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.base
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import androidx.appcompat.app.AppCompatActivity
6 | import androidx.viewbinding.ViewBinding
7 |
8 | abstract class BaseActivity(
9 | private val bindingFactory: (LayoutInflater) -> VB
10 | ) : AppCompatActivity() {
11 |
12 | protected lateinit var binding: VB
13 |
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | binding = bindingFactory(layoutInflater)
17 | setContentView(binding.root)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/base/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.base
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.annotation.LayoutRes
8 | import androidx.databinding.DataBindingUtil
9 | import androidx.databinding.ViewDataBinding
10 | import androidx.fragment.app.Fragment
11 | import com.google.android.material.snackbar.Snackbar
12 | import com.gta.presentation.R
13 |
14 | abstract class BaseFragment(
15 | @LayoutRes private val layoutRes: Int
16 | ) : Fragment() {
17 |
18 | private var _binding: VB? = null
19 | protected val binding get() = _binding!!
20 |
21 | override fun onCreateView(
22 | inflater: LayoutInflater,
23 | container: ViewGroup?,
24 | savedInstanceState: Bundle?
25 | ): View {
26 | _binding = DataBindingUtil.inflate(inflater, layoutRes, container, false)
27 | return binding.run {
28 | lifecycleOwner = viewLifecycleOwner
29 | root
30 | }
31 | }
32 |
33 | fun isBindingNotNull(): Boolean = _binding != null
34 |
35 | fun sendSnackBar(
36 | message: String?,
37 | @androidx.annotation.IntRange(from = -2) length: Int = Snackbar.LENGTH_SHORT,
38 | anchorView: View? = null
39 | ) {
40 | Snackbar.make(
41 | binding.root,
42 | message ?: getString(R.string.exception_not_found),
43 | length
44 | ).apply {
45 | if (anchorView != null) {
46 | this.anchorView = anchorView
47 | }
48 | }.show()
49 | }
50 |
51 | override fun onDestroyView() {
52 | super.onDestroyView()
53 | _binding = null
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/base/CameraGuideFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.base
2 |
3 | import android.Manifest
4 | import android.os.Bundle
5 | import android.os.Environment
6 | import android.view.View
7 | import androidx.activity.result.contract.ActivityResultContracts
8 | import androidx.core.content.FileProvider
9 | import androidx.core.content.PermissionChecker
10 | import com.gta.presentation.R
11 | import com.gta.presentation.databinding.FragmentCameraGuideBinding
12 | import java.io.File
13 |
14 | abstract class CameraGuideFragment : BaseFragment(
15 | R.layout.fragment_camera_guide
16 | ) {
17 |
18 | abstract fun navigate(uri: String)
19 |
20 | private val permissionLauncher =
21 | registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
22 | if (isGranted) {
23 | cameraLauncher.launch(photoUri)
24 | }
25 | }
26 |
27 | private val cameraLauncher =
28 | registerForActivityResult(ActivityResultContracts.TakePicture()) { isSucceed ->
29 | if (isSucceed) {
30 | navigate(photoUri.toString())
31 | }
32 | }
33 |
34 | private val photoFile by lazy {
35 | File.createTempFile(
36 | "IMG_",
37 | ".jpg",
38 | requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
39 | )
40 | }
41 |
42 | private val photoUri by lazy {
43 | FileProvider.getUriForFile(
44 | requireContext(),
45 | "${requireContext().packageName}.provider",
46 | photoFile
47 | )
48 | }
49 |
50 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
51 | super.onViewCreated(view, savedInstanceState)
52 | binding.btnCameraGuidePicture.setOnClickListener {
53 | takePicture()
54 | }
55 | }
56 |
57 | private fun takePicture() {
58 | val permissionCheck = PermissionChecker.checkCallingOrSelfPermission(
59 | requireContext(),
60 | Manifest.permission.CAMERA
61 | )
62 | if (permissionCheck == PermissionChecker.PERMISSION_GRANTED) {
63 | cameraLauncher.launch(photoUri)
64 | } else {
65 | permissionLauncher.launch(Manifest.permission.CAMERA)
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/cardetail/CarImagePagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.cardetail
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.DiffUtil
7 | import androidx.recyclerview.widget.ListAdapter
8 | import androidx.recyclerview.widget.RecyclerView.ViewHolder
9 | import com.gta.presentation.R
10 | import com.gta.presentation.databinding.ItemCarDetailImageBinding
11 |
12 | class CarImagePagerAdapter :
13 | ListAdapter(ImagesDiffCallback()) {
14 |
15 | class ImageViewHolder(
16 | private val binding: ItemCarDetailImageBinding
17 | ) : ViewHolder(binding.root) {
18 | fun bind(img: String) {
19 | binding.img = img
20 | }
21 | }
22 |
23 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
24 | return ImageViewHolder(
25 | DataBindingUtil.inflate(
26 | LayoutInflater.from(parent.context),
27 | R.layout.item_car_detail_image,
28 | parent,
29 | false
30 | )
31 | )
32 | }
33 |
34 | override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
35 | holder.bind(getItem(position))
36 | }
37 | }
38 |
39 | private class ImagesDiffCallback : DiffUtil.ItemCallback() {
40 | override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
41 | return oldItem == newItem
42 | }
43 |
44 | override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
45 | return oldItem == newItem
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/cardetail/edit/CarEditImagesAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.cardetail.edit
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.DiffUtil
7 | import androidx.recyclerview.widget.ListAdapter
8 | import androidx.recyclerview.widget.RecyclerView.ViewHolder
9 | import com.gta.presentation.R
10 | import com.gta.presentation.databinding.ItemCarEditImageBinding
11 | import com.gta.presentation.ui.cardetail.edit.CarEditImagesAdapter.ImageViewHolder
12 |
13 | class CarEditImagesAdapter : ListAdapter(ImagesDiffCallback()) {
14 |
15 | private var itemClickListener: OnItemClickListener? = null
16 |
17 | interface OnItemClickListener {
18 | fun onClick(position: Int)
19 | }
20 |
21 | fun setItemClickListener(onItemClickListener: OnItemClickListener) {
22 | itemClickListener = onItemClickListener
23 | }
24 |
25 | class ImageViewHolder(
26 | private val binding: ItemCarEditImageBinding,
27 | itemClickListener: OnItemClickListener?
28 | ) : ViewHolder(binding.root) {
29 |
30 | init {
31 | binding.ivDelete.setOnClickListener {
32 | itemClickListener?.onClick(bindingAdapterPosition)
33 | }
34 | }
35 |
36 | fun bind(img: String) {
37 | binding.img = img
38 | }
39 | }
40 |
41 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
42 | return ImageViewHolder(
43 | DataBindingUtil.inflate(
44 | LayoutInflater.from(parent.context),
45 | R.layout.item_car_edit_image,
46 | parent,
47 | false
48 | ),
49 | itemClickListener
50 | )
51 | }
52 |
53 | override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
54 | holder.bind(getItem(position))
55 | }
56 | }
57 |
58 | private class ImagesDiffCallback : DiffUtil.ItemCallback() {
59 | override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
60 | return oldItem == newItem
61 | }
62 |
63 | override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
64 | return oldItem == newItem
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/chatting/chat/ChattingViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.chatting.chat
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import androidx.lifecycle.ViewModel
5 | import androidx.lifecycle.viewModelScope
6 | import com.gta.domain.model.SimpleCar
7 | import com.gta.domain.usecase.car.GetSimpleCarUseCase
8 | import dagger.hilt.android.lifecycle.HiltViewModel
9 | import io.getstream.chat.android.client.ChatClient
10 | import kotlinx.coroutines.flow.MutableSharedFlow
11 | import kotlinx.coroutines.flow.MutableStateFlow
12 | import kotlinx.coroutines.flow.SharedFlow
13 | import kotlinx.coroutines.flow.StateFlow
14 | import kotlinx.coroutines.flow.first
15 | import kotlinx.coroutines.launch
16 | import javax.inject.Inject
17 |
18 | @HiltViewModel
19 | class ChattingViewModel @Inject constructor(
20 | args: SavedStateHandle,
21 | private val chatClient: ChatClient,
22 | private val getSimpleCarUseCase: GetSimpleCarUseCase
23 | ) : ViewModel() {
24 |
25 | private val _car = MutableStateFlow(SimpleCar())
26 | val car: StateFlow get() = _car
27 |
28 | private val _navigateCarDetailEvent = MutableSharedFlow()
29 | val navigateCarDetailEvent: SharedFlow get() = _navigateCarDetailEvent
30 |
31 | private val cid = args.get("cid") ?: ""
32 | private val channelId = cid.substringAfterLast(":")
33 | private val carId = cid.substringAfterLast("-")
34 |
35 | init {
36 | // cid는 "ChannelType:ChannelId"이 저장되어 있어요
37 | // ChannelId를 "대여자uid-차id"로 설정해줬기 때문에 ChannelId를 이용해 채팅 화면 상단에 차 정보를 보여줄 수 있습니다.
38 | viewModelScope.launch {
39 | _car.emit(getSimpleCarUseCase(carId).first())
40 | }
41 | }
42 |
43 | fun navigateCarDetail() {
44 | if (carId.isEmpty()) return
45 | viewModelScope.launch {
46 | _navigateCarDetailEvent.emit(carId)
47 | }
48 | }
49 |
50 | fun muteChannel() {
51 | chatClient.muteChannel(
52 | channelType = "messaging",
53 | channelId = channelId
54 | ).execute()
55 | }
56 |
57 | fun unmuteChannel() {
58 | chatClient.unmuteChannel(
59 | channelType = "messaging",
60 | channelId = channelId
61 | ).execute()
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/license/guide/LicenseGuideFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.license.guide
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.navigation.fragment.findNavController
6 | import com.gta.presentation.R
7 | import com.gta.presentation.ui.base.CameraGuideFragment
8 |
9 | class LicenseGuideFragment : CameraGuideFragment() {
10 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
11 | super.onViewCreated(view, savedInstanceState)
12 | binding.tvCameraGuideTitle.setText(R.string.license_guide_title)
13 | binding.ivCameraGuideSample.setImageResource(R.drawable.img_driving_license)
14 | }
15 |
16 | override fun navigate(uri: String) {
17 | val direction = LicenseGuideFragmentDirections
18 | .actionLicenseGuideFragmentToLicenseRegistrationFragment(uri)
19 | findNavController().navigate(direction)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/mypage/MyPageTermsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.mypage
2 |
3 | import android.content.res.AssetManager
4 | import android.os.Bundle
5 | import android.text.method.ScrollingMovementMethod
6 | import android.view.View
7 | import com.gta.presentation.R
8 | import com.gta.presentation.databinding.FragmentMypageTermsBinding
9 | import com.gta.presentation.ui.base.BaseFragment
10 | import java.io.BufferedReader
11 | import java.io.IOException
12 | import java.io.InputStream
13 | import java.io.InputStreamReader
14 |
15 | class MyPageTermsFragment :
16 | BaseFragment(R.layout.fragment_mypage_terms) {
17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
18 | super.onViewCreated(view, savedInstanceState)
19 |
20 | binding.tvDescriptionServices.text = getAssetData("terms_of_service.txt")
21 | binding.tvDescriptionPrivacy.text = getAssetData("terms_of_privacy.txt")
22 |
23 | binding.tvDescriptionServices.movementMethod = ScrollingMovementMethod()
24 | binding.tvDescriptionPrivacy.movementMethod = ScrollingMovementMethod()
25 | }
26 |
27 | private fun getAssetData(fileName: String): String {
28 | val inputStream: InputStream?
29 | var result: String
30 | try {
31 | inputStream = resources.assets.open(fileName, AssetManager.ACCESS_BUFFER)
32 | val reader = BufferedReader(InputStreamReader(inputStream))
33 | result = reader.readLines().joinToString("\n")
34 | inputStream.close()
35 | } catch (e: IOException) {
36 | result = ""
37 | }
38 |
39 | return result
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/mypage/license/MyPageLicenseViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.mypage.license
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.gta.domain.model.DrivingLicense
6 | import com.gta.domain.model.UCMCResult
7 | import com.gta.domain.usecase.license.GetLicenseFromDatabaseUseCase
8 | import com.gta.presentation.util.FirebaseUtil
9 | import com.gta.presentation.util.MutableEventFlow
10 | import dagger.hilt.android.lifecycle.HiltViewModel
11 | import kotlinx.coroutines.flow.MutableStateFlow
12 | import kotlinx.coroutines.flow.StateFlow
13 | import kotlinx.coroutines.launch
14 | import javax.inject.Inject
15 |
16 | @HiltViewModel
17 | class MyPageLicenseViewModel @Inject constructor(
18 | getLicenseFromDatabaseUseCase: GetLicenseFromDatabaseUseCase
19 | ) : ViewModel() {
20 |
21 | private val _drivingLicense = MutableStateFlow(null)
22 | val drivingLicense: StateFlow get() = _drivingLicense
23 |
24 | private val _drivingLicenseEvent = MutableEventFlow>()
25 | val drivingLicenseEvent get() = _drivingLicenseEvent
26 |
27 | init {
28 | viewModelScope.launch {
29 | when (val result = getLicenseFromDatabaseUseCase(FirebaseUtil.uid)) {
30 | is UCMCResult.Error -> {
31 | _drivingLicenseEvent.emit(result)
32 | }
33 | is UCMCResult.Success -> {
34 | _drivingLicense.emit(result.data)
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/mypage/mycars/MyCarsViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.mypage.mycars
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.gta.domain.model.SimpleCar
6 | import com.gta.domain.model.UCMCResult
7 | import com.gta.domain.usecase.cardetail.GetOwnerCarsUseCase
8 | import com.gta.domain.usecase.cardetail.RemoveCarUseCase
9 | import com.gta.presentation.util.EventFlow
10 | import com.gta.presentation.util.FirebaseUtil
11 | import com.gta.presentation.util.MutableEventFlow
12 | import com.gta.presentation.util.asEventFlow
13 | import dagger.hilt.android.lifecycle.HiltViewModel
14 | import kotlinx.coroutines.flow.collectLatest
15 | import kotlinx.coroutines.launch
16 | import javax.inject.Inject
17 |
18 | @HiltViewModel
19 | class MyCarsViewModel @Inject constructor(
20 | private val getOwnerCarsUseCase: GetOwnerCarsUseCase,
21 | private val removeCarUseCase: RemoveCarUseCase
22 | ) :
23 | ViewModel() {
24 |
25 | private val _deleteEvent = MutableEventFlow>()
26 | val deleteEvent: EventFlow> get() = _deleteEvent.asEventFlow()
27 |
28 | private val _userCarEvent = MutableEventFlow>>()
29 | val userCarEvent: EventFlow>> get() = _userCarEvent.asEventFlow()
30 |
31 | private val uid = FirebaseUtil.uid
32 |
33 | init {
34 | getCarList()
35 | }
36 |
37 | fun getCarList() {
38 | viewModelScope.launch {
39 | getOwnerCarsUseCase(uid).collectLatest { result ->
40 | _userCarEvent.emit(result)
41 | }
42 | }
43 | }
44 |
45 | fun deleteCar(carId: String) {
46 | viewModelScope.launch {
47 | _deleteEvent.emit(removeCarUseCase(uid, carId))
48 | getCarList()
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/mypage/mycars/OnItemClickListener.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.mypage.mycars
2 |
3 | import android.view.View
4 |
5 | interface OnItemClickListener {
6 | fun onClick(value: T)
7 | fun onLongClick(v: View, value: T)
8 | }
9 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/notification/NotificationListViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.notification
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import androidx.paging.PagingData
6 | import androidx.paging.cachedIn
7 | import com.gta.domain.model.NotificationInfo
8 | import com.gta.domain.usecase.notification.GetNotificationsInfoUseCase
9 | import com.gta.presentation.util.FirebaseUtil
10 | import dagger.hilt.android.lifecycle.HiltViewModel
11 | import kotlinx.coroutines.flow.Flow
12 | import kotlinx.coroutines.flow.MutableStateFlow
13 | import kotlinx.coroutines.flow.first
14 | import kotlinx.coroutines.launch
15 | import javax.inject.Inject
16 |
17 | @HiltViewModel
18 | class NotificationListViewModel @Inject constructor(
19 | getNotificationInfo: GetNotificationsInfoUseCase
20 | ) : ViewModel() {
21 |
22 | private val _notifyList = MutableStateFlow>(PagingData.empty())
23 | val notificationList: Flow>
24 | get() = _notifyList
25 |
26 | init {
27 | viewModelScope.launch {
28 | _notifyList.value =
29 | getNotificationInfo(FirebaseUtil.uid).cachedIn(viewModelScope).first()
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/pinkslip/guide/PinkSlipGuideFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.pinkslip.guide
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.navigation.fragment.findNavController
6 | import com.gta.presentation.R
7 | import com.gta.presentation.ui.base.CameraGuideFragment
8 |
9 | class PinkSlipGuideFragment : CameraGuideFragment() {
10 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
11 | super.onViewCreated(view, savedInstanceState)
12 | binding.tvCameraGuideTitle.setText(R.string.pink_slip_guide_title)
13 | binding.ivCameraGuideSample.setImageResource(R.drawable.img_pink_slip)
14 | }
15 |
16 | override fun navigate(uri: String) {
17 | val direction = PinkSlipGuideFragmentDirections
18 | .actionPinkSlipGuideFragmentToPinkSlipRegistrationFragment(uri)
19 | findNavController().navigate(direction)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/pinkslip/registration/PinkSlipRegistrationViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.pinkslip.registration
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import androidx.lifecycle.ViewModel
5 | import androidx.lifecycle.viewModelScope
6 | import com.google.firebase.auth.FirebaseAuth
7 | import com.gta.domain.model.PinkSlip
8 | import com.gta.domain.model.UCMCResult
9 | import com.gta.domain.usecase.pinkslip.GetPinkSlipUseCase
10 | import com.gta.domain.usecase.pinkslip.SetPinkSlipUseCase
11 | import com.gta.presentation.util.ImageUtil
12 | import com.gta.presentation.util.MutableEventFlow
13 | import com.gta.presentation.util.asEventFlow
14 | import dagger.hilt.android.lifecycle.HiltViewModel
15 | import kotlinx.coroutines.flow.MutableStateFlow
16 | import kotlinx.coroutines.flow.StateFlow
17 | import kotlinx.coroutines.launch
18 | import javax.inject.Inject
19 |
20 | @HiltViewModel
21 | class PinkSlipRegistrationViewModel @Inject constructor(
22 | private val imageUtil: ImageUtil,
23 | private val auth: FirebaseAuth,
24 | private val getPinkSlipUseCase: GetPinkSlipUseCase,
25 | private val setPinkSlipUseCase: SetPinkSlipUseCase,
26 | args: SavedStateHandle
27 | ) : ViewModel() {
28 |
29 | private val _pinkSlip = MutableStateFlow(null)
30 | val pinkSlip: StateFlow get() = _pinkSlip
31 |
32 | private val _registerEvent = MutableEventFlow>()
33 | val registerEvent get() = _registerEvent
34 |
35 | private val _pinkSlipPictureEvent = MutableEventFlow()
36 | val pinkSlipPictureEvent get() = _pinkSlipPictureEvent.asEventFlow()
37 |
38 | val pinkSlipPicture = args.get("uri") ?: ""
39 |
40 | init {
41 | viewModelScope.launch {
42 | _pinkSlipPictureEvent.emit(pinkSlipPicture)
43 | }
44 | getPinkSlip(pinkSlipPicture)
45 | }
46 |
47 | private fun getPinkSlip(uri: String) {
48 | val buffer = imageUtil.getByteBuffer(uri) ?: return
49 | viewModelScope.launch {
50 | _pinkSlip.emit(getPinkSlipUseCase(buffer))
51 | }
52 | }
53 |
54 | fun registerPinkSlip() {
55 | val pinkSlip = pinkSlip.value ?: return
56 | val uid = auth.currentUser?.uid ?: return
57 | viewModelScope.launch {
58 | _registerEvent.emit(setPinkSlipUseCase(uid, pinkSlip))
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.transaction
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.navigation.Navigation
6 | import androidx.navigation.fragment.navArgs
7 | import com.google.android.material.tabs.TabLayoutMediator
8 | import com.gta.presentation.R
9 | import com.gta.presentation.databinding.FragmentTransactionBinding
10 | import com.gta.presentation.model.TransactionUserState
11 | import com.gta.presentation.ui.MainActivity
12 | import com.gta.presentation.ui.base.BaseFragment
13 |
14 | class TransactionFragment : BaseFragment(R.layout.fragment_transaction) {
15 |
16 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
17 | super.onViewCreated(view, savedInstanceState)
18 |
19 | val args: TransactionFragmentArgs by navArgs()
20 | (requireActivity() as MainActivity).supportActionBar?.title = args.title
21 |
22 | val userState = if (args.title == getString(R.string.mypage_btn_transaction_buy)) {
23 | TransactionUserState.LENDER
24 | } else {
25 | TransactionUserState.OWNER
26 | }
27 |
28 | binding.vpTransactionListFragment.adapter = TransactionPagerAdapter(userState, this)
29 | setUpTabLayoutWithViewPager()
30 | }
31 |
32 | private fun setUpTabLayoutWithViewPager() {
33 | TabLayoutMediator(binding.tlTransactionListTabs, binding.vpTransactionListFragment) { tab, position ->
34 | val tabTitle = when (position) {
35 | 0 -> getString(R.string.trading)
36 | else -> getString(R.string.transaction_completed)
37 | }
38 | tab.text = tabTitle
39 | }.attach()
40 | }
41 |
42 | companion object {
43 | fun navigateToReservationCheck(view: View, reservationId: String) {
44 | Navigation.findNavController(view).navigate(TransactionFragmentDirections.actionTransactionFragmentToReservationCheckFragment(reservationId))
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.transaction
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.paging.PagingDataAdapter
7 | import androidx.recyclerview.widget.DiffUtil
8 | import androidx.recyclerview.widget.RecyclerView
9 | import com.gta.domain.model.Transaction
10 | import com.gta.presentation.R
11 | import com.gta.presentation.databinding.ItemTransactionBinding
12 |
13 | class TransactionListAdapter : PagingDataAdapter(TransactionDiffCallback()) {
14 |
15 | class TransactionViewHolder(
16 | private val binding: ItemTransactionBinding
17 | ) : RecyclerView.ViewHolder(binding.root) {
18 |
19 | fun bind(item: Transaction) {
20 | with(binding) {
21 | this.item = item
22 | root.setOnClickListener {
23 | TransactionFragment.navigateToReservationCheck(it, item.reservationId)
24 | }
25 | executePendingBindings()
26 | }
27 | }
28 | }
29 |
30 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder {
31 | return TransactionViewHolder(
32 | DataBindingUtil.inflate(
33 | LayoutInflater.from(parent.context),
34 | R.layout.item_transaction,
35 | parent,
36 | false
37 | )
38 | )
39 | }
40 |
41 | override fun onBindViewHolder(holder: TransactionViewHolder, position: Int) {
42 | getItem(position)?.let { holder.bind(it) }
43 | }
44 | }
45 |
46 | private class TransactionDiffCallback : DiffUtil.ItemCallback() {
47 | override fun areItemsTheSame(oldItem: Transaction, newItem: Transaction): Boolean {
48 | return oldItem.reservationId == newItem.reservationId
49 | }
50 |
51 | override fun areContentsTheSame(oldItem: Transaction, newItem: Transaction): Boolean {
52 | return oldItem == newItem
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionListViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.transaction
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import androidx.lifecycle.ViewModel
5 | import androidx.lifecycle.viewModelScope
6 | import androidx.paging.PagingData
7 | import com.gta.domain.model.Transaction
8 | import com.gta.domain.usecase.transaction.GetTransactionsUseCase
9 | import com.gta.presentation.model.TransactionState
10 | import com.gta.presentation.model.TransactionUserState
11 | import com.gta.presentation.util.FirebaseUtil
12 | import dagger.hilt.android.lifecycle.HiltViewModel
13 | import kotlinx.coroutines.flow.SharingStarted
14 | import kotlinx.coroutines.flow.StateFlow
15 | import kotlinx.coroutines.flow.stateIn
16 | import javax.inject.Inject
17 |
18 | @HiltViewModel
19 | class TransactionListViewModel @Inject constructor(
20 | args: SavedStateHandle,
21 | private val getTransactionsUseCase: GetTransactionsUseCase
22 | ) : ViewModel() {
23 |
24 | private val userState: TransactionUserState =
25 | args.get(TransactionPagerAdapter.USER_STATE_ARG)
26 | ?: TransactionUserState.LENDER
27 | private val transactionState: TransactionState =
28 | args.get(TransactionPagerAdapter.TRANSACTION_STATE_ARG)
29 | ?: TransactionState.TRADING
30 |
31 | val transaction: StateFlow>
32 | get() = getTransactionsUseCase(
33 | FirebaseUtil.uid,
34 | userState == TransactionUserState.LENDER,
35 | transactionState == TransactionState.TRADING
36 | ).stateIn(
37 | scope = viewModelScope,
38 | started = SharingStarted.WhileSubscribed(5000),
39 | initialValue = PagingData.empty()
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/ui/transaction/TransactionPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.ui.transaction
2 |
3 | import android.os.Bundle
4 | import androidx.fragment.app.Fragment
5 | import androidx.viewpager2.adapter.FragmentStateAdapter
6 | import com.gta.presentation.model.TransactionState
7 | import com.gta.presentation.model.TransactionUserState
8 |
9 | class TransactionPagerAdapter(private val userState: TransactionUserState, fragment: Fragment) : FragmentStateAdapter(fragment) {
10 | private val transactionStates = TransactionState.values()
11 |
12 | override fun getItemCount(): Int = transactionStates.size
13 |
14 | override fun createFragment(position: Int): Fragment {
15 | val fragment = TransactionListFragment()
16 | fragment.arguments = Bundle().apply {
17 | putParcelable(USER_STATE_ARG, userState)
18 | putParcelable(TRANSACTION_STATE_ARG, transactionStates[position])
19 | }
20 | return fragment
21 | }
22 |
23 | companion object {
24 | const val TRANSACTION_STATE_ARG = "TRANSACTION_STATE"
25 | const val USER_STATE_ARG = "USER_STATE"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/util/DateUtil.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.util
2 |
3 | import java.text.SimpleDateFormat
4 | import java.util.*
5 | import kotlin.math.abs
6 |
7 | object DateUtil {
8 | const val DAY_TIME_UNIT = 60 * 60 * 24 * 1000L
9 | const val MINUTE_UNIT = 60 * 1000L
10 |
11 | val dateFormat = SimpleDateFormat("yy/MM/dd", Locale.getDefault())
12 |
13 | fun getDateCount(startDate: Long, endDate: Long) =
14 | abs(endDate - startDate).div(DAY_TIME_UNIT).plus(1).toInt()
15 | }
16 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/util/DateValidator.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.util
2 |
3 | import com.google.android.material.datepicker.CalendarConstraints
4 | import com.google.android.material.datepicker.MaterialDatePicker
5 | import kotlinx.parcelize.Parcelize
6 |
7 | @Parcelize
8 | class DateValidator(
9 | private val reservationRange: Pair,
10 | private val inValidList: List>?
11 | ) : CalendarConstraints.DateValidator {
12 |
13 | override fun isValid(date: Long): Boolean {
14 | return date in reservationRange.first..reservationRange.second && date > MaterialDatePicker.todayInUtcMilliseconds() && checkInvalidList(
15 | date
16 | )
17 | }
18 |
19 | private fun checkInvalidList(date: Long): Boolean {
20 | inValidList ?: return true
21 |
22 | for (range in inValidList) {
23 | if (date in range.first..range.second) {
24 | return false
25 | }
26 | }
27 | return true
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/util/EventFlow.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.util
2 |
3 | import com.gta.presentation.util.EventFlow.Companion.DEFAULT_REPLAY
4 | import kotlinx.coroutines.flow.Flow
5 | import kotlinx.coroutines.flow.FlowCollector
6 | import kotlinx.coroutines.flow.MutableSharedFlow
7 | import java.util.concurrent.atomic.AtomicBoolean
8 |
9 | /*
10 | collect가 닫혀있을때 이벤트가 들어와서 휘발되는 것을 막아야합니다.
11 | 아직 소비되지 않은 이벤트를 캐싱하는 이벤트 플로우 입니다.
12 | */
13 | interface EventFlow : Flow {
14 | companion object {
15 | const val DEFAULT_REPLAY = 1
16 | }
17 | }
18 |
19 | // MutableEventFlow는 Emit을 할 수 있는 FlowCollector를 상속받습니다.
20 | interface MutableEventFlow : EventFlow, FlowCollector
21 |
22 | // collect에 들어가서 소비가 잘 됐는지 확인하기 위한 consumed 변수가 정의됐습니다.
23 | private class EventFlowSlot(val value: T) {
24 | private val consumed = AtomicBoolean(false)
25 |
26 | // markConsumed가 호출되면 consumed를 리턴한 다음 consumed를 true로 바꿔줍니다.
27 | fun markConsumed() = consumed.getAndSet(true)
28 | }
29 |
30 | // emit 할 수가 없는 ReadOnlyFlow를 정의합니다.
31 | private class ReadOnlyEventFlow(flow: EventFlow) : EventFlow by flow
32 |
33 | private class EventFlowImpl(replay: Int) : MutableEventFlow {
34 |
35 | // replay 만큼 이전에 emit한 값을 가지고 있습니다.
36 | private val flow: MutableSharedFlow> = MutableSharedFlow(replay)
37 |
38 | // 새로운 collect가 연결되면 flow는 이전 값을 방출 합니다.
39 | // 이전 값이 collect가 열려 있는 상태에서 들어왔었다면 consumed는 markConsumed()에 의해 true가 됐기 때문에 이번 방출에서 emit 되지 않습니다.
40 | // 이전 값이 collect가 닫혀 있는 상태에서 들어왔었다면 consumed는 초기값 false 그대로기 때문에 이번 방출에서 emit 됩니다.
41 | override suspend fun collect(collector: FlowCollector) =
42 | flow.collect { slot ->
43 | // 한 번 markConsumed가 호출된 slot은 다음 번 방출에서는 값을 emit하지 못합니다.
44 | if (!slot.markConsumed()) {
45 | collector.emit(slot.value)
46 | }
47 | }
48 |
49 | // EventFlowSlot의 consumed를 이용해야 하기 때문에 value는 EventFlowSlot에 감싸서 넣어줍니다.
50 | override suspend fun emit(value: T) {
51 | flow.emit(EventFlowSlot(value))
52 | }
53 | }
54 |
55 | // 사용할 MutableEventFlow와 EventFlow를 메소드 형태로 정의 해줍니다.
56 | @Suppress("FunctionName")
57 | fun MutableEventFlow(replay: Int = DEFAULT_REPLAY): MutableEventFlow = EventFlowImpl(replay)
58 |
59 | fun MutableEventFlow.asEventFlow(): EventFlow = ReadOnlyEventFlow(this)
60 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/util/FirebaseUtil.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.util
2 |
3 | import com.google.firebase.auth.FirebaseUser
4 |
5 | object FirebaseUtil {
6 | var uid = ""
7 | private set
8 |
9 | fun setUid(firebaseUser: FirebaseUser) {
10 | uid = firebaseUser.uid
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/util/ImageUtil.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.util
2 |
3 | import android.content.ContentResolver
4 | import androidx.core.net.toUri
5 | import java.nio.ByteBuffer
6 |
7 | class ImageUtil(
8 | private val contentResolver: ContentResolver
9 | ) {
10 | fun getByteBuffer(uri: String): ByteBuffer? {
11 | var byteBuffer: ByteBuffer? = null
12 | runCatching {
13 | contentResolver.openInputStream(uri.toUri())
14 | }.onSuccess { inputStream ->
15 | inputStream?.use {
16 | byteBuffer = ByteBuffer.wrap(it.readBytes())
17 | }
18 | }
19 | return byteBuffer
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/gta/presentation/util/RepeatOnStarted.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation.util
2 |
3 | import androidx.lifecycle.Lifecycle
4 | import androidx.lifecycle.LifecycleOwner
5 | import androidx.lifecycle.lifecycleScope
6 | import androidx.lifecycle.repeatOnLifecycle
7 | import kotlinx.coroutines.CoroutineScope
8 | import kotlinx.coroutines.launch
9 |
10 | fun LifecycleOwner.repeatOnStarted(lifecycleOwner: LifecycleOwner, block: suspend CoroutineScope.() -> Unit) {
11 | lifecycleScope.launch {
12 | lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED, block)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/bg_bottom_sheet.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/bg_reservation_tag.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/bg_textview_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/bg_transaction_state_tag.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/edittext_style.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 | -
14 |
15 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_arrow_right_on_background.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_bottom_nav_car.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_bottom_nav_chatting.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_bottom_nav_my.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_broken_image.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_camera.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_check.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
9 |
12 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_close.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_logo.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_mypage_car.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_mypage_deal.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_mypage_deal_inverse.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_mypage_edit.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_mypage_thumb.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_notification.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_right_arrow.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_search.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/ic_splash.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/img_driving_license.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/android01-UCMC/d326bfea3c8f199fae80f046cf6f9d2b03ca46ca/presentation/src/main/res/drawable/img_driving_license.png
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/img_pink_slip.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/android01-UCMC/d326bfea3c8f199fae80f046cf6f9d2b03ca46ca/presentation/src/main/res/drawable/img_pink_slip.jpg
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/selector_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
26 |
27 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/fragment_chatting_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/fragment_my_cars.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
10 |
11 |
12 |
15 |
16 |
20 |
21 |
27 |
28 |
29 |
30 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/fragment_notification.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/fragment_notification_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
17 |
18 |
25 |
26 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/fragment_transaction.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
17 |
18 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/item_car_detail_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/item_car_edit_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
16 |
17 |
30 |
31 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/item_map_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
24 |
25 |
34 |
35 |
--------------------------------------------------------------------------------
/presentation/src/main/res/menu/menu_bottom_nav.xml:
--------------------------------------------------------------------------------
1 |
2 |
20 |
--------------------------------------------------------------------------------
/presentation/src/main/res/menu/menu_main_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/presentation/src/main/res/menu/menu_mycars_item_click.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/presentation/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
27 |
28 |
--------------------------------------------------------------------------------
/presentation/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6a94ff
4 | #a1c4ff
5 | #2a67cb
6 |
7 | #f9cb00
8 | #fffe50
9 | #c19b00
10 |
11 | #FFFFFF
12 | #000000
13 | #000000
14 | #000000
15 |
16 | #FFFFFF
17 | #FEFBFF
18 | #E4E2E6
19 | #C7C6CA
20 | #ACAAAF
21 | #5E5E62
22 | #46464A
23 | #1B1B1F
24 | #000000
25 |
--------------------------------------------------------------------------------
/presentation/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 8dp
4 | 16dp
5 | 24dp
6 | 32dp
7 |
8 | 57sp
9 | 45sp
10 | 36sp
11 |
12 | 32sp
13 | 28sp
14 | 24sp
15 |
16 | 22sp
17 | 16sp
18 | 14sp
19 |
20 | 14sp
21 | 12sp
22 | 11sp
23 |
24 | 16sp
25 | 14sp
26 | 12sp
27 |
--------------------------------------------------------------------------------
/presentation/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
27 |
28 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/presentation/src/main/res/xml/file_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
--------------------------------------------------------------------------------
/presentation/src/test/java/com/gta/presentation/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.gta.presentation
2 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | // Required in naver map
14 | jcenter()
15 | maven("https://naver.jfrog.io/artifactory/maven/")
16 | maven("https://jitpack.io/")
17 | }
18 | }
19 |
20 | rootProject.name = "UCMC"
21 | include(":app", ":data", ":domain", ":presentation")
22 |
--------------------------------------------------------------------------------