├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── google-services.json ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── yapp │ │ └── android2 │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── yapp │ │ │ └── android2 │ │ │ ├── ActivityLifecycleLogger.kt │ │ │ ├── BestFriendsApplication.kt │ │ │ ├── FCMService.kt │ │ │ ├── deeplink │ │ │ └── DeepLinkActivity.kt │ │ │ └── di │ │ │ └── AppModule.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── app_icon_splash_screen.xml │ │ ├── branding_image_splash_screen.xml │ │ └── ic_launcher_background.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values-night │ │ └── themes.xml │ │ ├── values-v31 │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── yapp │ └── android2 │ └── ExampleUnitTest.kt ├── build.gradle ├── core-deeplink ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── yapp │ └── android2 │ └── deeplink │ ├── AbstractDeepLinkModule.kt │ └── DeepLinkPrefixSpec.kt ├── core-design-system ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── res │ ├── drawable │ ├── app_logo.png │ ├── bg_oval_color_sub.xml │ ├── bg_rect_gray1_r10.xml │ ├── bg_rect_gray2_r10.xml │ ├── bg_rect_primary50_r10.xml │ ├── bg_rect_primary_r10.xml │ ├── bg_rect_primary_r18.xml │ ├── bg_rect_white_r20.xml │ ├── icon_add.xml │ ├── icon_app_logo.xml │ ├── icon_back_button.xml │ ├── icon_calendar.xml │ ├── icon_checkbox_checked.xml │ ├── icon_checkbox_unchecked.xml │ ├── icon_chevron_left.xml │ ├── icon_chevron_right.xml │ ├── icon_close.xml │ ├── icon_delete_button.xml │ ├── icon_home_active.xml │ ├── icon_home_inactive.xml │ ├── icon_left_arrow.xml │ ├── icon_no_notification.xml │ ├── icon_notification_logo.xml │ ├── icon_notifications.xml │ ├── icon_record_active.xml │ ├── icon_record_inactive.xml │ ├── icon_right_arrow.xml │ ├── icon_settings.xml │ └── selector_checkbox.xml │ ├── font │ ├── spoqa_han_sans.xml │ ├── spoqa_han_sans_bold.otf │ ├── spoqa_han_sans_medium.otf │ └── spoqa_han_sans_regular.otf │ └── values │ ├── colors.xml │ ├── strings.xml │ └── typography.xml ├── core ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── best │ │ └── friends │ │ └── core │ │ ├── AbstractViewHolder.kt │ │ ├── BaseActivity.kt │ │ ├── BaseFragment.kt │ │ ├── BaseViewModel.kt │ │ ├── BindingAdapters.kt │ │ ├── OnSingleClickListener.kt │ │ └── extensions │ │ ├── Context.kt │ │ ├── PixelExt.kt │ │ ├── String.kt │ │ ├── View.kt │ │ └── ZonedDateTime.kt │ └── res │ └── values │ └── ids.xml ├── credentials.gradle ├── data ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── yapp │ │ │ └── android2 │ │ │ └── data │ │ │ ├── di │ │ │ ├── LocalModule.kt │ │ │ ├── RemoteModule.kt │ │ │ ├── RepositoryModule.kt │ │ │ └── ServiceModule.kt │ │ │ ├── extensions │ │ │ └── Interceptor.kt │ │ │ ├── local │ │ │ ├── BestFriendSharedPreference.kt │ │ │ ├── LocalDataSource.kt │ │ │ ├── login │ │ │ │ ├── LoginLocalDataSource.kt │ │ │ │ └── LoginLocalDataSourceImpl.kt │ │ │ ├── logout │ │ │ │ ├── LogoutLocalDataSource.kt │ │ │ │ └── LogoutLocalDataSourceImpl.kt │ │ │ └── notification │ │ │ │ ├── NotificationLocalDataSource.kt │ │ │ │ └── NotificationLocalDataSourceImpl.kt │ │ │ ├── remote │ │ │ ├── RemoteDataSource.kt │ │ │ ├── login │ │ │ │ ├── LoginRemoteDataSource.kt │ │ │ │ └── LoginRemoteDataSourceImpl.kt │ │ │ ├── logout │ │ │ │ ├── LogoutRemoteDataSource.kt │ │ │ │ └── LogoutRemoteDataSourceImpl.kt │ │ │ ├── notification │ │ │ │ ├── NotificationRemoteDataSource.kt │ │ │ │ └── NotificationRemoteDataSourceImpl.kt │ │ │ ├── products │ │ │ │ ├── ProductsRemoteDataSource.kt │ │ │ │ └── ProductsRemoteDataSourceImpl.kt │ │ │ ├── record │ │ │ │ ├── RecordRemoteDataSource.kt │ │ │ │ └── RecordRemoteDataSourceImpl.kt │ │ │ ├── request │ │ │ │ ├── LoginForAppReviewRequest.kt │ │ │ │ ├── PostLoginRequest.kt │ │ │ │ ├── PostProductsRequest.kt │ │ │ │ ├── PostSavingRecordsRequest.kt │ │ │ │ └── UpdateProductsRequest.kt │ │ │ ├── version │ │ │ │ ├── AppVersionCheckDataSource.kt │ │ │ │ └── AppVersionCheckDataSourceImpl.kt │ │ │ └── withdraw │ │ │ │ ├── WithDrawRemoteDataSource.kt │ │ │ │ └── WithDrawRemoteDataSourceImpl.kt │ │ │ ├── repository │ │ │ ├── AppVersionRepositoryImpl.kt │ │ │ ├── LoginRepositoryImpl.kt │ │ │ ├── LogoutRepositoryImpl.kt │ │ │ ├── NotificationRepositoryImpl.kt │ │ │ ├── ProductsRepositoryImpl.kt │ │ │ ├── RecordRepositoryImpl.kt │ │ │ ├── RemoteConfigRepositoryImpl.kt │ │ │ ├── SettingRepositoryImpl.kt │ │ │ └── WithDrawRepositoryImpl.kt │ │ │ └── service │ │ │ ├── LoginService.kt │ │ │ ├── LogoutService.kt │ │ │ ├── NotificationService.kt │ │ │ ├── ProductsService.kt │ │ │ ├── RecordService.kt │ │ │ ├── Service.kt │ │ │ ├── WithDrawService.kt │ │ │ ├── convert │ │ │ ├── BestFriendResponseConvertFactory.kt │ │ │ └── BestFriendResponseConverter.kt │ │ │ └── interceptor │ │ │ └── HeaderInterceptor.kt │ └── res │ │ └── xml │ │ └── remote_config_default.xml │ └── test │ └── java │ └── com │ └── yapp │ └── android2 │ └── data │ └── ExampleUnitTest.kt ├── dependencies.gradle ├── domain ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── yapp │ └── android2 │ └── domain │ ├── Entity.kt │ ├── UseCase.kt │ ├── entity │ ├── FCMToken.kt │ ├── Login.kt │ ├── Notification.kt │ ├── Product.kt │ ├── RecentNotification.kt │ ├── RenewalResponse.kt │ ├── User.kt │ ├── Version.kt │ └── base │ │ ├── ApiResponse.kt │ │ ├── BaseResponse.kt │ │ └── Record.kt │ ├── key │ └── Key.kt │ ├── repository │ ├── Notification │ │ └── NotificationRepository.kt │ ├── ProductsRepository.kt │ ├── RemoteConfigRepository.kt │ ├── Repository.kt │ ├── login │ │ └── LoginRepository.kt │ ├── logout │ │ └── LogoutRepository.kt │ ├── record │ │ └── RecordRepository.kt │ ├── setting │ │ └── SettingRepository.kt │ ├── version │ │ └── AppVersionRepository.kt │ └── withdraw │ │ └── WithDrawRepository.kt │ ├── url │ └── WebURL.kt │ └── usecase │ ├── GetUserUseCase.kt │ ├── IsUnreadNotification.kt │ ├── LoginUseCase.kt │ ├── PostFCMTokenUseCase.kt │ └── RecordUseCase.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── key ├── BestFriends └── private_key.pepk ├── navigator ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── best │ └── friends │ └── navigator │ ├── HomeNavigator.kt │ ├── LoginNavigator.kt │ ├── LogoutNavigator.kt │ ├── Navigator.kt │ ├── NotificationNavigator.kt │ ├── SecondsNavigator.kt │ ├── SettingNavigator.kt │ ├── WebViewNavigator.kt │ └── WithDrawNavigator.kt ├── presentation ├── drawwith │ ├── .gitignore │ ├── build.gradle │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── yapp │ │ │ └── android2 │ │ │ └── drawwith │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── yapp │ │ │ │ └── android2 │ │ │ │ └── drawwith │ │ │ │ ├── WithDrawActivity.kt │ │ │ │ ├── WithDrawModule.kt │ │ │ │ ├── WithDrawNavigatorImpl.kt │ │ │ │ └── WithDrawViewModel.kt │ │ └── res │ │ │ ├── layout │ │ │ └── activity_with_draw.xml │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── yapp │ │ └── android2 │ │ └── drawwith │ │ └── ExampleUnitTest.kt ├── home │ ├── .gitignore │ ├── build.gradle │ └── src │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── best │ │ │ │ └── friends │ │ │ │ └── home │ │ │ │ ├── AbstractTabSelectedListener.kt │ │ │ │ ├── FragmentStateAdapter.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── MainDeepLinkModule.kt │ │ │ │ ├── MainViewModel.kt │ │ │ │ ├── dialog │ │ │ │ ├── DatePickerWithTodayButtonDialog.kt │ │ │ │ └── HorizontalButtonsDialogFragment.kt │ │ │ │ ├── home │ │ │ │ ├── HomeFragment.kt │ │ │ │ ├── HomeModule.kt │ │ │ │ ├── HomeNavigatorImpl.kt │ │ │ │ ├── HomeViewModel.kt │ │ │ │ └── SavingsListAdapter.kt │ │ │ │ ├── register │ │ │ │ ├── SavingItemAddActivity.kt │ │ │ │ └── SavingItemAddViewModel.kt │ │ │ │ └── update │ │ │ │ ├── SavingItemUpdateActivity.kt │ │ │ │ ├── SavingItemUpdateModule.kt │ │ │ │ └── SavingItemUpdateViewModel.kt │ │ └── res │ │ │ ├── drawable │ │ │ └── selector_saving_add_button.xml │ │ │ ├── layout │ │ │ ├── activity_main.xml │ │ │ ├── activity_saving_item_add.xml │ │ │ ├── activity_saving_item_update.xml │ │ │ ├── fragment_dialog_date_picker_today_button.xml │ │ │ ├── fragment_dialog_horizontal_buttons.xml │ │ │ ├── fragment_home.xml │ │ │ ├── layout_custom_tab.xml │ │ │ ├── layout_saving_add.xml │ │ │ ├── layout_saving_item.xml │ │ │ └── layout_saving_item_empty.xml │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── best │ │ └── friends │ │ └── home │ │ └── ExampleUnitTest.kt ├── login │ ├── .gitignore │ ├── build.gradle │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── best │ │ │ └── friends │ │ │ └── login │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── best │ │ │ │ └── friends │ │ │ │ └── login │ │ │ │ ├── LoginActivity.kt │ │ │ │ ├── LoginDeepLinkModule.kt │ │ │ │ ├── LoginForAppReviewActivity.kt │ │ │ │ ├── LoginForAppReviewViewModel.kt │ │ │ │ ├── LoginModule.kt │ │ │ │ ├── LoginNavigatorImpl.kt │ │ │ │ ├── LoginViewModel.kt │ │ │ │ └── UserApiClientExt.kt │ │ └── res │ │ │ ├── drawable │ │ │ ├── bg_google_login.xml │ │ │ ├── bg_kakao_login.xml │ │ │ ├── ic_best_friends.xml │ │ │ ├── ic_kakao.xml │ │ │ └── icon_google.png │ │ │ ├── font │ │ │ └── noto_sans_kr_medium.otf │ │ │ ├── layout │ │ │ ├── activity_login.xml │ │ │ └── activity_login_for_app_review.xml │ │ │ └── values │ │ │ ├── google_strings.xml │ │ │ ├── kakao_strings.xml │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── best │ │ └── friends │ │ └── login │ │ └── ExampleUnitTest.kt ├── logout │ ├── .gitignore │ ├── build.gradle │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── yapp │ │ │ └── android2 │ │ │ └── logout │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── yapp │ │ │ │ └── android2 │ │ │ │ └── logout │ │ │ │ ├── LogoutActivity.kt │ │ │ │ ├── LogoutModule.kt │ │ │ │ ├── LogoutNavigatorImpl.kt │ │ │ │ └── LogoutViewModel.kt │ │ └── res │ │ │ ├── layout │ │ │ └── activity_logout.xml │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── yapp │ │ └── android2 │ │ └── logout │ │ └── ExampleUnitTest.kt ├── notification │ ├── .gitignore │ ├── build.gradle │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── best │ │ │ └── friends │ │ │ └── notification │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── best │ │ │ │ └── friends │ │ │ │ └── notification │ │ │ │ ├── NotificationActivity.kt │ │ │ │ ├── NotificationAdapter.kt │ │ │ │ ├── NotificationDeepLinkModule.kt │ │ │ │ ├── NotificationModule.kt │ │ │ │ ├── NotificationNavigatorImpl.kt │ │ │ │ └── NotificationViewModel.kt │ │ └── res │ │ │ ├── layout │ │ │ ├── activity_notification.xml │ │ │ └── item_notification.xml │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── best │ │ └── friends │ │ └── notification │ │ └── ExampleUnitTest.kt ├── record │ ├── .gitignore │ ├── build.gradle │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── yapp │ │ │ └── android2 │ │ │ └── record │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── yapp │ │ │ │ └── android2 │ │ │ │ └── record │ │ │ │ ├── BindingAdapter.kt │ │ │ │ ├── RecordFragment.kt │ │ │ │ ├── RecordViewHolder.kt │ │ │ │ ├── RecordViewModel.kt │ │ │ │ ├── adapter │ │ │ │ └── RecordAdapter.kt │ │ │ │ └── view │ │ │ │ ├── Calendar.kt │ │ │ │ └── RoundedPagerTransformer.kt │ │ └── res │ │ │ ├── drawable │ │ │ ├── shape_corner15_bg_ff8717.xml │ │ │ └── shape_dot_line_bg_659dff.xml │ │ │ ├── layout │ │ │ ├── calendar_header_view.xml │ │ │ ├── calendar_item.xml │ │ │ ├── fragment_record.xml │ │ │ └── item_record.xml │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── yapp │ │ └── android2 │ │ └── record │ │ └── ExampleUnitTest.kt ├── settings │ ├── .gitignore │ ├── build.gradle │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── yapp │ │ │ └── android2 │ │ │ └── settings │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── yapp │ │ │ │ └── android2 │ │ │ │ └── settings │ │ │ │ ├── SettingViewModel.kt │ │ │ │ ├── SettingsActivity.kt │ │ │ │ ├── SettingsModule.kt │ │ │ │ ├── SettingsNavigatorImpl.kt │ │ │ │ └── SettingsParseFactory.kt │ │ └── res │ │ │ ├── anim │ │ │ ├── activity_in_transition.xml │ │ │ ├── activity_out_transition.xml │ │ │ └── activity_stay_transition.xml │ │ │ ├── drawable │ │ │ ├── selector_switch_thumb.xml │ │ │ └── selector_switch_track.xml │ │ │ ├── layout │ │ │ └── activity_settings.xml │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── yapp │ │ └── android2 │ │ └── settings │ │ └── ExampleUnitTest.kt ├── splash │ ├── .gitignore │ ├── build.gradle │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── best │ │ │ └── friends │ │ │ └── splash │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── best │ │ │ │ └── friends │ │ │ │ └── splash │ │ │ │ ├── SplashActivity.kt │ │ │ │ └── SplashViewModel.kt │ │ └── res │ │ │ ├── layout │ │ │ └── activity_splash.xml │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── best │ │ └── friends │ │ └── splash │ │ └── ExampleUnitTest.kt └── webview │ ├── .gitignore │ ├── build.gradle │ └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── yapp │ │ └── android2 │ │ └── webview │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── yapp │ │ │ └── android2 │ │ │ └── webview │ │ │ ├── BestFriendWebChromeClient.kt │ │ │ ├── BestFriendWebViewClient.kt │ │ │ ├── WebViewActivity.kt │ │ │ ├── WebViewNavigatorImpl.kt │ │ │ └── WebViewNavigatorModule.kt │ └── res │ │ ├── layout │ │ └── activity_web_view.xml │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── yapp │ └── android2 │ └── webview │ └── ExampleUnitTest.kt └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | # 20th-Android-Team-2-Android 2 | [20th] Android 2팀 절약과 친해지기 (절친) 안드로이드 프로젝트 저장소 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/consumer-rules.pro -------------------------------------------------------------------------------- /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. 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 22 | 23 | # https://developers.kakao.com/docs/latest/en/getting-started/sdk-android#configure-for-shrinking-and-obfuscation-(optional) 24 | -keep class com.kakao.sdk.**.model.* { ; } 25 | -keep class * extends com.google.gson.TypeAdapter 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/yapp/android2/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.yapp.android2", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/yapp/android2/BestFriendsApplication.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2 2 | 3 | import android.app.Application 4 | import com.best.friends.login.R 5 | import com.kakao.sdk.common.KakaoSdk 6 | import dagger.hilt.android.HiltAndroidApp 7 | import timber.log.Timber 8 | import javax.inject.Inject 9 | 10 | @HiltAndroidApp 11 | class BestFriendsApplication : Application() { 12 | 13 | @Inject 14 | lateinit var activityLogger: ActivityLifecycleLogger 15 | 16 | override fun onCreate() { 17 | super.onCreate() 18 | initTimber() 19 | initKakaoSdk() 20 | registerActivityLifecycleCallbacks() 21 | } 22 | 23 | private fun initTimber() { 24 | Timber.plant(Timber.DebugTree()) 25 | } 26 | 27 | private fun initKakaoSdk() { 28 | KakaoSdk.init(this, getString(R.string.kakao_native_app_key)) 29 | } 30 | 31 | private fun registerActivityLifecycleCallbacks() { 32 | registerActivityLifecycleCallbacks(activityLogger) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/yapp/android2/deeplink/DeepLinkActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.deeplink 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.airbnb.deeplinkdispatch.BaseDeepLinkDelegate 6 | import com.airbnb.deeplinkdispatch.DeepLinkHandler 7 | import com.best.friends.home.MainDeepLinkModule 8 | import com.best.friends.home.MainDeepLinkModuleRegistry 9 | import com.best.friends.login.LoginDeepLinkModule 10 | import com.best.friends.login.LoginDeepLinkModuleRegistry 11 | import com.best.friends.notification.NotificationDeepLinkModule 12 | import com.best.friends.notification.NotificationDeepLinkModuleRegistry 13 | 14 | @DeepLinkHandler( 15 | value = [ 16 | AbstractDeepLinkModule::class, 17 | MainDeepLinkModule::class, 18 | NotificationDeepLinkModule::class, 19 | LoginDeepLinkModule::class 20 | ] 21 | ) 22 | class DeepLinkActivity : AppCompatActivity() { 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | 27 | val deepLinkDelegate = BaseDeepLinkDelegate( 28 | listOf( 29 | AbstractDeepLinkModuleRegistry(), 30 | MainDeepLinkModuleRegistry(), 31 | NotificationDeepLinkModuleRegistry(), 32 | LoginDeepLinkModuleRegistry() 33 | ) 34 | ) 35 | 36 | deepLinkDelegate.dispatchFrom(activity = this@DeepLinkActivity) 37 | finish() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/yapp/android2/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.di 2 | 3 | import com.google.firebase.messaging.FirebaseMessaging 4 | import com.google.gson.Gson 5 | import com.google.gson.GsonBuilder 6 | import com.yapp.android2.domain.key.POLICY_WEB_URL_KEY 7 | import com.yapp.android2.domain.url.WebURL 8 | import dagger.Module 9 | import dagger.Provides 10 | import dagger.hilt.InstallIn 11 | import dagger.hilt.components.SingletonComponent 12 | import javax.inject.Named 13 | import javax.inject.Singleton 14 | 15 | @Module 16 | @InstallIn(SingletonComponent::class) 17 | object AppModule { 18 | 19 | @Singleton 20 | @Provides 21 | fun bindsFirebaseMessagingProvide(): FirebaseMessaging = FirebaseMessaging.getInstance() 22 | 23 | @Provides 24 | @Singleton 25 | fun provideGson(): Gson { 26 | return GsonBuilder().create() 27 | } 28 | 29 | @Provides 30 | @Singleton 31 | @Named(POLICY_WEB_URL_KEY) 32 | fun providesWebURL(): WebURL { 33 | /** 34 | * 기존 raw url 35 | * @param = https://rustic-decade-83c.notion.site/cdfb1587f8854c7e86a2ee46ec471311 36 | **/ 37 | 38 | return WebURL("https://bit.ly/3yn5yL4") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/app_icon_splash_screen.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values-v31/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 절친 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/test/java/com/yapp/android2/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | apply from: 'dependencies.gradle' 3 | 4 | repositories { 5 | google() 6 | mavenCentral() 7 | maven { url "https://plugins.gradle.org/m2/" } 8 | } 9 | 10 | dependencies { 11 | classpath 'org.jlleitschuh.gradle:ktlint-gradle:10.3.0' 12 | classpath 'com.google.dagger:hilt-android-gradle-plugin:2.42' 13 | classpath 'com.google.gms:google-services:4.3.13' 14 | classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1' 15 | } 16 | } 17 | 18 | plugins { 19 | id 'com.android.application' version '7.1.2' apply false 20 | id 'com.android.library' version '7.1.2' apply false 21 | id 'org.jetbrains.kotlin.android' version '1.7.10' apply false 22 | id "org.jlleitschuh.gradle.ktlint" version "10.2.1" 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | 29 | ext { 30 | schemes = [ 31 | bestfriends : "bestfriends" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /core-deeplink/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core-deeplink/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | id 'kotlin-kapt' 5 | } 6 | 7 | android { 8 | compileSdk 32 9 | 10 | defaultConfig { 11 | minSdk 23 12 | targetSdk 32 13 | 14 | buildConfigField 'String', 'SCHEME_PREFIX', "\"${schemes['bestfriends']}\"" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | kotlinOptions { 28 | jvmTarget = '1.8' 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation(jetpackDeps) 34 | implementation deps.kotlin.stdlib 35 | implementation deps.deepLink.core 36 | kapt deps.deepLink.processor 37 | } 38 | -------------------------------------------------------------------------------- /core-deeplink/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /core-deeplink/src/main/java/com/yapp/android2/deeplink/AbstractDeepLinkModule.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.deeplink 2 | 3 | import com.airbnb.deeplinkdispatch.DeepLinkModule 4 | 5 | @DeepLinkModule 6 | abstract class AbstractDeepLinkModule 7 | -------------------------------------------------------------------------------- /core-deeplink/src/main/java/com/yapp/android2/deeplink/DeepLinkPrefixSpec.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.deeplink 2 | 3 | import com.airbnb.deeplinkdispatch.DeepLinkSpec 4 | 5 | @DeepLinkSpec( 6 | prefix = [ 7 | BuildConfig.SCHEME_PREFIX + "://" 8 | ] 9 | ) 10 | @kotlin.annotation.Retention(AnnotationRetention.BINARY) 11 | annotation class DeepLinkPrefixSpec(vararg val value: String) 12 | -------------------------------------------------------------------------------- /core-design-system/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core-design-system/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | compileSdk 32 8 | 9 | defaultConfig { 10 | minSdk 23 11 | targetSdk 32 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles "consumer-rules.pro" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | kotlinOptions { 28 | jvmTarget = '1.8' 29 | } 30 | } 31 | 32 | dependencies { 33 | 34 | implementation(jetpackDeps) 35 | 36 | // testing 37 | testImplementation(testDeps) 38 | androidTestImplementation(androidTestDeps) 39 | } 40 | -------------------------------------------------------------------------------- /core-design-system/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/app_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/core-design-system/src/main/res/drawable/app_logo.png -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/bg_oval_color_sub.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/bg_rect_gray1_r10.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/bg_rect_gray2_r10.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/bg_rect_primary50_r10.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/bg_rect_primary_r10.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/bg_rect_primary_r18.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/bg_rect_white_r20.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_add.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_app_logo.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_back_button.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_calendar.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_checkbox_checked.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_checkbox_unchecked.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_chevron_left.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_chevron_right.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_delete_button.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_home_active.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_home_inactive.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_left_arrow.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_no_notification.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_notification_logo.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_notifications.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_record_active.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_record_inactive.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/icon_right_arrow.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/drawable/selector_checkbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/font/spoqa_han_sans.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 14 | 15 | 16 | 23 | 24 | 25 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/font/spoqa_han_sans_bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/core-design-system/src/main/res/font/spoqa_han_sans_bold.otf -------------------------------------------------------------------------------- /core-design-system/src/main/res/font/spoqa_han_sans_medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/core-design-system/src/main/res/font/spoqa_han_sans_medium.otf -------------------------------------------------------------------------------- /core-design-system/src/main/res/font/spoqa_han_sans_regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/core-design-system/src/main/res/font/spoqa_han_sans_regular.otf -------------------------------------------------------------------------------- /core-design-system/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | 9 | 10 | #FF8717 11 | #4DFF8717 12 | #80FF8717 13 | #659dff 14 | #A5C6FF 15 | #32A5C6FF 16 | 17 | #000000 18 | #B2000000 19 | #FFFFFFFF 20 | #CCFFFFFF 21 | 22 | #F5F6F9 23 | #C6C6C6 24 | #A6A6A6 25 | #252525 26 | 27 | #BDBDBD 28 | #00000000 29 | #99333333 30 | 31 | 32 | -------------------------------------------------------------------------------- /core-design-system/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 기록 6 | 등록 7 | 삭제 8 | 수정 9 | 취소 10 | 확인 11 | 12 | 오늘 13 | 14 | 15 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-android' 5 | id 'kotlin-kapt' 6 | id 'kotlin-parcelize' 7 | id 'dagger.hilt.android.plugin' 8 | } 9 | 10 | android { 11 | compileSdk 32 12 | 13 | defaultConfig { 14 | minSdk 23 15 | targetSdk 32 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | consumerProguardFiles "consumer-rules.pro" 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | compileOptions { 28 | sourceCompatibility JavaVersion.VERSION_1_8 29 | targetCompatibility JavaVersion.VERSION_1_8 30 | } 31 | kotlinOptions { 32 | jvmTarget = '1.8' 33 | } 34 | 35 | buildFeatures { 36 | dataBinding true 37 | } 38 | } 39 | 40 | dependencies { 41 | 42 | implementation(jetpackDeps) 43 | implementation(coroutines) 44 | 45 | implementation deps.hilt.core 46 | kapt deps.hilt.compiler 47 | 48 | // testing 49 | testImplementation(testDeps) 50 | androidTestImplementation(androidTestDeps) 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/AbstractViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | abstract class AbstractViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 7 | abstract fun bind(data: T) 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.LayoutRes 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.databinding.DataBindingUtil 7 | import androidx.databinding.ViewDataBinding 8 | import kotlinx.coroutines.launch 9 | import kotlinx.coroutines.runBlocking 10 | 11 | abstract class BaseActivity( 12 | @LayoutRes private val layoutId: Int 13 | ) : AppCompatActivity() { 14 | 15 | protected lateinit var binding: T 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | binding = DataBindingUtil.setContentView(this, layoutId) 20 | binding.lifecycleOwner = this 21 | } 22 | 23 | override fun onDestroy() { 24 | binding.unbind() 25 | super.onDestroy() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core 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.ViewDataBinding 9 | import androidx.fragment.app.Fragment 10 | import java.lang.IllegalStateException 11 | 12 | abstract class BaseFragment(@LayoutRes layout: Int) : Fragment(layout) { 13 | 14 | protected var _binding: VB? = null 15 | val binding: VB 16 | get() = _binding ?: throw IllegalStateException("_binding is not initialized !") 17 | 18 | abstract val viewModel: VM 19 | 20 | override fun onCreateView( 21 | inflater: LayoutInflater, 22 | container: ViewGroup?, 23 | savedInstanceState: Bundle? 24 | ): View? { 25 | binding.lifecycleOwner = viewLifecycleOwner 26 | return binding.root 27 | } 28 | 29 | protected fun onBind(body: VB.() -> Unit) { 30 | binding.run(body) 31 | } 32 | 33 | override fun onDestroyView() { 34 | binding.unbind() 35 | super.onDestroyView() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import kotlinx.coroutines.flow.MutableSharedFlow 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.flow.SharedFlow 8 | import kotlinx.coroutines.flow.StateFlow 9 | import kotlinx.coroutines.launch 10 | 11 | abstract class BaseViewModel : ViewModel() { 12 | 13 | protected val _loading = MutableStateFlow(false) 14 | val loading: StateFlow 15 | get() = _loading 16 | 17 | private val _error = MutableSharedFlow() 18 | val error: SharedFlow 19 | get() = _error 20 | 21 | protected fun startLoading() { 22 | _loading.value = true 23 | } 24 | 25 | protected fun stopLoading() { 26 | _loading.value = false 27 | } 28 | 29 | protected fun sendErrorMessage(throwable: Throwable) { 30 | sendErrorMessage(throwable.message.toString()) 31 | } 32 | 33 | protected fun sendErrorMessage(message: String?) { 34 | viewModelScope.launch { 35 | _error.emit(message.orEmpty()) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/BindingAdapters.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core 2 | 3 | import android.view.View 4 | import androidx.databinding.BindingAdapter 5 | 6 | @BindingAdapter("android:visibility") 7 | fun setVisibility(view: View, visible: Boolean) { 8 | view.visibility = if (visible) View.VISIBLE else View.GONE 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/OnSingleClickListener.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core 2 | 3 | import android.os.SystemClock 4 | import android.view.View 5 | import androidx.databinding.BindingAdapter 6 | 7 | class OnSingleClickListener( 8 | private val interval: Int, 9 | private val onSingleClick: (View) -> Unit 10 | ) : View.OnClickListener { 11 | 12 | private var lastClickedTime: Long = 0L 13 | 14 | override fun onClick(v: View) { 15 | val elapsedRealTime = SystemClock.elapsedRealtime() 16 | if ((elapsedRealTime - lastClickedTime) < interval) { 17 | return 18 | } 19 | lastClickedTime = elapsedRealTime 20 | onSingleClick(v) 21 | } 22 | } 23 | 24 | fun View.setOnSingleClickListener( 25 | interval: Int = 200, 26 | onClick: (View) -> Unit = { } 27 | ) { 28 | setOnClickListener(OnSingleClickListener(interval, onClick)) 29 | } 30 | 31 | @BindingAdapter("android:onClick", "throttleMillis", requireAll = false) 32 | fun setOnSingleClickListener( 33 | view: View, 34 | listener: View.OnClickListener?, 35 | throttleMillis: Long? = 200 36 | ) { 37 | view.setOnClickListener(listener?.let { 38 | View.OnClickListener { 39 | val lastClickedAt = (view.getTag(R.id.last_click_at) as Long?) ?: 0L 40 | if (System.currentTimeMillis() > lastClickedAt + (throttleMillis ?: 200L)) { 41 | listener.onClick(view) 42 | view.setTag(R.id.last_click_at, System.currentTimeMillis()) 43 | } 44 | } 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/extensions/Context.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core.extensions 2 | 3 | import android.content.Context 4 | import android.widget.Toast 5 | import androidx.fragment.app.Fragment 6 | 7 | fun Context.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) { 8 | Toast.makeText(this, message, duration).show() 9 | } 10 | 11 | fun Fragment.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) { 12 | Toast.makeText(requireContext(), message, duration).show() 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/extensions/PixelExt.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core.extensions 2 | 3 | import android.content.Context 4 | import android.content.res.Resources 5 | import android.util.TypedValue 6 | import kotlin.math.roundToInt 7 | 8 | fun dpToPx(dp: Int, context: Context): Int = 9 | TypedValue.applyDimension( 10 | TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), 11 | context.resources.displayMetrics 12 | ).toInt() 13 | 14 | val Int.dp: Int 15 | @JvmSynthetic inline get() = TypedValue.applyDimension( 16 | TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), 17 | Resources.getSystem().displayMetrics 18 | ).roundToInt() 19 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/extensions/String.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core.extensions 2 | 3 | val String.Companion.Empty 4 | get() = "" 5 | 6 | val String.Companion.Space 7 | get() = " " 8 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/extensions/View.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core.extensions 2 | 3 | import android.view.View 4 | import androidx.core.view.isInvisible 5 | import androidx.core.view.isVisible 6 | 7 | fun View.visible() { 8 | this.isVisible = true 9 | } 10 | 11 | fun View.visibleOrGone(isVisible: Boolean) { 12 | this.isVisible = isVisible 13 | } 14 | 15 | fun View.gone() { 16 | this.isVisible = false 17 | } 18 | 19 | fun View.goneIfNeeded() { 20 | if (isVisible) { 21 | this.isVisible = false 22 | } 23 | } 24 | 25 | fun View.invisible() { 26 | this.isInvisible = true 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/com/best/friends/core/extensions/ZonedDateTime.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.core.extensions 2 | 3 | import java.time.ZonedDateTime 4 | 5 | val ZonedDateTime.isToday: Boolean 6 | get() { 7 | val now = ZonedDateTime.now() 8 | return year == now.year && 9 | monthValue == now.monthValue && 10 | dayOfMonth == now.dayOfMonth 11 | } 12 | 13 | val ZonedDateTime.isNotToday: Boolean 14 | get() = !isToday 15 | -------------------------------------------------------------------------------- /core/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /credentials.gradle: -------------------------------------------------------------------------------- 1 | project.ext { 2 | keyStoreFile="BestFriends" 3 | keyStorePassword="best_friends" 4 | keyAlias="BestFriends_SigningKey" 5 | keyPassword="best_friends" 6 | } 7 | -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /data/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id "kotlin-kapt" 5 | id 'dagger.hilt.android.plugin' 6 | } 7 | 8 | android { 9 | compileSdk 32 10 | 11 | defaultConfig { 12 | minSdk 23 13 | targetSdk 32 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | consumerProguardFiles "consumer-rules.pro" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | kotlinOptions { 30 | jvmTarget = '1.8' 31 | } 32 | } 33 | 34 | dependencies { 35 | 36 | implementation(project(":domain")) 37 | implementation(project(":presentation:login")) 38 | 39 | implementation(jetpackDeps) 40 | implementation(coroutines) 41 | 42 | implementation deps.hilt.core 43 | kapt deps.hilt.compiler 44 | 45 | implementation platform(deps.firebaseBom) 46 | implementation deps.remoteConfig 47 | 48 | implementation deps.network.core 49 | implementation deps.network.adapter 50 | implementation deps.network.interceptor 51 | 52 | implementation deps.timber 53 | 54 | // testing 55 | testImplementation(testDeps) 56 | androidTestImplementation(androidTestDeps) 57 | 58 | // chucker 59 | debugImplementation deps.chucker.library 60 | releaseImplementation deps.chucker.noOp 61 | } 62 | -------------------------------------------------------------------------------- /data/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/di/LocalModule.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.di 2 | 3 | import android.content.Context 4 | import com.google.gson.Gson 5 | import com.yapp.android2.data.local.BestFriendSharedPreferenceProviderImpl 6 | import com.yapp.android2.data.local.SharedPreferenceProvider 7 | import com.yapp.android2.data.local.login.LoginLocalDataSource 8 | import com.yapp.android2.data.local.login.LoginLocalDataSourceImpl 9 | import com.yapp.android2.data.local.logout.LogoutLocalDataSource 10 | import com.yapp.android2.data.local.logout.LogoutLocalDataSourceImpl 11 | import com.yapp.android2.data.local.notification.NotificationLocalDataSource 12 | import com.yapp.android2.data.local.notification.NotificationLocalDataSourceImpl 13 | import dagger.Module 14 | import dagger.Provides 15 | import dagger.hilt.InstallIn 16 | import dagger.hilt.android.qualifiers.ApplicationContext 17 | import dagger.hilt.components.SingletonComponent 18 | 19 | @Module 20 | @InstallIn(SingletonComponent::class) 21 | internal class LocalModule { 22 | 23 | @Provides 24 | fun bindsLocalSharedPreferenceProvide( 25 | @ApplicationContext context: Context, 26 | gson: Gson 27 | ): SharedPreferenceProvider { 28 | return BestFriendSharedPreferenceProviderImpl(context, gson) 29 | } 30 | 31 | @Provides 32 | fun provideLoginLocalDataModule(sharedPreference: BestFriendSharedPreferenceProviderImpl): LoginLocalDataSource = 33 | LoginLocalDataSourceImpl(sharedPreference) 34 | 35 | @Provides 36 | fun provideLogoutLocalDataModule(sharedPreference: BestFriendSharedPreferenceProviderImpl): LogoutLocalDataSource = 37 | LogoutLocalDataSourceImpl(sharedPreference) 38 | 39 | @Provides 40 | fun provideNotificationLocalDataModule(sharedPreference: BestFriendSharedPreferenceProviderImpl): NotificationLocalDataSource = 41 | NotificationLocalDataSourceImpl(sharedPreference) 42 | } 43 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/extensions/Interceptor.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.extensions 2 | 3 | import okhttp3.Interceptor 4 | import okhttp3.OkHttpClient 5 | 6 | internal fun createInterceptors(interceptors: List): OkHttpClient { 7 | return OkHttpClient.Builder() 8 | .addInterceptors(interceptors) 9 | .build() 10 | } 11 | 12 | 13 | private fun OkHttpClient.Builder.addInterceptors(interceptor: List): OkHttpClient.Builder = apply { 14 | interceptor.forEach(::addInterceptor) 15 | } 16 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/local/LocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.local 2 | 3 | interface LocalDataSource 4 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/local/login/LoginLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.local.login 2 | 3 | import com.yapp.android2.data.local.LocalDataSource 4 | import com.yapp.android2.domain.entity.User 5 | 6 | interface LoginLocalDataSource: LocalDataSource { 7 | fun saveUser(user: User) 8 | fun getUser(): User 9 | } 10 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/local/login/LoginLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.local.login 2 | 3 | import com.yapp.android2.data.local.BestFriendSharedPreferenceProviderImpl 4 | import com.yapp.android2.domain.entity.User 5 | import javax.inject.Inject 6 | 7 | class LoginLocalDataSourceImpl @Inject constructor( 8 | private val preference: BestFriendSharedPreferenceProviderImpl 9 | ): LoginLocalDataSource { 10 | 11 | override fun saveUser(user: User) { 12 | preference.user = user 13 | } 14 | 15 | override fun getUser(): User { 16 | return preference.user 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/local/logout/LogoutLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.local.logout 2 | 3 | interface LogoutLocalDataSource { 4 | fun clearUser() 5 | fun clearAccessToken() 6 | fun clearRefreshToken() 7 | } 8 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/local/logout/LogoutLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.local.logout 2 | 3 | import com.yapp.android2.data.local.SharedPreferenceProvider 4 | import com.yapp.android2.domain.entity.User 5 | import com.yapp.android2.domain.key.ACCESS_TOKEN_KEY 6 | import com.yapp.android2.domain.key.REFRESH_TOKEN_KEY 7 | import javax.inject.Inject 8 | 9 | class LogoutLocalDataSourceImpl @Inject constructor( 10 | private val preferenceProvider: SharedPreferenceProvider 11 | ) : LogoutLocalDataSource { 12 | 13 | override fun clearUser() { 14 | preferenceProvider.user = User.EMPTY 15 | } 16 | 17 | override fun clearAccessToken() { 18 | preferenceProvider.putString(ACCESS_TOKEN_KEY, "") 19 | } 20 | 21 | override fun clearRefreshToken() { 22 | preferenceProvider.putString(REFRESH_TOKEN_KEY, "") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/local/notification/NotificationLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.local.notification 2 | 3 | interface NotificationLocalDataSource { 4 | fun saveLastNotificationTime(time: String) 5 | fun getLastNotificationTime(): String 6 | } 7 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/local/notification/NotificationLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.local.notification 2 | 3 | import com.yapp.android2.data.local.BestFriendSharedPreferenceProviderImpl 4 | import com.yapp.android2.domain.key.LAST_NOTIFICATION_TIME_KEY 5 | import javax.inject.Inject 6 | 7 | class NotificationLocalDataSourceImpl @Inject constructor( 8 | private val preference: BestFriendSharedPreferenceProviderImpl 9 | ): NotificationLocalDataSource { 10 | override fun saveLastNotificationTime(time: String) { 11 | preference.putString(LAST_NOTIFICATION_TIME_KEY, time) 12 | } 13 | 14 | override fun getLastNotificationTime(): String { 15 | return preference.getString(LAST_NOTIFICATION_TIME_KEY) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/RemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote 2 | 3 | interface RemoteDataSource 4 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/login/LoginRemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.login 2 | 3 | import com.yapp.android2.data.remote.RemoteDataSource 4 | import com.yapp.android2.data.remote.request.LoginForAppReviewRequest 5 | import com.yapp.android2.data.remote.request.PostLoginRequest 6 | import com.yapp.android2.domain.entity.FCMToken 7 | import com.yapp.android2.domain.entity.User 8 | 9 | interface LoginRemoteDataSource : RemoteDataSource { 10 | 11 | suspend fun postLogin(request: PostLoginRequest): User 12 | 13 | suspend fun postFCMToken(request: FCMToken) 14 | 15 | suspend fun loginForAppReview(request: LoginForAppReviewRequest): User 16 | } 17 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/login/LoginRemoteDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.login 2 | 3 | import com.yapp.android2.data.remote.request.LoginForAppReviewRequest 4 | import com.yapp.android2.data.remote.request.PostLoginRequest 5 | import com.yapp.android2.data.service.LoginService 6 | import com.yapp.android2.domain.entity.FCMToken 7 | import com.yapp.android2.domain.entity.User 8 | import javax.inject.Inject 9 | 10 | class LoginRemoteDataSourceImpl @Inject constructor( 11 | private val loginService: LoginService 12 | ) : LoginRemoteDataSource { 13 | 14 | override suspend fun postLogin(request: PostLoginRequest): User { 15 | return loginService.postLogin(request).data ?: User.EMPTY 16 | } 17 | 18 | override suspend fun postFCMToken(request: FCMToken) { 19 | return loginService.postFCMToken(request) 20 | } 21 | 22 | override suspend fun loginForAppReview(request: LoginForAppReviewRequest): User { 23 | return loginService.loginForAppReview(request).data ?: User.EMPTY 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/logout/LogoutRemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.logout 2 | 3 | interface LogoutRemoteDataSource { 4 | suspend fun logout() 5 | } 6 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/logout/LogoutRemoteDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.logout 2 | 3 | import com.yapp.android2.data.service.LogoutService 4 | import javax.inject.Inject 5 | 6 | class LogoutRemoteDataSourceImpl @Inject constructor( 7 | private val logoutService: LogoutService 8 | ): LogoutRemoteDataSource { 9 | 10 | override suspend fun logout() = logoutService.logout() 11 | 12 | } 13 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/notification/NotificationRemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.notification 2 | 3 | import com.yapp.android2.data.remote.RemoteDataSource 4 | import com.yapp.android2.domain.entity.Notification 5 | import com.yapp.android2.domain.entity.RecentNotification 6 | 7 | interface NotificationRemoteDataSource: RemoteDataSource { 8 | suspend fun getNotification(): List 9 | suspend fun getRecentCreatedNotification(): RecentNotification 10 | } 11 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/notification/NotificationRemoteDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.notification 2 | 3 | import com.yapp.android2.data.service.NotificationService 4 | import com.yapp.android2.domain.entity.Notification 5 | import com.yapp.android2.domain.entity.RecentNotification 6 | import javax.inject.Inject 7 | 8 | class NotificationRemoteDataSourceImpl @Inject constructor( 9 | private val notificationService: NotificationService 10 | ): NotificationRemoteDataSource { 11 | 12 | override suspend fun getNotification(): List { 13 | return notificationService.getNotification().data ?: emptyList() 14 | } 15 | 16 | override suspend fun getRecentCreatedNotification(): RecentNotification { 17 | return notificationService.getRecentCreatedNotification().data ?: RecentNotification.EMPTY 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/products/ProductsRemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.products 2 | 3 | import com.yapp.android2.data.remote.request.PostProductsRequest 4 | import com.yapp.android2.data.remote.request.UpdateProductsRequest 5 | import com.yapp.android2.domain.entity.Product 6 | 7 | interface ProductsRemoteDataSource { 8 | suspend fun getProductsByYmd(recordYmd: String): List 9 | suspend fun postProducts(request: PostProductsRequest) 10 | suspend fun updateProducts(request: UpdateProductsRequest) 11 | suspend fun deleteProduct(productId: Long) 12 | } 13 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/products/ProductsRemoteDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.products 2 | 3 | import com.yapp.android2.data.remote.request.PostProductsRequest 4 | import com.yapp.android2.data.remote.request.UpdateProductsRequest 5 | import com.yapp.android2.data.service.ProductsService 6 | import com.yapp.android2.domain.entity.Product 7 | import java.lang.Exception 8 | import javax.inject.Inject 9 | 10 | class ProductsRemoteDataSourceImpl @Inject constructor( 11 | private val productsService: ProductsService 12 | ) : ProductsRemoteDataSource { 13 | 14 | override suspend fun getProductsByYmd(recordYmd: String): List { 15 | return productsService.getProducts(recordYmd).data ?: emptyList() 16 | } 17 | 18 | override suspend fun postProducts(request: PostProductsRequest) { 19 | return productsService.postProduct(request) 20 | } 21 | 22 | override suspend fun updateProducts(request: UpdateProductsRequest) { 23 | return productsService.updateProduct(request) 24 | } 25 | 26 | override suspend fun deleteProduct(productId: Long) { 27 | return productsService.deleteProduct(productId.toString()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/record/RecordRemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.record 2 | 3 | import com.yapp.android2.domain.entity.Product 4 | import com.yapp.android2.domain.entity.User 5 | import com.yapp.android2.domain.entity.base.Record 6 | import com.yapp.android2.domain.entity.base.Response 7 | import com.yapp.android2.domain.entity.base.Summary 8 | 9 | interface RecordRemoteDataSource { 10 | suspend fun fetchRecords(recordMM: String): List 11 | suspend fun fetchSummaryRecord(recordMM: String): List 12 | suspend fun updateRecords(product: Product, user: User) 13 | } 14 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/record/RecordRemoteDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.record 2 | 3 | import com.yapp.android2.data.remote.request.PostSavingRecordsRequest 4 | import com.yapp.android2.data.service.RecordService 5 | import com.yapp.android2.domain.entity.Product 6 | import com.yapp.android2.domain.entity.User 7 | import com.yapp.android2.domain.entity.base.Record 8 | import com.yapp.android2.domain.entity.base.Response 9 | import com.yapp.android2.domain.entity.base.Summary 10 | import javax.inject.Inject 11 | 12 | class RecordRemoteDataSourceImpl @Inject constructor( 13 | private val recordService: RecordService 14 | ) : RecordRemoteDataSource { 15 | 16 | override suspend fun fetchRecords(recordMM: String): List { 17 | return recordService.fetchRecords(recordMM).data ?: emptyList() 18 | } 19 | 20 | override suspend fun fetchSummaryRecord(recordMM: String): List { 21 | return recordService.fetchSummaryRecords(recordMM).data ?: emptyList() 22 | } 23 | 24 | override suspend fun updateRecords(product: Product, user: User) { 25 | val request = PostSavingRecordsRequest( 26 | productId = product.productId, 27 | userId = user.userId, 28 | today = product.today.orEmpty() 29 | ) 30 | 31 | recordService.updateRecords(request) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/request/LoginForAppReviewRequest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.request 2 | 3 | data class LoginForAppReviewRequest( 4 | val email: String, 5 | val password: String 6 | ) 7 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/request/PostLoginRequest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.request 2 | 3 | data class PostLoginRequest( 4 | val email: String, 5 | val nickName: String, 6 | val provider: String, 7 | val providerId: String 8 | ) 9 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/request/PostProductsRequest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.request 2 | 3 | data class PostProductsRequest( 4 | val userId: Long, 5 | val name: String, 6 | val price: String, 7 | val resolution: String 8 | ) 9 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/request/PostSavingRecordsRequest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.request 2 | 3 | data class PostSavingRecordsRequest( 4 | val productId: Long, 5 | val userId: Long, 6 | val today: String 7 | ) 8 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/request/UpdateProductsRequest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.request 2 | 3 | data class UpdateProductsRequest( 4 | val productId: Long, 5 | val userId: Long, 6 | val name: String, 7 | val price: String, 8 | val resolution: String 9 | ) 10 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/version/AppVersionCheckDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.version 2 | 3 | import com.yapp.android2.domain.entity.Version 4 | 5 | interface AppVersionCheckDataSource { 6 | suspend fun fetchPlayStore(): Version 7 | } 8 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/version/AppVersionCheckDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.version 2 | 3 | import com.yapp.android2.domain.entity.Version 4 | import com.yapp.android2.domain.repository.version.AppVersionRepository 5 | import javax.inject.Inject 6 | 7 | class AppVersionCheckDataSourceImpl @Inject constructor( 8 | private val repository: AppVersionRepository 9 | ) : AppVersionCheckDataSource { 10 | override suspend fun fetchPlayStore(): Version { 11 | return repository.fetchPlayStore() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/withdraw/WithDrawRemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.withdraw 2 | 3 | interface WithDrawRemoteDataSource { 4 | suspend fun withDraw() 5 | } 6 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/remote/withdraw/WithDrawRemoteDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.remote.withdraw 2 | 3 | import com.yapp.android2.data.service.WithDrawService 4 | import javax.inject.Inject 5 | 6 | class WithDrawRemoteDataSourceImpl @Inject constructor( 7 | private val service: WithDrawService 8 | ): WithDrawRemoteDataSource { 9 | 10 | override suspend fun withDraw() = service.withDraw() 11 | 12 | } 13 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/repository/AppVersionRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.repository 2 | 3 | import com.yapp.android2.data.remote.version.AppVersionCheckDataSource 4 | import com.yapp.android2.domain.entity.Version 5 | import com.yapp.android2.domain.repository.version.AppVersionRepository 6 | import javax.inject.Inject 7 | 8 | class AppVersionRepositoryImpl @Inject constructor( 9 | private val appVersionCheckDataSource: AppVersionCheckDataSource 10 | ) : AppVersionRepository { 11 | override suspend fun fetchPlayStore(): Version { 12 | return appVersionCheckDataSource.fetchPlayStore() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/repository/LoginRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.repository 2 | 3 | import com.yapp.android2.data.local.login.LoginLocalDataSource 4 | import com.yapp.android2.data.remote.login.LoginRemoteDataSource 5 | import com.yapp.android2.data.remote.request.LoginForAppReviewRequest 6 | import com.yapp.android2.data.remote.request.PostLoginRequest 7 | import com.yapp.android2.domain.entity.FCMToken 8 | import com.yapp.android2.domain.entity.User 9 | import com.yapp.android2.domain.repository.login.LoginRepository 10 | import javax.inject.Inject 11 | 12 | class LoginRepositoryImpl @Inject constructor( 13 | private val loginRemoteDataSource: LoginRemoteDataSource, 14 | private val loginLocalDataSource: LoginLocalDataSource 15 | ): LoginRepository { 16 | 17 | override suspend fun postLogin(email: String, nickName: String, provider: String, providerId: String): User { 18 | val request = PostLoginRequest( 19 | email = email, 20 | nickName = nickName, 21 | provider = provider, 22 | providerId = providerId 23 | ) 24 | return loginRemoteDataSource.postLogin(request) 25 | } 26 | 27 | override suspend fun loginForAppReview(email: String, password: String) { 28 | val request = LoginForAppReviewRequest(email, password) 29 | val user = loginRemoteDataSource.loginForAppReview(request) 30 | saveUser(user) 31 | } 32 | 33 | override suspend fun postFCMToken(fcmToken: String) { 34 | val request = FCMToken( 35 | fcmToken = fcmToken 36 | ) 37 | return loginRemoteDataSource.postFCMToken(request) 38 | } 39 | 40 | override fun saveUser(user: User) { 41 | loginLocalDataSource.saveUser(user) 42 | } 43 | 44 | override fun getUser(): User { 45 | return loginLocalDataSource.getUser() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/repository/LogoutRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.repository 2 | 3 | import com.yapp.android2.data.local.logout.LogoutLocalDataSource 4 | import com.yapp.android2.data.remote.logout.LogoutRemoteDataSource 5 | import com.yapp.android2.domain.repository.logout.LogoutRepository 6 | import javax.inject.Inject 7 | 8 | class LogoutRepositoryImpl @Inject constructor( 9 | private val logoutRemoteDataSource: LogoutRemoteDataSource, 10 | private val logoutLocalDataSourceImpl: LogoutLocalDataSource 11 | ) : LogoutRepository { 12 | 13 | override suspend fun logout() = runCatching { 14 | logoutRemoteDataSource.logout() 15 | }.onSuccess { 16 | clearRefreshToken() 17 | clearAccessToken() 18 | clearUser() 19 | }.onFailure { it.printStackTrace() } 20 | 21 | private fun clearUser() { 22 | logoutLocalDataSourceImpl.clearUser() 23 | } 24 | 25 | private fun clearAccessToken() { 26 | logoutLocalDataSourceImpl.clearAccessToken() 27 | } 28 | 29 | private fun clearRefreshToken() { 30 | logoutLocalDataSourceImpl.clearRefreshToken() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/repository/NotificationRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.repository 2 | 3 | import com.yapp.android2.data.local.notification.NotificationLocalDataSource 4 | import com.yapp.android2.data.remote.notification.NotificationRemoteDataSource 5 | import com.yapp.android2.domain.entity.Notification 6 | import com.yapp.android2.domain.entity.RecentNotification 7 | import com.yapp.android2.domain.repository.Notification.NotificationRepository 8 | import javax.inject.Inject 9 | 10 | class NotificationRepositoryImpl @Inject constructor( 11 | private val notificationRemoteDataSource: NotificationRemoteDataSource, 12 | private val notificationLocalDataSource: NotificationLocalDataSource 13 | ): NotificationRepository { 14 | 15 | override suspend fun getNotification(): List { 16 | return notificationRemoteDataSource.getNotification() 17 | } 18 | 19 | override suspend fun getRecentCreatedNotification(): RecentNotification { 20 | return notificationRemoteDataSource.getRecentCreatedNotification() 21 | } 22 | 23 | override fun saveLastNotificationTime(time: String) { 24 | notificationLocalDataSource.saveLastNotificationTime(time) 25 | } 26 | 27 | override fun getLastNotificationTime(): String { 28 | return notificationLocalDataSource.getLastNotificationTime() 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/repository/RemoteConfigRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.repository 2 | 3 | import com.google.firebase.remoteconfig.FirebaseRemoteConfig 4 | import com.yapp.android2.domain.repository.RemoteConfigRepository 5 | import javax.inject.Inject 6 | 7 | class RemoteConfigRepositoryImpl @Inject constructor( 8 | private val remoteConfig: FirebaseRemoteConfig 9 | ) : RemoteConfigRepository { 10 | 11 | override fun isLoginOnlySNS(): Boolean { 12 | return remoteConfig.getBoolean(KEY_LOGIN_ONLY_SNS) 13 | } 14 | 15 | companion object { 16 | private const val KEY_LOGIN_ONLY_SNS = "login_only_sns" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/repository/SettingRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.repository 2 | 3 | import com.yapp.android2.data.local.login.LoginLocalDataSource 4 | import com.yapp.android2.domain.repository.setting.SettingRepository 5 | import javax.inject.Inject 6 | 7 | class SettingRepositoryImpl @Inject constructor( 8 | private val localDataSource: LoginLocalDataSource 9 | ) : SettingRepository { 10 | override fun getUserOrThrow(): SettingRepository.Settings.Success { 11 | val user = localDataSource.getUser() 12 | 13 | return if(user.email.isNullOrEmpty()) { 14 | throw IllegalArgumentException("email is empty") 15 | } else { 16 | SettingRepository.Settings.Success(user.email.orEmpty(), user.createAt) 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/repository/WithDrawRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.repository 2 | 3 | import com.yapp.android2.data.local.logout.LogoutLocalDataSource 4 | import com.yapp.android2.data.remote.withdraw.WithDrawRemoteDataSource 5 | import com.yapp.android2.domain.repository.withdraw.WithDrawRepository 6 | import javax.inject.Inject 7 | 8 | class WithDrawRepositoryImpl @Inject constructor( 9 | private val logoutLocalDataSource: LogoutLocalDataSource, 10 | private val withDrawRemoteDataSource: WithDrawRemoteDataSource 11 | ) : WithDrawRepository { 12 | override suspend fun withDraw() = runCatching { withDrawRemoteDataSource.withDraw() } 13 | .onSuccess { 14 | clearKeys() 15 | logoutLocalDataSource.clearUser() 16 | } 17 | .onFailure { it.printStackTrace() } 18 | 19 | private fun clearKeys() { 20 | logoutLocalDataSource.clearAccessToken() 21 | logoutLocalDataSource.clearRefreshToken() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/service/LoginService.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.service 2 | 3 | import com.yapp.android2.data.remote.request.LoginForAppReviewRequest 4 | import com.yapp.android2.data.remote.request.PostLoginRequest 5 | import com.yapp.android2.domain.entity.FCMToken 6 | import com.yapp.android2.domain.entity.User 7 | import com.yapp.android2.domain.entity.base.ApiResponse 8 | import retrofit2.http.Body 9 | import retrofit2.http.POST 10 | 11 | interface LoginService : Service { 12 | 13 | @POST("/api/oauth/sign-in") 14 | suspend fun postLogin( 15 | @Body request: PostLoginRequest 16 | ): ApiResponse 17 | 18 | @POST("/api/fcm-token") 19 | suspend fun postFCMToken( 20 | @Body request: FCMToken 21 | ) 22 | 23 | @POST("api/user/signin") 24 | suspend fun loginForAppReview( 25 | @Body request: LoginForAppReviewRequest 26 | ): ApiResponse 27 | } 28 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/service/LogoutService.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.service 2 | 3 | import retrofit2.http.GET 4 | 5 | interface LogoutService : Service { 6 | 7 | @GET("/api/user/logout") 8 | suspend fun logout() 9 | } 10 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/service/NotificationService.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.service 2 | 3 | import com.yapp.android2.domain.entity.Notification 4 | import com.yapp.android2.domain.entity.RecentNotification 5 | import com.yapp.android2.domain.entity.base.ApiResponse 6 | import retrofit2.http.GET 7 | 8 | interface NotificationService: Service { 9 | 10 | @GET("/api/alarm") 11 | suspend fun getNotification(): ApiResponse> 12 | 13 | @GET("/api/alarm/recent-created") 14 | suspend fun getRecentCreatedNotification(): ApiResponse 15 | } 16 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/service/ProductsService.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.service 2 | 3 | import com.yapp.android2.data.remote.request.PostProductsRequest 4 | import com.yapp.android2.data.remote.request.UpdateProductsRequest 5 | import com.yapp.android2.domain.entity.Product 6 | import com.yapp.android2.domain.entity.base.ApiResponse 7 | import retrofit2.http.Body 8 | import retrofit2.http.DELETE 9 | import retrofit2.http.GET 10 | import retrofit2.http.PATCH 11 | import retrofit2.http.POST 12 | import retrofit2.http.Path 13 | import retrofit2.http.Query 14 | 15 | interface ProductsService { 16 | 17 | @GET("/api/products") 18 | suspend fun getProducts( 19 | @Query("recordYmd") recordYmd: String // 기록 일자(YYYYMMDD) ex)20220601 20 | ): ApiResponse> 21 | 22 | @POST("/api/products") 23 | suspend fun postProduct( 24 | @Body request: PostProductsRequest 25 | ) 26 | 27 | @PATCH("/api/products") 28 | suspend fun updateProduct( 29 | @Body request: UpdateProductsRequest 30 | ) 31 | 32 | @DELETE("/api/products/{productId}") 33 | suspend fun deleteProduct( 34 | @Path("productId") productId: String 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/service/RecordService.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.service 2 | 3 | import com.yapp.android2.data.remote.request.PostSavingRecordsRequest 4 | import com.yapp.android2.domain.entity.base.Record 5 | import com.yapp.android2.domain.entity.base.ApiResponse 6 | import com.yapp.android2.domain.entity.base.Response 7 | import com.yapp.android2.domain.entity.base.Summary 8 | import retrofit2.http.Body 9 | import retrofit2.http.GET 10 | import retrofit2.http.POST 11 | import retrofit2.http.Query 12 | 13 | interface RecordService : Service { 14 | 15 | @GET("/api/savingRecords") 16 | suspend fun fetchRecords( 17 | @Query("recordMM") date: String 18 | ): ApiResponse> 19 | 20 | @GET("/api/savingRecords/summary") 21 | suspend fun fetchSummaryRecords( 22 | @Query("recordMM") date: String 23 | ): ApiResponse> 24 | 25 | @POST("/api/savingRecords") 26 | suspend fun updateRecords( 27 | @Body request: PostSavingRecordsRequest 28 | ): ApiResponse 29 | } 30 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/service/Service.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.service 2 | 3 | import com.yapp.android2.data.extensions.createInterceptors 4 | import okhttp3.Interceptor 5 | import retrofit2.Retrofit 6 | import retrofit2.converter.gson.GsonConverterFactory 7 | 8 | interface Service { 9 | companion object { 10 | const val BASE_URL = "https://jeol-chin.com" 11 | 12 | internal fun retroBuilder(vararg interceptor: Interceptor): Retrofit { 13 | return Retrofit.Builder() 14 | .baseUrl(BASE_URL) 15 | .client(createInterceptors(interceptor.toList())) 16 | .addConverterFactory(GsonConverterFactory.create()) 17 | .build() 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/service/WithDrawService.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.service 2 | 3 | import retrofit2.http.GET 4 | 5 | interface WithDrawService : Service { 6 | 7 | @GET("/api/user/withdrawal") 8 | suspend fun withDraw() 9 | } 10 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/service/convert/BestFriendResponseConvertFactory.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.service.convert 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.reflect.TypeToken 5 | import okhttp3.ResponseBody 6 | import retrofit2.Converter 7 | import retrofit2.Retrofit 8 | import java.lang.reflect.Type 9 | 10 | class BestFriendResponseConvertFactory private constructor( 11 | private val gson: Gson 12 | ) : Converter.Factory() { 13 | 14 | override fun responseBodyConverter( 15 | type: Type, 16 | annotations: Array, 17 | retrofit: Retrofit 18 | ): Converter { 19 | return BestFriendResponseConverter(gson, gson.getAdapter(TypeToken.get(type))) 20 | } 21 | 22 | companion object { 23 | fun create(gson: Gson = Gson()): BestFriendResponseConvertFactory { 24 | return BestFriendResponseConvertFactory(gson) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data/src/main/java/com/yapp/android2/data/service/convert/BestFriendResponseConverter.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data.service.convert 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.JsonIOException 5 | import com.google.gson.TypeAdapter 6 | import com.google.gson.stream.JsonToken 7 | import com.yapp.android2.domain.entity.base.ApiResponse 8 | import com.yapp.android2.domain.entity.base.BaseResponse 9 | import okhttp3.ResponseBody 10 | import retrofit2.Converter 11 | 12 | internal class BestFriendResponseConverter( 13 | private val gson: Gson, 14 | private val adapter: TypeAdapter<*> 15 | ) : Converter> { 16 | 17 | override fun convert(value: ResponseBody): BaseResponse<*> { 18 | value.use { 19 | val reader = gson.newJsonReader(value.charStream()) 20 | 21 | val result = adapter.read(reader) as BaseResponse<*> 22 | 23 | if (reader.peek() != JsonToken.END_DOCUMENT) { 24 | throw JsonIOException("JSON document was not fully consumed.") 25 | } 26 | 27 | if (result.statusCode in 200..299) { 28 | return result 29 | } 30 | 31 | throw Exception() 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /data/src/main/res/xml/remote_config_default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | login_only_sns 5 | false 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/src/test/java/com/yapp/android2/data/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.data 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /domain/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /domain/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.jetbrains.kotlin.jvm' 3 | id 'kotlin-kapt' 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_1_8 8 | targetCompatibility = JavaVersion.VERSION_1_8 9 | } 10 | 11 | dependencies { 12 | implementation deps.inject 13 | } 14 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/Entity.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain 2 | 3 | interface Entity 4 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/UseCase.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain 2 | 3 | interface UseCase { 4 | suspend fun execute(params: IN): Out 5 | } 6 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/FCMToken.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity 2 | 3 | data class FCMToken( 4 | val fcmToken: String 5 | ) 6 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/Login.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity 2 | 3 | data class Login(val redirectUrl: String) { 4 | companion object { 5 | val Error = Login("") 6 | } 7 | 8 | enum class Type { KAKAO } 9 | } 10 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/Notification.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity 2 | 3 | import com.yapp.android2.domain.Entity 4 | import java.io.Serializable 5 | 6 | data class Notification( 7 | val title: String?, 8 | val body: String?, 9 | val elapsedTime: String?, 10 | val createAt: String? 11 | ): Entity, Serializable { 12 | 13 | companion object { 14 | const val INIT_TIME = "0000-00-00T00:00:00" 15 | } 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/Product.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity 2 | 3 | import com.yapp.android2.domain.Entity 4 | import java.io.Serializable 5 | import java.lang.Exception 6 | import java.text.DecimalFormat 7 | 8 | data class Product( 9 | val productId: Long, 10 | val name: String?, 11 | val price: String, 12 | val resolution: String?, 13 | val today: String?, 14 | val checked: Boolean 15 | ) : Entity, Serializable { 16 | 17 | val formattedPrice: String 18 | get() = try { 19 | DecimalFormat("###,###").format(price.toInt()) 20 | } catch (e: Exception) { 21 | price 22 | } 23 | 24 | val wonPrice: String 25 | get() = try { 26 | val formatText = DecimalFormat("###,###").format(price.toInt()) 27 | String.format("%s원", formatText) 28 | } catch (e: Exception) { 29 | price 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/RecentNotification.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity 2 | 3 | import com.yapp.android2.domain.Entity 4 | import java.io.Serializable 5 | 6 | data class RecentNotification( 7 | val createAt: String? 8 | ): Entity, Serializable { 9 | 10 | companion object { 11 | 12 | val EMPTY = RecentNotification( 13 | createAt = "0000-00-00T00:00:00" 14 | ) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/RenewalResponse.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity 2 | 3 | import com.yapp.android2.domain.entity.base.BaseResponse 4 | 5 | data class RenewalResponse(override val data: Data) : BaseResponse() { 6 | data class Data( 7 | val accessToken: String? 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/User.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity 2 | 3 | import com.yapp.android2.domain.Entity 4 | import java.io.Serializable 5 | 6 | data class User( 7 | val accessToken: String, 8 | val refreshToken: String, 9 | val userId: Long, 10 | val nickName: String, 11 | val email: String, 12 | val createAt: String 13 | ) : Entity, Serializable { 14 | 15 | companion object { 16 | 17 | val EMPTY = User( 18 | accessToken = "", 19 | refreshToken = "", 20 | userId = 0, 21 | nickName = "", 22 | email = "", 23 | createAt = "" 24 | ) 25 | } 26 | 27 | enum class Type { KAKAO, GOOGLE } 28 | } 29 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/Version.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity 2 | 3 | @JvmInline 4 | value class Version(val value: String) 5 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/base/ApiResponse.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity.base 2 | 3 | data class ApiResponse( 4 | val data: T?, 5 | val message: String, 6 | val statusCode: Int 7 | ) { 8 | val isSuccess: Boolean 9 | get() { 10 | return statusCode in 200..299 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/base/BaseResponse.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity.base 2 | 3 | abstract class BaseResponse { 4 | abstract val data: T 5 | val statusCode: Int = 200 6 | val message: String = "" 7 | } 8 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/entity/base/Record.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.entity.base 2 | 3 | import com.yapp.android2.domain.Entity 4 | 5 | sealed interface Record { 6 | val name: String 7 | val recordYmd: String 8 | } 9 | 10 | data class Response( 11 | override val name: String, 12 | val price: Int, 13 | val productId: Int, 14 | override val recordYmd: String, 15 | private val resolution: String?, 16 | val timesComparedToPrev: Int 17 | ): Entity, Record { 18 | val promise: String 19 | get() = resolution.orEmpty().ifEmpty { "화이팅" } 20 | } 21 | 22 | data class Summary( 23 | val baseTimes: Int, 24 | val prevTimes: Int, 25 | val timesComparedToPrev: Int, 26 | override val name: String, 27 | override val recordYmd: String, 28 | val price: Int? 29 | ): Entity, Record 30 | 31 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/key/Key.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.key 2 | 3 | const val SHARED_PREFERENCE_KEY = "BestFriend-Shared" 4 | 5 | const val AUTHORIZED = "Authorization" 6 | 7 | const val ACCESS_TOKEN_KEY = "BestFriend-Access-Token" 8 | 9 | const val REFRESH_TOKEN_KEY = "BestFriend-Refresh-Token" 10 | 11 | const val USER = "user" 12 | 13 | const val EMAIL = "email" 14 | 15 | const val PRODUCT = "product" 16 | 17 | const val PRODUCT_RESULT = "product_result" 18 | 19 | const val POLICY_WEB_URL_KEY = "policy_web_url" 20 | 21 | const val LAST_NOTIFICATION_TIME_KEY = "BestFriend-Notification-Time" 22 | 23 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/repository/Notification/NotificationRepository.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.repository.Notification 2 | 3 | import com.yapp.android2.domain.entity.Notification 4 | import com.yapp.android2.domain.entity.RecentNotification 5 | 6 | interface NotificationRepository { 7 | suspend fun getNotification(): List 8 | suspend fun getRecentCreatedNotification(): RecentNotification 9 | fun saveLastNotificationTime(time: String) 10 | fun getLastNotificationTime(): String 11 | } 12 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/repository/ProductsRepository.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.repository 2 | 3 | import com.yapp.android2.domain.entity.Product 4 | import java.time.ZonedDateTime 5 | 6 | interface ProductsRepository { 7 | suspend fun getProductsToday(): List 8 | suspend fun getProductsByZonedDateTime(zonedDateTime: ZonedDateTime): List 9 | suspend fun getProductsByYmd(recordYmd: String): List // 기록 일자(YYYYMMDD) ex)20220601 10 | suspend fun postProducts( 11 | userId: Long, 12 | name: String, 13 | price: String 14 | ) 15 | suspend fun updateProducts( 16 | productId: Long, 17 | userId: Long, 18 | name: String, 19 | price: String 20 | ) 21 | suspend fun deleteProduct(productId: Long) 22 | } 23 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/repository/RemoteConfigRepository.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.repository 2 | 3 | interface RemoteConfigRepository { 4 | fun isLoginOnlySNS(): Boolean 5 | } 6 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/repository/Repository.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.repository 2 | 3 | interface Repository 4 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/repository/login/LoginRepository.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.repository.login 2 | 3 | import com.yapp.android2.domain.entity.User 4 | import com.yapp.android2.domain.repository.Repository 5 | 6 | interface LoginRepository : Repository { 7 | 8 | suspend fun postLogin( 9 | email: String, 10 | nickName: String, 11 | provider: String, 12 | providerId: String 13 | ): User 14 | 15 | suspend fun loginForAppReview(email: String, password: String) 16 | 17 | suspend fun postFCMToken(fcmToken: String) 18 | 19 | fun saveUser(user: User) 20 | 21 | fun getUser(): User 22 | } 23 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/repository/logout/LogoutRepository.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.repository.logout 2 | 3 | interface LogoutRepository { 4 | suspend fun logout(): Result 5 | } 6 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/repository/record/RecordRepository.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.repository.record 2 | 3 | import com.yapp.android2.domain.entity.Product 4 | import com.yapp.android2.domain.entity.User 5 | import com.yapp.android2.domain.entity.base.Response 6 | import com.yapp.android2.domain.repository.Repository 7 | 8 | interface RecordRepository : Repository { 9 | suspend fun fetchRecordsOrThrow(recordMM: String): List 10 | suspend fun updateRecords(product: Product, user: User) 11 | } 12 | 13 | data class Item( 14 | val record: Response, 15 | val recordDates: List, 16 | val totalCount: Int, 17 | val timesComparedToPrev: Int 18 | ) 19 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/repository/setting/SettingRepository.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.repository.setting 2 | 3 | interface SettingRepository { 4 | 5 | sealed class Settings { 6 | object Init : Settings() 7 | data class Success(val email: String, val createAt: String?): Settings() 8 | object Error : Settings() 9 | } 10 | 11 | fun getUserOrThrow(): Settings.Success 12 | } 13 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/repository/version/AppVersionRepository.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.repository.version 2 | 3 | import com.yapp.android2.domain.entity.Version 4 | 5 | interface AppVersionRepository { 6 | suspend fun fetchPlayStore(): Version 7 | } 8 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/repository/withdraw/WithDrawRepository.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.repository.withdraw 2 | 3 | interface WithDrawRepository { 4 | suspend fun withDraw(): Result 5 | } 6 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/url/WebURL.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.url 2 | 3 | data class WebURL(val url: String) 4 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/usecase/GetUserUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.usecase 2 | 3 | import com.yapp.android2.domain.entity.User 4 | import com.yapp.android2.domain.repository.login.LoginRepository 5 | import javax.inject.Inject 6 | 7 | class GetUserUseCase @Inject constructor( 8 | private val loginRepository: LoginRepository 9 | ) { 10 | 11 | operator fun invoke(): User { 12 | return loginRepository.getUser() 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/usecase/IsUnreadNotification.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.usecase 2 | 3 | import com.yapp.android2.domain.entity.Notification 4 | import com.yapp.android2.domain.repository.Notification.NotificationRepository 5 | import java.text.SimpleDateFormat 6 | import java.util.* 7 | import javax.inject.Inject 8 | 9 | /** 읽지 않은 알림이 있는지 판단 10 | * 읽지 않은 알림이 있는 경우 true 11 | * 읽지 않은 알림이 없는 경우 false */ 12 | class IsUnreadNotification @Inject constructor( 13 | private val notificationRepository: NotificationRepository 14 | ) { 15 | suspend operator fun invoke(): Boolean { 16 | kotlin.runCatching { 17 | notificationRepository.getRecentCreatedNotification() 18 | }.onSuccess { 19 | val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", java.util.Locale.getDefault()) 20 | val remoteLastNotification = it.createAt 21 | val localLastNotification = notificationRepository.getLastNotificationTime() 22 | return when { 23 | remoteLastNotification == Notification.INIT_TIME || remoteLastNotification.isNullOrEmpty() -> { 24 | false 25 | } 26 | localLastNotification.isEmpty() -> { 27 | true 28 | } 29 | else -> { 30 | val remoteLastNotificationFormat = requireNotNull(dateFormat.parse(remoteLastNotification)) 31 | val localLastNotificationFormat = dateFormat.parse(localLastNotification) 32 | remoteLastNotificationFormat.after(localLastNotificationFormat) 33 | } 34 | } 35 | }.onFailure { throw it } 36 | 37 | return false 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/usecase/LoginUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.usecase 2 | 3 | import com.yapp.android2.domain.entity.User 4 | import com.yapp.android2.domain.repository.login.LoginRepository 5 | import javax.inject.Inject 6 | 7 | class LoginUseCase @Inject constructor( 8 | private val loginRepository: LoginRepository 9 | ) { 10 | 11 | suspend operator fun invoke( 12 | email: String, 13 | nickName: String, 14 | provider: String, 15 | providerId: String 16 | ): Result { 17 | return kotlin.runCatching { 18 | loginRepository.postLogin( 19 | email = email, 20 | nickName = nickName, 21 | provider = provider, 22 | providerId = providerId 23 | ) 24 | }.onSuccess { 25 | loginRepository.saveUser(it) 26 | }.onFailure { 27 | throw it 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/usecase/PostFCMTokenUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.usecase 2 | 3 | import com.yapp.android2.domain.repository.login.LoginRepository 4 | import javax.inject.Inject 5 | 6 | class PostFCMTokenUseCase @Inject constructor( 7 | private val loginRepository: LoginRepository 8 | ) { 9 | 10 | suspend operator fun invoke(fcmToken: String) { 11 | return loginRepository.postFCMToken(fcmToken) 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /domain/src/main/java/com/yapp/android2/domain/usecase/RecordUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.domain.usecase 2 | 3 | import com.yapp.android2.domain.UseCase 4 | import com.yapp.android2.domain.repository.record.Item 5 | import com.yapp.android2.domain.repository.record.RecordRepository 6 | import java.time.LocalDate 7 | import java.time.format.DateTimeFormatter 8 | import javax.inject.Inject 9 | 10 | class GetRecordUseCase @Inject constructor( 11 | private val recordRepository: RecordRepository 12 | ) : RecordUseCase { 13 | 14 | override suspend fun execute(params: Unit): List { 15 | val now = LocalDate.now() 16 | val reformatDate = DateTimeFormatter.ofPattern("yyyyMM").format(now) 17 | 18 | return runCatching { recordRepository.fetchRecordsOrThrow(reformatDate) } 19 | .getOrElse { emptyList() } 20 | } 21 | } 22 | 23 | interface RecordUseCase : UseCase> { 24 | @JvmInline 25 | value class Params(val date: String) 26 | } 27 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat May 14 01:20:17 KST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /key/BestFriends: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/key/BestFriends -------------------------------------------------------------------------------- /key/private_key.pepk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/key/private_key.pepk -------------------------------------------------------------------------------- /navigator/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /navigator/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdk 32 8 | 9 | defaultConfig { 10 | minSdk 23 11 | targetSdk 32 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles "consumer-rules.pro" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | kotlinOptions { 28 | jvmTarget = '1.8' 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /navigator/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /navigator/src/main/java/com/best/friends/navigator/HomeNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.navigator 2 | 3 | interface HomeNavigator : Navigator 4 | -------------------------------------------------------------------------------- /navigator/src/main/java/com/best/friends/navigator/LoginNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.navigator 2 | 3 | interface LoginNavigator: Navigator 4 | -------------------------------------------------------------------------------- /navigator/src/main/java/com/best/friends/navigator/LogoutNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.navigator 2 | 3 | interface LogoutNavigator : Navigator 4 | -------------------------------------------------------------------------------- /navigator/src/main/java/com/best/friends/navigator/Navigator.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.navigator 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | 6 | interface Navigator { 7 | fun intent(context: Context): Intent 8 | } 9 | -------------------------------------------------------------------------------- /navigator/src/main/java/com/best/friends/navigator/NotificationNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.navigator 2 | 3 | interface NotificationNavigator : Navigator 4 | -------------------------------------------------------------------------------- /navigator/src/main/java/com/best/friends/navigator/SecondsNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.navigator 2 | 3 | interface SecondsNavigator : Navigator 4 | -------------------------------------------------------------------------------- /navigator/src/main/java/com/best/friends/navigator/SettingNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.navigator 2 | 3 | interface SettingNavigator : Navigator -------------------------------------------------------------------------------- /navigator/src/main/java/com/best/friends/navigator/WebViewNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.navigator 2 | 3 | interface WebViewNavigator : Navigator 4 | -------------------------------------------------------------------------------- /navigator/src/main/java/com/best/friends/navigator/WithDrawNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.navigator 2 | 3 | interface WithDrawNavigator : Navigator 4 | -------------------------------------------------------------------------------- /presentation/drawwith/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /presentation/drawwith/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id "kotlin-kapt" 5 | id 'dagger.hilt.android.plugin' 6 | } 7 | 8 | android { 9 | hilt { 10 | enableAggregatingTask = true 11 | } 12 | } 13 | 14 | android { 15 | compileSdk 32 16 | 17 | defaultConfig { 18 | minSdk 21 19 | targetSdk 32 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | consumerProguardFiles "consumer-rules.pro" 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | buildFeatures { 39 | dataBinding = true 40 | } 41 | } 42 | 43 | dependencies { 44 | 45 | implementation(project(":core-design-system")) 46 | implementation(project(":core")) 47 | implementation(project(":presentation:login")) 48 | implementation(project(":navigator")) 49 | implementation(project(":domain")) 50 | 51 | implementation(jetpackDeps) 52 | implementation(coroutines) 53 | 54 | implementation deps.hilt.core 55 | kapt deps.hilt.compiler 56 | 57 | implementation platform(deps.firebaseBom) 58 | implementation(firebaseDeps) 59 | 60 | testImplementation(testDeps) 61 | androidTestImplementation(androidTestDeps) 62 | } 63 | -------------------------------------------------------------------------------- /presentation/drawwith/src/androidTest/java/com/yapp/android2/drawwith/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.drawwith 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.yapp.android2.drawwith.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /presentation/drawwith/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/drawwith/src/main/java/com/yapp/android2/drawwith/WithDrawActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.drawwith 2 | 3 | import android.os.Bundle 4 | import androidx.activity.viewModels 5 | import com.best.friends.core.BaseActivity 6 | import com.best.friends.core.setOnSingleClickListener 7 | import com.best.friends.core.extensions.showToast 8 | import com.best.friends.navigator.LoginNavigator 9 | import com.google.firebase.messaging.FirebaseMessaging 10 | import com.yapp.android2.drawwith.databinding.ActivityWithDrawBinding 11 | import dagger.hilt.android.AndroidEntryPoint 12 | import javax.inject.Inject 13 | 14 | @AndroidEntryPoint 15 | class WithDrawActivity : BaseActivity(R.layout.activity_with_draw) { 16 | 17 | @Inject 18 | lateinit var loginNavigator: LoginNavigator 19 | 20 | @Inject 21 | lateinit var firebaseMessaging: FirebaseMessaging 22 | 23 | private val viewModel by viewModels() 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | 28 | binding.tvWithDrawDescription.text = getString(R.string.with_draw_description, viewModel.email) 29 | 30 | binding.ivBack.setOnSingleClickListener { finish() } 31 | 32 | binding.tvWithDraw.setOnSingleClickListener { 33 | viewModel.withDraw() 34 | } 35 | 36 | viewModel.finish.observe(this) { 37 | firebaseMessaging.deleteToken().addOnCompleteListener { 38 | if(it.isSuccessful) { 39 | startActivity(loginNavigator.intent(this)) 40 | finishAffinity() 41 | } else { 42 | this.showToast("알 수 없는 에러 발생") 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /presentation/drawwith/src/main/java/com/yapp/android2/drawwith/WithDrawModule.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.drawwith 2 | 3 | import com.best.friends.navigator.WithDrawNavigator 4 | import dagger.Binds 5 | import dagger.Module 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.android.components.ActivityComponent 8 | 9 | @Module 10 | @InstallIn(ActivityComponent::class) 11 | internal abstract class WithDrawModule { 12 | 13 | @Binds 14 | abstract fun bindWithDrawNavigator(navigator: WithDrawNavigatorImpl): WithDrawNavigator 15 | } 16 | -------------------------------------------------------------------------------- /presentation/drawwith/src/main/java/com/yapp/android2/drawwith/WithDrawNavigatorImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.drawwith 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.best.friends.navigator.WithDrawNavigator 6 | import javax.inject.Inject 7 | 8 | class WithDrawNavigatorImpl @Inject constructor() : WithDrawNavigator { 9 | override fun intent(context: Context): Intent { 10 | return Intent(context, WithDrawActivity::class.java) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /presentation/drawwith/src/main/java/com/yapp/android2/drawwith/WithDrawViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.drawwith 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.SavedStateHandle 6 | import androidx.lifecycle.viewModelScope 7 | import com.best.friends.core.BaseViewModel 8 | import com.yapp.android2.domain.key.EMAIL 9 | import com.yapp.android2.domain.repository.withdraw.WithDrawRepository 10 | import dagger.hilt.android.lifecycle.HiltViewModel 11 | import kotlinx.coroutines.launch 12 | import javax.inject.Inject 13 | 14 | @HiltViewModel 15 | class WithDrawViewModel @Inject constructor( 16 | savedStateHandle: SavedStateHandle, 17 | private val withDrawRepository: WithDrawRepository 18 | ) : BaseViewModel() { 19 | 20 | val email: String? = savedStateHandle.get(EMAIL) 21 | 22 | private val _finish = MutableLiveData() 23 | val finish: LiveData 24 | get() = _finish 25 | 26 | fun withDraw() { 27 | viewModelScope.launch { 28 | val result = withDrawRepository.withDraw() 29 | 30 | if (result.isSuccess) { 31 | _finish.value = Unit 32 | } else { 33 | sendErrorMessage("알 수 없는 에러가 발생했어요") 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /presentation/drawwith/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 계정 탈퇴 3 | 탈퇴하기 4 | 정말 탈퇴하실 건가요? 5 | 그동안 %1$s 님과\n함께해서 행복했어요… 6 | 7 | -------------------------------------------------------------------------------- /presentation/drawwith/src/test/java/com/yapp/android2/drawwith/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.drawwith 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /presentation/home/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /presentation/home/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id "kotlin-kapt" 5 | id 'dagger.hilt.android.plugin' 6 | } 7 | 8 | android { 9 | hilt { 10 | enableAggregatingTask = true 11 | } 12 | } 13 | 14 | android { 15 | compileSdk 32 16 | 17 | defaultConfig { 18 | minSdk 23 19 | targetSdk 32 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | kotlinOptions { 34 | jvmTarget = '1.8' 35 | } 36 | 37 | buildFeatures { 38 | dataBinding true 39 | } 40 | } 41 | 42 | dependencies { 43 | 44 | implementation(project(":core")) 45 | implementation(project(":core-deeplink")) 46 | implementation(project(":core-design-system")) 47 | implementation(project(":navigator")) 48 | implementation(project(":domain")) 49 | implementation(project(":presentation:record")) 50 | 51 | implementation(jetpackDeps) 52 | implementation(coroutines) 53 | 54 | implementation deps.hilt.core 55 | kapt deps.hilt.compiler 56 | 57 | implementation deps.deepLink.core 58 | kapt deps.deepLink.processor 59 | 60 | implementation deps.timber 61 | 62 | // testing 63 | testImplementation(testDeps) 64 | androidTestImplementation(androidTestDeps) 65 | } 66 | -------------------------------------------------------------------------------- /presentation/home/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /presentation/home/src/main/java/com/best/friends/home/AbstractTabSelectedListener.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.home 2 | 3 | import com.google.android.material.tabs.TabLayout 4 | 5 | abstract class AbstractTabSelectedListener : TabLayout.OnTabSelectedListener { 6 | 7 | override fun onTabSelected(tab: TabLayout.Tab) { 8 | /* explicitly empty */ 9 | } 10 | 11 | override fun onTabUnselected(tab: TabLayout.Tab) { 12 | /* explicitly empty */ 13 | } 14 | 15 | override fun onTabReselected(tab: TabLayout.Tab) { 16 | /* explicitly empty */ 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /presentation/home/src/main/java/com/best/friends/home/MainDeepLinkModule.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.home 2 | 3 | import com.airbnb.deeplinkdispatch.DeepLinkModule 4 | import com.yapp.android2.deeplink.AbstractDeepLinkModule 5 | 6 | @DeepLinkModule 7 | class MainDeepLinkModule : AbstractDeepLinkModule() 8 | -------------------------------------------------------------------------------- /presentation/home/src/main/java/com/best/friends/home/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.home 2 | 3 | import com.best.friends.core.BaseViewModel 4 | import dagger.hilt.android.lifecycle.HiltViewModel 5 | import javax.inject.Inject 6 | 7 | @HiltViewModel 8 | class MainViewModel @Inject constructor() : BaseViewModel() 9 | -------------------------------------------------------------------------------- /presentation/home/src/main/java/com/best/friends/home/home/HomeModule.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.home.home 2 | 3 | import com.best.friends.navigator.HomeNavigator 4 | import dagger.Binds 5 | import dagger.Module 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.components.SingletonComponent 8 | 9 | @Module 10 | @InstallIn(SingletonComponent::class) 11 | internal abstract class HomeModule { 12 | 13 | @Binds 14 | abstract fun bindHomeNavigator(navigator: HomeNavigatorImpl): HomeNavigator 15 | } 16 | -------------------------------------------------------------------------------- /presentation/home/src/main/java/com/best/friends/home/home/HomeNavigatorImpl.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.home.home 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.best.friends.home.MainActivity 6 | import com.best.friends.navigator.HomeNavigator 7 | import javax.inject.Inject 8 | 9 | internal class HomeNavigatorImpl @Inject constructor() : HomeNavigator { 10 | 11 | override fun intent(context: Context): Intent { 12 | return Intent(context, MainActivity::class.java) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /presentation/home/src/main/java/com/best/friends/home/register/SavingItemAddViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.home.register 2 | 3 | import androidx.lifecycle.viewModelScope 4 | import com.best.friends.core.BaseViewModel 5 | import com.best.friends.core.extensions.Empty 6 | import com.yapp.android2.domain.repository.ProductsRepository 7 | import com.yapp.android2.domain.repository.login.LoginRepository 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import kotlinx.coroutines.flow.MutableSharedFlow 10 | import kotlinx.coroutines.flow.MutableStateFlow 11 | import kotlinx.coroutines.flow.SharedFlow 12 | import kotlinx.coroutines.launch 13 | import javax.inject.Inject 14 | 15 | @HiltViewModel 16 | class SavingItemAddViewModel @Inject constructor( 17 | private val productsRepository: ProductsRepository, 18 | private val loginRepository: LoginRepository 19 | ) : BaseViewModel() { 20 | 21 | val content = MutableStateFlow(String.Empty) 22 | val price = MutableStateFlow(String.Empty) 23 | 24 | private val _action = MutableSharedFlow() 25 | val action: SharedFlow 26 | get() = _action 27 | 28 | fun setContentText(text: String) { 29 | content.value = text 30 | } 31 | 32 | fun setPriceText(text: String) { 33 | price.value = text 34 | } 35 | 36 | fun addSavingItem() { 37 | viewModelScope.launch { 38 | kotlin.runCatching { 39 | val user = loginRepository.getUser() 40 | productsRepository.postProducts( 41 | userId = user.userId, 42 | name = content.value.trim(), 43 | price = price.value 44 | .replace(",", "") 45 | .replace("원", "") 46 | ) 47 | }.onSuccess { 48 | _action.emit(Action.ItemAdded) 49 | }.onFailure { throwable -> 50 | sendErrorMessage(throwable.message) 51 | } 52 | } 53 | } 54 | 55 | sealed class Action { 56 | object ItemAdded : Action() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /presentation/home/src/main/java/com/best/friends/home/update/SavingItemUpdateModule.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.home.update 2 | 3 | import androidx.lifecycle.SavedStateHandle 4 | import com.best.friends.home.update.SavingItemUpdateActivity.Companion.EXTRA_PRODUCT 5 | import com.yapp.android2.domain.entity.Product 6 | import dagger.Module 7 | import dagger.Provides 8 | import dagger.hilt.InstallIn 9 | import dagger.hilt.android.components.ViewModelComponent 10 | import dagger.hilt.android.scopes.ViewModelScoped 11 | 12 | @Module 13 | @InstallIn(ViewModelComponent::class) 14 | object SavingItemUpdateModule { 15 | 16 | @Provides 17 | @ViewModelScoped 18 | fun provideParams(handle: SavedStateHandle): SavingItemUpdateViewModel.Params { 19 | val product = handle.get(EXTRA_PRODUCT) 20 | ?: throw IllegalStateException("product must be not null") 21 | 22 | return SavingItemUpdateViewModel.Params(product) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /presentation/home/src/main/res/drawable/selector_saving_add_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /presentation/home/src/main/res/layout/layout_custom_tab.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 19 | 20 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /presentation/home/src/main/res/layout/layout_saving_add.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 18 | 19 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /presentation/home/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 뒤로가기 버튼을 한번 더 누르면 종료됩니다. 4 | 5 | 6 | 아직 절약할 항목이 없어요 7 | 절약한 항목이 없어요 8 | 소소한 지출도 모이면 꽤나 큰 금액이니까.\n평소에 술술 나가는 지출을 잡아보세요! 9 | 절약 리스트 추가 10 | 절약과 좀 더 친해지는 오늘! 11 | 총 %s 원을 아꼈어요! 12 | 13 | 14 | 절약 추가 15 | 절약 수정 16 | 절약하고 싶은 항목 17 | 예) 아메리카노, 하리보젤리, 담배 18 | 회당 지출 금액 19 | 예) 4,500원 20 | 21 | 22 | 정말 수정하시겠어요? 23 | 지금부터 수정한 내용이\n기록 화면에 적용되어 보일거예요. 24 | 정말 삭제하시겠어요? 25 | 삭제할 수 없어요. 26 | 삭제하시면 되돌릴 수 없어요.\n신중하게 생각해보시고 삭제하세요! 27 | 절약한 항목은 삭제할 수 없어요!\n삭제를 원하면 체크를 해제해주세요. 28 | 29 | 30 | -------------------------------------------------------------------------------- /presentation/home/src/test/java/com/best/friends/home/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.home 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /presentation/login/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /presentation/login/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id "kotlin-kapt" 5 | id 'dagger.hilt.android.plugin' 6 | } 7 | 8 | android { 9 | hilt { 10 | enableAggregatingTask = true 11 | } 12 | } 13 | 14 | android { 15 | compileSdk 32 16 | 17 | defaultConfig { 18 | minSdk 23 19 | targetSdk 32 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | kotlinOptions { 34 | jvmTarget = '1.8' 35 | } 36 | 37 | buildFeatures { 38 | dataBinding true 39 | } 40 | } 41 | 42 | dependencies { 43 | implementation(project(":core")) 44 | implementation(project(":core-deeplink")) 45 | implementation(project(":navigator")) 46 | implementation(project(":domain")) 47 | implementation(project(":core-design-system")) 48 | 49 | implementation(jetpackDeps) 50 | implementation(coroutines) 51 | 52 | implementation deps.hilt.core 53 | kapt deps.hilt.compiler 54 | 55 | implementation deps.deepLink.core 56 | kapt deps.deepLink.processor 57 | 58 | implementation(loginDeps) 59 | implementation deps.timber 60 | implementation platform(deps.firebaseBom) 61 | implementation(firebaseDeps) 62 | 63 | // testing 64 | testImplementation(testDeps) 65 | androidTestImplementation(androidTestDeps) 66 | } 67 | -------------------------------------------------------------------------------- /presentation/login/src/androidTest/java/com/best/friends/login/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.login 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.best.friends.login.test", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /presentation/login/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /presentation/login/src/main/java/com/best/friends/login/LoginDeepLinkModule.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.login 2 | 3 | import com.airbnb.deeplinkdispatch.DeepLinkModule 4 | import com.yapp.android2.deeplink.AbstractDeepLinkModule 5 | 6 | @DeepLinkModule 7 | class LoginDeepLinkModule : AbstractDeepLinkModule() 8 | -------------------------------------------------------------------------------- /presentation/login/src/main/java/com/best/friends/login/LoginModule.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.login 2 | 3 | import com.best.friends.navigator.LoginNavigator 4 | import dagger.Binds 5 | import dagger.Module 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.components.SingletonComponent 8 | 9 | @Module 10 | @InstallIn(SingletonComponent::class) 11 | internal abstract class LoginModule { 12 | 13 | @Binds 14 | abstract fun bindLoginNavigator(navigator: LoginNavigatorImpl): LoginNavigator 15 | } 16 | -------------------------------------------------------------------------------- /presentation/login/src/main/java/com/best/friends/login/LoginNavigatorImpl.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.login 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.best.friends.navigator.LoginNavigator 6 | import com.yapp.android2.domain.repository.RemoteConfigRepository 7 | import javax.inject.Inject 8 | 9 | class LoginNavigatorImpl @Inject constructor( 10 | private val remoteConfigRepository: RemoteConfigRepository 11 | ) : LoginNavigator { 12 | 13 | override fun intent(context: Context): Intent { 14 | return if (remoteConfigRepository.isLoginOnlySNS()) { 15 | // 구글로그인, 카카오로그인만 있는 화면 16 | Intent(context, LoginActivity::class.java) 17 | } else { 18 | // 자체 로그인 입력란이 있는 화면 19 | Intent(context, LoginForAppReviewActivity::class.java) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /presentation/login/src/main/res/drawable/bg_google_login.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /presentation/login/src/main/res/drawable/bg_kakao_login.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /presentation/login/src/main/res/drawable/ic_best_friends.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /presentation/login/src/main/res/drawable/ic_kakao.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /presentation/login/src/main/res/drawable/icon_google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/presentation/login/src/main/res/drawable/icon_google.png -------------------------------------------------------------------------------- /presentation/login/src/main/res/font/noto_sans_kr_medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YAPP-Github/20th-Android-Team-2-Android/50a8fd47e99c87d88360cb5371c08283bbb7ad2e/presentation/login/src/main/res/font/noto_sans_kr_medium.otf -------------------------------------------------------------------------------- /presentation/login/src/main/res/values/google_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 238291040120-0hc84ta4jv65nvqvdrqge02peol4bj43.apps.googleusercontent.com 4 | 5 | -------------------------------------------------------------------------------- /presentation/login/src/main/res/values/kakao_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2d86268045860b29b088be15f109a708 4 | kakao2d86268045860b29b088be15f109a708 5 | 6 | -------------------------------------------------------------------------------- /presentation/login/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 절친 4 | 소소한 지출을 막아 \n절약의 습관을 잡아 5 | 카카오 로그인 6 | 구글 로그인 7 | 8 | 이메일 9 | 비밀번호 10 | 아이디 찾기 11 | 비밀번호 찾기 12 | 회원가입 13 | 로그인 14 | 15 | 16 | -------------------------------------------------------------------------------- /presentation/login/src/test/java/com/best/friends/login/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.login 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /presentation/logout/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /presentation/logout/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id "kotlin-kapt" 5 | id 'dagger.hilt.android.plugin' 6 | } 7 | 8 | android { 9 | hilt { 10 | enableAggregatingTask = true 11 | } 12 | } 13 | 14 | android { 15 | compileSdk 32 16 | 17 | defaultConfig { 18 | minSdk 21 19 | targetSdk 32 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | consumerProguardFiles "consumer-rules.pro" 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | buildFeatures { 39 | dataBinding = true 40 | } 41 | } 42 | 43 | dependencies { 44 | implementation(project(":core-design-system")) 45 | implementation(project(":core")) 46 | implementation(project(":presentation:login")) 47 | implementation(project(":navigator")) 48 | implementation(project(":domain")) 49 | 50 | implementation(jetpackDeps) 51 | implementation(coroutines) 52 | 53 | implementation deps.hilt.core 54 | kapt deps.hilt.compiler 55 | 56 | testImplementation(testDeps) 57 | androidTestImplementation(androidTestDeps) 58 | } 59 | -------------------------------------------------------------------------------- /presentation/logout/src/androidTest/java/com/yapp/android2/logout/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.logout 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.yapp.android2.logout.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /presentation/logout/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/logout/src/main/java/com/yapp/android2/logout/LogoutActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.logout 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.activity.viewModels 6 | import com.best.friends.core.BaseActivity 7 | import com.best.friends.core.setOnSingleClickListener 8 | import com.best.friends.navigator.LoginNavigator 9 | import com.yapp.android2.logout.databinding.ActivityLogoutBinding 10 | import dagger.hilt.android.AndroidEntryPoint 11 | import javax.inject.Inject 12 | 13 | @AndroidEntryPoint 14 | class LogoutActivity : BaseActivity(R.layout.activity_logout) { 15 | 16 | private val viewModel by viewModels() 17 | 18 | @Inject 19 | lateinit var loginNavigator: LoginNavigator 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | 24 | binding.tvLogout.setOnSingleClickListener { viewModel.logout() } 25 | 26 | binding.ivBack.setOnSingleClickListener { finish() } 27 | 28 | viewModel.finish.observe(this) { 29 | startActivity(loginNavigator.intent(this).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) 30 | finishAffinity() 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /presentation/logout/src/main/java/com/yapp/android2/logout/LogoutModule.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.logout 2 | 3 | import com.best.friends.navigator.LogoutNavigator 4 | import dagger.Binds 5 | import dagger.Module 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.android.components.ActivityComponent 8 | 9 | @Module 10 | @InstallIn(ActivityComponent::class) 11 | internal abstract class LogoutModule { 12 | 13 | @Binds 14 | abstract fun bindLogoutNavigator(navi: LogoutNavigatorImpl): LogoutNavigator 15 | 16 | } 17 | -------------------------------------------------------------------------------- /presentation/logout/src/main/java/com/yapp/android2/logout/LogoutNavigatorImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.logout 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.best.friends.navigator.LogoutNavigator 6 | import javax.inject.Inject 7 | 8 | class LogoutNavigatorImpl @Inject constructor() : LogoutNavigator { 9 | 10 | 11 | override fun intent(context: Context): Intent { 12 | return Intent(context, LogoutActivity::class.java) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /presentation/logout/src/main/java/com/yapp/android2/logout/LogoutViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.logout 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.viewModelScope 6 | import com.best.friends.core.BaseViewModel 7 | import com.yapp.android2.domain.repository.logout.LogoutRepository 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import kotlinx.coroutines.launch 10 | import javax.inject.Inject 11 | 12 | @HiltViewModel 13 | class LogoutViewModel @Inject constructor( 14 | private val logoutRepository: LogoutRepository 15 | ) : BaseViewModel() { 16 | 17 | private val _finish = MutableLiveData() 18 | 19 | val finish: LiveData 20 | get() = _finish 21 | 22 | fun logout() { 23 | viewModelScope.launch { 24 | val result = logoutRepository.logout() 25 | 26 | if (result.isSuccess) { 27 | _finish.value = Unit 28 | } else { 29 | sendErrorMessage("알 수 없는 에러입니다.") 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /presentation/logout/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 로그아웃 3 | 로그아웃 하시겠어요? 4 | 절친이 그리워지면 언제든지 돌아오세요.\n기다리고 있을게요…! 5 | -------------------------------------------------------------------------------- /presentation/logout/src/test/java/com/yapp/android2/logout/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.logout 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /presentation/notification/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /presentation/notification/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id "kotlin-kapt" 5 | id 'dagger.hilt.android.plugin' 6 | } 7 | 8 | android { 9 | hilt { 10 | enableAggregatingTask = true 11 | } 12 | } 13 | 14 | android { 15 | compileSdk 32 16 | 17 | defaultConfig { 18 | minSdk 23 19 | targetSdk 32 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | consumerProguardFiles "consumer-rules.pro" 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | buildFeatures { 40 | dataBinding true 41 | } 42 | } 43 | 44 | dependencies { 45 | implementation(project(":core")) 46 | implementation(project(":navigator")) 47 | implementation(project(":domain")) 48 | implementation(project(":core-design-system")) 49 | implementation(project(":core-deeplink")) 50 | 51 | implementation(jetpackDeps) 52 | implementation(coroutines) 53 | 54 | implementation deps.hilt.core 55 | kapt deps.hilt.compiler 56 | 57 | implementation deps.deepLink.core 58 | kapt deps.deepLink.processor 59 | 60 | implementation platform(deps.firebaseBom) 61 | implementation(firebaseDeps) 62 | 63 | implementation deps.timber 64 | 65 | // testing 66 | testImplementation(testDeps) 67 | androidTestImplementation(androidTestDeps) 68 | } 69 | -------------------------------------------------------------------------------- /presentation/notification/src/androidTest/java/com/best/friends/notification/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.notification 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.best.friends.notification.test", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /presentation/notification/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /presentation/notification/src/main/java/com/best/friends/notification/NotificationActivity.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.notification 2 | 3 | import android.os.Bundle 4 | import androidx.activity.viewModels 5 | import com.best.friends.core.BaseActivity 6 | import com.best.friends.core.setOnSingleClickListener 7 | import com.best.friends.core.extensions.visibleOrGone 8 | import com.best.friends.notification.databinding.ActivityNotificationBinding 9 | import com.yapp.android2.deeplink.DeepLinkPrefixSpec 10 | import dagger.hilt.android.AndroidEntryPoint 11 | 12 | @DeepLinkPrefixSpec( 13 | value = [ 14 | "notifications" 15 | ] 16 | ) 17 | @AndroidEntryPoint 18 | class NotificationActivity : 19 | BaseActivity(R.layout.activity_notification) { 20 | 21 | private val adapter by lazy { NotificationAdapter() } 22 | private val viewModel by viewModels() 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | 27 | initAdapter() 28 | initView() 29 | observe() 30 | } 31 | 32 | private fun initAdapter() { 33 | binding.rvNotification.itemAnimator = null 34 | binding.rvNotification.adapter = adapter 35 | } 36 | 37 | private fun initView() { 38 | binding.ivBack.setOnSingleClickListener { 39 | onBackPressed() 40 | } 41 | 42 | binding.tvToolbarTitle.text = getString(R.string.toolbar_title_notification) 43 | } 44 | 45 | private fun observe() { 46 | viewModel.notificationList.observe(this) { notifications -> 47 | binding.rvNotification.visibleOrGone(notifications.isNotEmpty()) 48 | binding.tvAlarmEmpty.visibleOrGone(notifications.isEmpty()) 49 | 50 | adapter.submitList(notifications) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /presentation/notification/src/main/java/com/best/friends/notification/NotificationAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.notification 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.best.friends.notification.databinding.ItemNotificationBinding 7 | import com.yapp.android2.domain.entity.Notification 8 | 9 | class NotificationAdapter: RecyclerView.Adapter() { 10 | 11 | private val notifications = mutableListOf() 12 | 13 | fun submitList(list: List) { 14 | this.notifications.clear() 15 | notifications.addAll(list) 16 | notifyDataSetChanged() 17 | } 18 | 19 | class NotificationViewHolder( 20 | private val binding: ItemNotificationBinding 21 | ): RecyclerView.ViewHolder(binding.root) { 22 | 23 | fun onBind(data: Notification){ 24 | binding.data = data 25 | binding.executePendingBindings() 26 | } 27 | } 28 | 29 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationViewHolder { 30 | val binding = ItemNotificationBinding.inflate( 31 | LayoutInflater.from(parent.context), parent, false 32 | ) 33 | return NotificationViewHolder(binding) 34 | } 35 | 36 | override fun onBindViewHolder(holder: NotificationViewHolder, position: Int) { 37 | holder.onBind(notifications[position]) 38 | } 39 | 40 | override fun getItemCount(): Int { 41 | return notifications.size 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /presentation/notification/src/main/java/com/best/friends/notification/NotificationDeepLinkModule.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.notification 2 | 3 | import com.airbnb.deeplinkdispatch.DeepLinkModule 4 | import com.yapp.android2.deeplink.AbstractDeepLinkModule 5 | 6 | @DeepLinkModule 7 | class NotificationDeepLinkModule : AbstractDeepLinkModule() 8 | -------------------------------------------------------------------------------- /presentation/notification/src/main/java/com/best/friends/notification/NotificationModule.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.notification 2 | 3 | import com.best.friends.navigator.NotificationNavigator 4 | import dagger.Binds 5 | import dagger.Module 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.components.SingletonComponent 8 | 9 | @Module 10 | @InstallIn(SingletonComponent::class) 11 | internal abstract class NotificationModule { 12 | 13 | @Binds 14 | abstract fun bindNotificationNavigator(navigator: NotificationNavigatorImpl): NotificationNavigator 15 | } 16 | -------------------------------------------------------------------------------- /presentation/notification/src/main/java/com/best/friends/notification/NotificationNavigatorImpl.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.notification 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.best.friends.navigator.NotificationNavigator 6 | import javax.inject.Inject 7 | 8 | class NotificationNavigatorImpl @Inject constructor() : NotificationNavigator { 9 | 10 | override fun intent(context: Context): Intent { 11 | return Intent(context, NotificationActivity::class.java) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /presentation/notification/src/main/java/com/best/friends/notification/NotificationViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.notification 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.viewModelScope 6 | import com.best.friends.core.BaseViewModel 7 | import com.yapp.android2.domain.entity.Notification 8 | import com.yapp.android2.domain.repository.Notification.NotificationRepository 9 | import dagger.hilt.android.lifecycle.HiltViewModel 10 | import kotlinx.coroutines.launch 11 | import timber.log.Timber 12 | import javax.inject.Inject 13 | 14 | @HiltViewModel 15 | class NotificationViewModel @Inject constructor( 16 | private val notificationRepository: NotificationRepository 17 | ) : BaseViewModel() { 18 | 19 | private val _notificationList = MutableLiveData>() 20 | val notificationList: LiveData> = _notificationList 21 | 22 | init { 23 | getNotificationList() 24 | } 25 | 26 | private fun getNotificationList() { 27 | viewModelScope.launch { 28 | kotlin.runCatching { 29 | notificationRepository.getNotification() 30 | }.onSuccess { 31 | _notificationList.postValue(it) 32 | 33 | if(it.isNotEmpty()) { 34 | saveLastNotificationTime(it[0].createAt ?: "") 35 | } 36 | }.onFailure { throwable -> 37 | Timber.e("--- NotificationViewModel error: ${throwable.message}") 38 | sendErrorMessage(throwable.message) 39 | } 40 | } 41 | } 42 | 43 | private fun saveLastNotificationTime(time: String) { 44 | notificationRepository.saveLastNotificationTime(time) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /presentation/notification/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 알림 5 | 아직 알림이 없어요 6 | 7 | 8 | -------------------------------------------------------------------------------- /presentation/notification/src/test/java/com/best/friends/notification/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.notification 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /presentation/record/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /presentation/record/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id "kotlin-kapt" 5 | id 'dagger.hilt.android.plugin' 6 | } 7 | 8 | android { 9 | compileSdk 32 10 | 11 | defaultConfig { 12 | minSdk 26 13 | targetSdk 32 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | kotlinOptions { 28 | jvmTarget = '1.8' 29 | } 30 | buildFeatures { 31 | dataBinding true 32 | } 33 | } 34 | 35 | dependencies { 36 | 37 | implementation(project(":core")) 38 | implementation(project(":navigator")) 39 | implementation(project(":domain")) 40 | implementation(project(":core-design-system")) 41 | 42 | implementation(jetpackDeps) 43 | implementation(coroutines) 44 | 45 | implementation deps.hilt.core 46 | kapt deps.hilt.compiler 47 | 48 | implementation deps.jodaTime 49 | implementation deps.calendar 50 | 51 | testImplementation(testDeps) 52 | androidTestImplementation(androidTestDeps) 53 | } 54 | -------------------------------------------------------------------------------- /presentation/record/src/androidTest/java/com/yapp/android2/record/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.record 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.yapp.android2.record.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /presentation/record/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /presentation/record/src/main/java/com/yapp/android2/record/BindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.record 2 | 3 | import android.widget.TextView 4 | import androidx.databinding.BindingAdapter 5 | import com.kizitonwose.calendarview.CalendarView 6 | import com.yapp.android2.domain.repository.record.Item 7 | import com.yapp.android2.record.view.* 8 | import java.text.NumberFormat 9 | import java.time.LocalDateTime 10 | import java.time.Month 11 | import java.time.Year 12 | import java.util.* 13 | 14 | @BindingAdapter("app:totalSaving") 15 | fun TextView.bindTotalSaving(items: List?) { 16 | if(items != null) { 17 | val totalSaving = items.sumOf { it.record.price } 18 | 19 | text = context.getString(R.string.record_total_price, NumberFormat.getInstance(Locale.KOREAN).format(totalSaving)) 20 | } 21 | } 22 | 23 | @BindingAdapter("app:itemSavingMessage") 24 | fun TextView.bindItemSavingMessage(timesComparedToPrev: Int?) { 25 | if(timesComparedToPrev == null) { return } 26 | 27 | val now = LocalDateTime.now() 28 | 29 | val prevMonth = if(now.month == Month.JANUARY) { 30 | Month.DECEMBER.value 31 | } else { 32 | now.monthValue.minus(1) 33 | } 34 | 35 | text = context.getString(R.string.record_saving, prevMonth, timesComparedToPrev) 36 | } 37 | 38 | @BindingAdapter("app:price") 39 | fun TextView.bindingPrice(price: Int?) { 40 | if(price != null) { 41 | text = context.getString(R.string.record_price, NumberFormat.getInstance(Locale.KOREAN).format(price)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /presentation/record/src/main/java/com/yapp/android2/record/RecordViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.record 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.yapp.android2.domain.repository.record.Item 7 | import com.yapp.android2.record.databinding.ItemRecordBinding 8 | 9 | class RecordViewHolder(private val view: ItemRecordBinding) : RecyclerView.ViewHolder(view.root) { 10 | fun onBind(item: Item) { 11 | view.item = item 12 | } 13 | 14 | companion object { 15 | fun create(parent: ViewGroup, attachToParent: Boolean): RecordViewHolder { 16 | return RecordViewHolder( 17 | ItemRecordBinding.inflate( 18 | LayoutInflater.from(parent.context), 19 | parent, 20 | attachToParent 21 | ) 22 | ) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /presentation/record/src/main/java/com/yapp/android2/record/RecordViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.record 2 | 3 | import androidx.lifecycle.viewModelScope 4 | import com.best.friends.core.BaseViewModel 5 | import com.yapp.android2.domain.repository.record.Item 6 | import com.yapp.android2.domain.usecase.GetRecordUseCase 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import kotlinx.coroutines.Job 9 | import kotlinx.coroutines.flow.MutableSharedFlow 10 | import kotlinx.coroutines.flow.MutableStateFlow 11 | import kotlinx.coroutines.flow.SharedFlow 12 | import kotlinx.coroutines.flow.StateFlow 13 | import kotlinx.coroutines.flow.asSharedFlow 14 | import kotlinx.coroutines.launch 15 | import javax.inject.Inject 16 | 17 | @HiltViewModel 18 | class RecordViewModel @Inject constructor( 19 | private val getRecordUseCase: GetRecordUseCase 20 | ) : BaseViewModel() { 21 | 22 | private val _items: MutableStateFlow> = MutableStateFlow(emptyList()) 23 | val items: StateFlow> 24 | get() = _items 25 | 26 | private val _action = MutableSharedFlow() 27 | val action: SharedFlow 28 | get() = _action.asSharedFlow() 29 | 30 | /** 31 | * @param date format yyyyMM 32 | */ 33 | fun fetchRecords() { 34 | viewModelScope.launch { 35 | try { 36 | val response = getRecordUseCase.execute(Unit) 37 | 38 | _items.value = response 39 | } catch (exception: Exception) { 40 | exception.printStackTrace() 41 | } 42 | } 43 | } 44 | 45 | fun onItemClick() { 46 | viewModelScope.launch { 47 | _action.emit(Action.ItemClick) 48 | } 49 | } 50 | 51 | sealed class Action { 52 | object ItemClick : Action() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /presentation/record/src/main/java/com/yapp/android2/record/adapter/RecordAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.record.adapter 2 | 3 | import android.view.ViewGroup 4 | import androidx.recyclerview.widget.DiffUtil 5 | import androidx.recyclerview.widget.ListAdapter 6 | import com.yapp.android2.domain.repository.record.Item 7 | import com.yapp.android2.record.RecordViewHolder 8 | 9 | class RecordAdapter : ListAdapter(DIFF_UTIL) { 10 | 11 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecordViewHolder { 12 | return RecordViewHolder.create(parent, false) 13 | } 14 | 15 | override fun onBindViewHolder(holder: RecordViewHolder, position: Int) { 16 | holder.onBind(getItem(position)) 17 | } 18 | 19 | override fun getItemCount(): Int = currentList.size 20 | 21 | override fun getItemId(position: Int): Long { 22 | return getItem(position).record.productId.toLong() 23 | } 24 | 25 | 26 | companion object { 27 | private val DIFF_UTIL = object : DiffUtil.ItemCallback() { 28 | override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean { 29 | return oldItem.record.productId == newItem.record.productId 30 | } 31 | 32 | override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean { 33 | return oldItem == newItem 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /presentation/record/src/main/java/com/yapp/android2/record/view/RoundedPagerTransformer.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.record.view 2 | 3 | import android.graphics.drawable.GradientDrawable 4 | import androidx.core.content.ContextCompat 5 | import androidx.core.graphics.ColorUtils 6 | import androidx.viewpager2.widget.ViewPager2 7 | import com.best.friend.design.R 8 | import kotlin.math.abs 9 | 10 | fun ViewPager2.setOffsetTransformer(rounded: Float = 30f): ViewPager2.PageTransformer { 11 | 12 | return ViewPager2.PageTransformer { page, position -> 13 | 14 | val startColor = ContextCompat.getColor(context, R.color.color_sub) 15 | val endColor = ContextCompat.getColor(context, R.color.color_sub_25) 16 | 17 | val color = ColorUtils.blendARGB(startColor, endColor, abs(position)) 18 | 19 | val item = GradientDrawable().apply { 20 | this.setColor(color) 21 | this.cornerRadius = rounded 22 | } 23 | 24 | page.background = item 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /presentation/record/src/main/res/drawable/shape_corner15_bg_ff8717.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /presentation/record/src/main/res/drawable/shape_dot_line_bg_659dff.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /presentation/record/src/main/res/layout/calendar_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /presentation/record/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | %1$s원 4 | %1$d번 5 | 총 %1$s번의 절약 6 | %1$s원 아꼈어요! 7 | 절약한 기록이 없어요 8 | 절약을 시작하면 기록을 볼 수 있어요. 9 | 절친 기록 10 | 11 | %1$d년 %2$d월 12 | %1$d월보다 %2$d번 더 아꼈어요! 13 | 14 | -------------------------------------------------------------------------------- /presentation/record/src/test/java/com/yapp/android2/record/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.record 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /presentation/settings/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /presentation/settings/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id "kotlin-kapt" 5 | id 'dagger.hilt.android.plugin' 6 | } 7 | 8 | android { 9 | compileSdk 32 10 | 11 | defaultConfig { 12 | minSdk 21 13 | targetSdk 32 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | consumerProguardFiles "consumer-rules.pro" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_11 27 | targetCompatibility JavaVersion.VERSION_11 28 | } 29 | kotlinOptions { 30 | jvmTarget = '11' 31 | } 32 | buildFeatures { 33 | dataBinding = true 34 | } 35 | } 36 | 37 | dependencies { 38 | 39 | implementation(project(":core")) 40 | implementation(project(":navigator")) 41 | implementation(project(":domain")) 42 | implementation(project(":core-design-system")) 43 | 44 | implementation(jetpackDeps) 45 | implementation(loginDeps) 46 | 47 | implementation deps.hilt.core 48 | implementation 'androidx.appcompat:appcompat:1.4.2' 49 | implementation 'com.google.android.material:material:1.6.1' 50 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 51 | kapt deps.hilt.compiler 52 | 53 | testImplementation(testDeps) 54 | androidTestImplementation(androidTestDeps) 55 | } 56 | -------------------------------------------------------------------------------- /presentation/settings/src/androidTest/java/com/yapp/android2/settings/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.settings 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.yapp.android2.settings.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /presentation/settings/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /presentation/settings/src/main/java/com/yapp/android2/settings/SettingViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.settings 2 | 3 | import androidx.lifecycle.viewModelScope 4 | import com.best.friends.core.BaseViewModel 5 | import com.yapp.android2.domain.repository.setting.SettingRepository 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import kotlinx.coroutines.flow.SharingStarted 8 | import kotlinx.coroutines.flow.stateIn 9 | import javax.inject.Inject 10 | 11 | @HiltViewModel 12 | class SettingViewModel @Inject constructor( 13 | settingUserData: SettingsUserDataParseFactory 14 | ) : BaseViewModel() { 15 | 16 | val user = settingUserData.getUser() 17 | .stateIn( 18 | scope = viewModelScope, 19 | initialValue = SettingRepository.Settings.Init, 20 | started = SharingStarted.WhileSubscribed(5_000L) 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /presentation/settings/src/main/java/com/yapp/android2/settings/SettingsModule.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.settings 2 | 3 | import com.best.friends.navigator.SettingNavigator 4 | import dagger.Binds 5 | import dagger.Module 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.components.SingletonComponent 8 | 9 | @Module 10 | @InstallIn(SingletonComponent::class) 11 | internal abstract class SettingsModule { 12 | 13 | @Binds 14 | abstract fun bindSettingsNavigator(navi: SettingsNavigatorImpl): SettingNavigator 15 | } 16 | -------------------------------------------------------------------------------- /presentation/settings/src/main/java/com/yapp/android2/settings/SettingsNavigatorImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.settings 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.best.friends.navigator.SettingNavigator 6 | import javax.inject.Inject 7 | 8 | class SettingsNavigatorImpl @Inject constructor() : SettingNavigator { 9 | override fun intent(context: Context): Intent { 10 | return Intent(context, SettingsActivity::class.java) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /presentation/settings/src/main/java/com/yapp/android2/settings/SettingsParseFactory.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.settings 2 | 3 | import com.yapp.android2.domain.repository.setting.SettingRepository 4 | import kotlinx.coroutines.flow.Flow 5 | import kotlinx.coroutines.flow.catch 6 | import kotlinx.coroutines.flow.flow 7 | import javax.inject.Inject 8 | 9 | class SettingsUserDataParseFactory @Inject constructor( 10 | private val settingRepository: SettingRepository 11 | ) { 12 | 13 | fun getUser(): Flow = flow { 14 | val user = settingRepository.getUserOrThrow() 15 | 16 | emit(user) 17 | }.catch { SettingRepository.Settings.Error } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /presentation/settings/src/main/res/anim/activity_in_transition.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /presentation/settings/src/main/res/anim/activity_out_transition.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /presentation/settings/src/main/res/anim/activity_stay_transition.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /presentation/settings/src/main/res/drawable/selector_switch_thumb.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /presentation/settings/src/main/res/drawable/selector_switch_track.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /presentation/settings/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 설정 3 | 로그아웃 4 | 알림 받기 5 | 이용약관 6 | 버전 7 | 8 | %1$s 간편가입 9 | 10 | -------------------------------------------------------------------------------- /presentation/settings/src/test/java/com/yapp/android2/settings/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.settings 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /presentation/splash/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /presentation/splash/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id "kotlin-kapt" 5 | id 'dagger.hilt.android.plugin' 6 | } 7 | 8 | android { 9 | hilt { 10 | enableAggregatingTask = true 11 | } 12 | } 13 | 14 | android { 15 | compileSdk 32 16 | 17 | defaultConfig { 18 | minSdk 23 19 | targetSdk 32 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | kotlinOptions { 34 | jvmTarget = '1.8' 35 | } 36 | 37 | buildFeatures { 38 | dataBinding true 39 | } 40 | } 41 | 42 | dependencies { 43 | 44 | implementation(project(":core")) 45 | implementation(project(":core-design-system")) 46 | implementation(project(":navigator")) 47 | implementation(project(":domain")) 48 | 49 | implementation(jetpackDeps) 50 | implementation(coroutines) 51 | 52 | implementation deps.hilt.core 53 | kapt deps.hilt.compiler 54 | 55 | implementation deps.timber 56 | implementation deps.splashscreen 57 | 58 | // testing 59 | testImplementation(testDeps) 60 | androidTestImplementation(androidTestDeps) 61 | } 62 | -------------------------------------------------------------------------------- /presentation/splash/src/androidTest/java/com/best/friends/splash/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.splash 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.best.friends.splash.test", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /presentation/splash/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /presentation/splash/src/main/java/com/best/friends/splash/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.splash 2 | 3 | import android.os.Bundle 4 | import androidx.activity.viewModels 5 | import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen 6 | import androidx.lifecycle.lifecycleScope 7 | import com.best.friends.core.BaseActivity 8 | import com.best.friends.navigator.HomeNavigator 9 | import com.best.friends.navigator.LoginNavigator 10 | import com.best.friends.splash.databinding.ActivitySplashBinding 11 | import dagger.hilt.android.AndroidEntryPoint 12 | import kotlinx.coroutines.delay 13 | import kotlinx.coroutines.launch 14 | import javax.inject.Inject 15 | 16 | @AndroidEntryPoint 17 | class SplashActivity : BaseActivity(R.layout.activity_splash) { 18 | 19 | private val viewModel by viewModels() 20 | 21 | @Inject 22 | lateinit var loginNavigator: LoginNavigator 23 | 24 | @Inject 25 | lateinit var homeNavigator: HomeNavigator 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | applySplashScreen() 29 | super.onCreate(savedInstanceState) 30 | 31 | lifecycleScope.launch { 32 | delay(2000) 33 | 34 | if (viewModel.isAlreadyUser()) { 35 | //메인 화면으로 이동 36 | startActivity(homeNavigator.intent(this@SplashActivity)) 37 | } else { 38 | startActivity(loginNavigator.intent(this@SplashActivity)) 39 | } 40 | finish() 41 | } 42 | } 43 | 44 | private fun applySplashScreen() { 45 | val screen = installSplashScreen() 46 | screen.setKeepOnScreenCondition { true } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /presentation/splash/src/main/java/com/best/friends/splash/SplashViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.splash 2 | 3 | import com.best.friends.core.BaseViewModel 4 | import com.yapp.android2.domain.entity.User 5 | import com.yapp.android2.domain.usecase.GetUserUseCase 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import javax.inject.Inject 8 | 9 | @HiltViewModel 10 | class SplashViewModel @Inject constructor( 11 | private val getUserUseCase: GetUserUseCase 12 | ) : BaseViewModel() { 13 | 14 | fun isAlreadyUser() = getUserUseCase() != User.EMPTY 15 | 16 | } 17 | -------------------------------------------------------------------------------- /presentation/splash/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 절친 3 | 나만의 절약 친구,  4 | 5 | -------------------------------------------------------------------------------- /presentation/splash/src/test/java/com/best/friends/splash/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.best.friends.splash 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /presentation/webview/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /presentation/webview/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id "kotlin-kapt" 5 | id 'dagger.hilt.android.plugin' 6 | } 7 | 8 | android { 9 | hilt { 10 | enableAggregatingTask = true 11 | } 12 | } 13 | 14 | android { 15 | compileSdk 32 16 | 17 | defaultConfig { 18 | minSdk 23 19 | targetSdk 32 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | consumerProguardFiles "consumer-rules.pro" 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | buildFeatures { 39 | dataBinding true 40 | } 41 | } 42 | 43 | dependencies { 44 | 45 | implementation(project(":core")) 46 | implementation(project(":core-design-system")) 47 | implementation(project(":navigator")) 48 | implementation(project(":domain")) 49 | 50 | implementation(jetpackDeps) 51 | implementation(coroutines) 52 | 53 | implementation deps.hilt.core 54 | kapt deps.hilt.compiler 55 | 56 | implementation deps.timber 57 | 58 | // testing 59 | testImplementation(testDeps) 60 | androidTestImplementation(androidTestDeps) 61 | 62 | } -------------------------------------------------------------------------------- /presentation/webview/src/androidTest/java/com/yapp/android2/webview/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.webview 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.yapp.android2.webview.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /presentation/webview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /presentation/webview/src/main/java/com/yapp/android2/webview/BestFriendWebChromeClient.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.webview 2 | 3 | import android.webkit.WebChromeClient 4 | import android.webkit.WebView 5 | 6 | class BestFriendWebChromeClient private constructor( 7 | private val onChangeProgress: (Int) -> Unit 8 | ) : WebChromeClient() { 9 | 10 | override fun onProgressChanged(view: WebView?, newProgress: Int) { 11 | onChangeProgress.invoke(newProgress) 12 | 13 | super.onProgressChanged(view, newProgress) 14 | } 15 | 16 | companion object { 17 | fun getInstance(onChangeProgress: (Int) -> Unit) = BestFriendWebChromeClient(onChangeProgress) 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /presentation/webview/src/main/java/com/yapp/android2/webview/BestFriendWebViewClient.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.webview 2 | 3 | import android.graphics.Bitmap 4 | import android.webkit.CookieManager 5 | import android.webkit.WebResourceRequest 6 | import android.webkit.WebView 7 | import android.webkit.WebViewClient 8 | 9 | class BestFriendWebViewClient private constructor( 10 | private val onPageStarted: () -> Unit = {} 11 | ) : WebViewClient() { 12 | 13 | override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { 14 | super.onPageStarted(view, url, favicon) 15 | 16 | onPageStarted.invoke() 17 | } 18 | 19 | override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { 20 | val url = request?.url.toString() 21 | 22 | view?.loadUrl(url) 23 | 24 | return true 25 | 26 | } 27 | 28 | companion object { 29 | fun getInstance(onPageStarted: () -> Unit) = BestFriendWebViewClient(onPageStarted) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /presentation/webview/src/main/java/com/yapp/android2/webview/WebViewNavigatorImpl.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.webview 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.best.friends.navigator.WebViewNavigator 6 | import javax.inject.Inject 7 | 8 | class WebViewNavigatorImpl @Inject constructor() : WebViewNavigator { 9 | override fun intent(context: Context): Intent { 10 | return Intent(context, WebViewActivity::class.java) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /presentation/webview/src/main/java/com/yapp/android2/webview/WebViewNavigatorModule.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.webview 2 | 3 | import com.best.friends.navigator.WebViewNavigator 4 | import dagger.Binds 5 | import dagger.Module 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.android.components.ActivityComponent 8 | 9 | @Module 10 | @InstallIn(ActivityComponent::class) 11 | abstract class WebViewNavigatorModule { 12 | 13 | @Binds 14 | abstract fun bindWebViewActivity(navigator: WebViewNavigatorImpl) : WebViewNavigator 15 | } 16 | -------------------------------------------------------------------------------- /presentation/webview/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 이용약관 3 | 4 | -------------------------------------------------------------------------------- /presentation/webview/src/test/java/com/yapp/android2/webview/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.yapp.android2.webview 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 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 | maven { url "https://jitpack.io" } 14 | maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' } 15 | } 16 | } 17 | rootProject.name = "Android2Team" 18 | include ':app' 19 | include ':presentation' 20 | include ':domain' 21 | include ':data' 22 | include ':presentation:home' 23 | include ':core' 24 | include ':navigator' 25 | include ':presentation:login' 26 | include ':core-design-system' 27 | include ':presentation:record' 28 | include ':presentation:splash' 29 | include ':presentation:notification' 30 | include ':presentation:settings' 31 | include ':presentation:logout' 32 | include ':presentation:drawwith' 33 | include ':presentation:webview' 34 | include ':core-deeplink' 35 | --------------------------------------------------------------------------------