├── .github ├── ISSUE_TEMPLATE │ └── genti-issue-template.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── firebase_distribution_builder.yml │ └── pr_checker.yml ├── .gitignore ├── LICENSE ├── README.md ├── app ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── kr │ │ └── genti │ │ └── android │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_genti_launcher-playstore.png │ ├── java │ │ └── kr │ │ │ └── genti │ │ │ └── android │ │ │ └── MyApp.kt │ └── res │ │ ├── drawable │ │ └── ic_genti_launcher_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_genti_launcher.xml │ │ └── ic_genti_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_genti_launcher.webp │ │ ├── ic_genti_launcher_foreground.webp │ │ └── ic_genti_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_genti_launcher.webp │ │ ├── ic_genti_launcher_foreground.webp │ │ └── ic_genti_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_genti_launcher.webp │ │ ├── ic_genti_launcher_foreground.webp │ │ └── ic_genti_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_genti_launcher.webp │ │ ├── ic_genti_launcher_foreground.webp │ │ └── ic_genti_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_genti_launcher.webp │ │ ├── ic_genti_launcher_foreground.webp │ │ └── ic_genti_launcher_round.webp │ │ ├── values │ │ └── theme.xml │ │ └── xml │ │ └── file_paths.xml │ └── test │ └── java │ └── kr │ └── genti │ └── android │ └── ExampleUnitTest.kt ├── build-logic ├── convention │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── kr │ │ └── genti │ │ └── convention │ │ ├── Constants.kt │ │ ├── config │ │ ├── CommonPlugin.kt │ │ ├── ComposePlugin.kt │ │ ├── HiltPlugin.kt │ │ ├── KotlinPlugin.kt │ │ ├── TestPlugin.kt │ │ └── VersionPlugin.kt │ │ ├── extension │ │ ├── DependencyHandlerScopeExt.kt │ │ ├── ProjectExt.kt │ │ └── VersionCatalogExt.kt │ │ └── plugin │ │ ├── AndroidApplicationPlugin.kt │ │ ├── AndroidComposePlugin.kt │ │ ├── AndroidLibraryPlugin.kt │ │ └── JavaLibraryPlugin.kt ├── gradle.properties └── settings.gradle.kts ├── build.gradle.kts ├── core ├── common │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── common │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── java │ │ │ └── kr │ │ │ │ └── genti │ │ │ │ └── common │ │ │ │ ├── extension │ │ │ │ ├── ContextExt.kt │ │ │ │ ├── ModifierExt.kt │ │ │ │ ├── StringExt.kt │ │ │ │ └── UriExt.kt │ │ │ │ ├── manager │ │ │ │ ├── AmplitudeManager.kt │ │ │ │ ├── AppUpdateManager.kt │ │ │ │ ├── ImageManager.kt │ │ │ │ ├── LauncherManager.kt │ │ │ │ ├── NetworkMonitorManager.kt │ │ │ │ └── PermissionManager.kt │ │ │ │ └── util │ │ │ │ └── DoubleBackHandler.kt │ │ └── res │ │ │ └── values │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── kr │ │ └── genti │ │ └── common │ │ ├── ExampleUnitTest.kt │ │ └── PermissionManagerTest.kt ├── datastore │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── datastore │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── datastore │ │ │ ├── di │ │ │ └── SharedPrefModule.kt │ │ │ └── local │ │ │ ├── UserSharedPref.kt │ │ │ └── UserSharedPrefImpl.kt │ │ └── test │ │ └── java │ │ └── kr │ │ └── genti │ │ └── datastore │ │ └── ExampleUnitTest.kt ├── designsystem │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── designsystem │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── java │ │ │ └── kr │ │ │ │ └── genti │ │ │ │ └── designsystem │ │ │ │ ├── component │ │ │ │ ├── button │ │ │ │ │ ├── CloseButton.kt │ │ │ │ │ ├── GentiButton.kt │ │ │ │ │ ├── GentiGradationButton.kt │ │ │ │ │ ├── GentiGradationIconButton.kt │ │ │ │ │ └── GentiSelectButton.kt │ │ │ │ ├── dialog │ │ │ │ │ ├── GentiBottomSheet.kt │ │ │ │ │ ├── GentiErrorDialog.kt │ │ │ │ │ ├── GentiImageDetailDialog.kt │ │ │ │ │ ├── GentiNotiDialog.kt │ │ │ │ │ ├── GentiRatingDialog.kt │ │ │ │ │ ├── GentiTextFieldDialog.kt │ │ │ │ │ └── GentiWarningDialog.kt │ │ │ │ ├── item │ │ │ │ │ ├── GentiAsyncImage.kt │ │ │ │ │ ├── GentiAsyncUriImage.kt │ │ │ │ │ ├── GentiCheckedText.kt │ │ │ │ │ ├── GentiPageIndicator.kt │ │ │ │ │ └── GentiRatingBar.kt │ │ │ │ ├── layout │ │ │ │ │ ├── GentiLoadingScreen.kt │ │ │ │ │ ├── GentiPullToRefreshBox.kt │ │ │ │ │ ├── GentiTopBar.kt │ │ │ │ │ └── GentiTopBottomShadow.kt │ │ │ │ └── snackbar │ │ │ │ │ └── GentiSnackBar.kt │ │ │ │ ├── event │ │ │ │ └── SnackbarTrigger.kt │ │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ └── res │ │ │ ├── drawable │ │ │ ├── ic_back.xml │ │ │ ├── ic_blank.xml │ │ │ ├── ic_check.xml │ │ │ ├── ic_close.xml │ │ │ ├── ic_download.xml │ │ │ ├── ic_download_no_border.xml │ │ │ ├── ic_drag_handle.xml │ │ │ ├── ic_female.xml │ │ │ ├── ic_genti_launcher.png │ │ │ ├── ic_info.xml │ │ │ ├── ic_kakao.xml │ │ │ ├── ic_male.xml │ │ │ ├── ic_next.xml │ │ │ ├── ic_play.xml │ │ │ ├── ic_setting.xml │ │ │ ├── ic_star.xml │ │ │ ├── ic_verify.png │ │ │ ├── img_alarm.png │ │ │ ├── img_alert_circle.png │ │ │ ├── img_alert_triangle.png │ │ │ ├── img_empty_image.xml │ │ │ ├── img_glow.png │ │ │ ├── img_gradation_black_bottom.xml │ │ │ ├── img_gradation_black_top.xml │ │ │ ├── img_gradation_image_bottom.xml │ │ │ ├── img_login_bg.png │ │ │ ├── img_number_one.xml │ │ │ ├── img_number_two.xml │ │ │ ├── img_onboarding_first.png │ │ │ ├── img_onboarding_second.png │ │ │ ├── img_onboarding_third.png │ │ │ ├── img_parent_ex_1.png │ │ │ ├── img_parent_ex_2.png │ │ │ ├── img_parent_ex_3.png │ │ │ ├── img_photo_add.png │ │ │ ├── img_profile_create_active.xml │ │ │ ├── img_profile_create_inactive.xml │ │ │ ├── img_profile_making.png │ │ │ ├── img_ratio_2_3.xml │ │ │ ├── img_ratio_3_2.xml │ │ │ ├── img_selfie_one.png │ │ │ ├── img_selfie_three.png │ │ │ ├── img_selfie_two.png │ │ │ ├── img_shine.png │ │ │ ├── img_tooltip_feed.png │ │ │ ├── img_tooltip_finished.png │ │ │ ├── img_tooltip_verify.png │ │ │ ├── img_verify_bg.png │ │ │ ├── logo_genti_2d.png │ │ │ ├── mock_img_2_3.png │ │ │ └── mock_img_3_2.png │ │ │ ├── font │ │ │ ├── pretendard_bold.ttf │ │ │ ├── pretendard_light.ttf │ │ │ ├── pretendard_medium.ttf │ │ │ ├── pretendard_regular.ttf │ │ │ └── pretendard_semibold.ttf │ │ │ ├── raw │ │ │ ├── lottie_loading.json │ │ │ ├── lottie_loading_create.json │ │ │ ├── lottie_loading_image.json │ │ │ ├── lottie_splash.json │ │ │ └── lottie_waiting.json │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── kr │ │ └── genti │ │ └── designsystem │ │ └── ExampleUnitTest.kt ├── navigation │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── navigation │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── navigation │ │ │ ├── GenerateRoute.kt │ │ │ ├── MainTabRoute.kt │ │ │ ├── OnboardingRoute.kt │ │ │ ├── ResultRoute.kt │ │ │ ├── Route.kt │ │ │ └── SettingRoute.kt │ │ └── test │ │ └── java │ │ └── kr │ │ └── genti │ │ └── navigation │ │ └── ExampleUnitTest.kt └── network │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ ├── androidTest │ └── java │ │ └── kr │ │ └── genti │ │ └── network │ │ └── ExampleInstrumentedTest.kt │ ├── main │ └── java │ │ └── kr │ │ └── genti │ │ └── network │ │ ├── AuthInterceptor.kt │ │ ├── RetrofitModule.kt │ │ ├── RetrofitQualifier.kt │ │ └── StringExt.kt │ └── test │ └── java │ └── kr │ └── genti │ └── network │ └── ExampleUnitTest.kt ├── data ├── build.gradle.kts ├── consumer-rules.pro └── src │ ├── androidTest │ └── java │ │ └── kr │ │ └── genti │ │ └── data │ │ └── ExampleInstrumentedTest.kt │ ├── main │ └── java │ │ └── kr │ │ └── genti │ │ └── data │ │ ├── dataSource │ │ ├── AuthDataSource.kt │ │ ├── CreateDataSource.kt │ │ ├── FeedDataSource.kt │ │ ├── GenerateDataSource.kt │ │ └── InfoDataSource.kt │ │ ├── dataSourceImpl │ │ ├── AuthDataSourceImpl.kt │ │ ├── CreateDataSourceImpl.kt │ │ ├── FeedDataSourceImpl.kt │ │ ├── GenerateDataSourceImpl.kt │ │ └── InfoDataSourceImpl.kt │ │ ├── di │ │ ├── DataSourceModule.kt │ │ ├── RepositoryModule.kt │ │ └── ServiceModule.kt │ │ ├── dto │ │ ├── BaseResponse.kt │ │ ├── request │ │ │ ├── AuthRequestDto.kt │ │ │ ├── CreateRequestDto.kt │ │ │ ├── CreateTwoRequestDto.kt │ │ │ ├── ImageBucketRequestDto.kt │ │ │ ├── KeyRequestDto.kt │ │ │ ├── PurchaseValidRequestDto.kt │ │ │ ├── ReissueRequestDto.kt │ │ │ ├── ReportRequestDto.kt │ │ │ └── SignupRequestDto.kt │ │ └── response │ │ │ ├── AuthTokenDto.kt │ │ │ ├── FeedItemDto.kt │ │ │ ├── GenerateStatusDto.kt │ │ │ ├── ImageBucketDto.kt │ │ │ ├── ImageDto.kt │ │ │ ├── OpenchatDto.kt │ │ │ ├── PicturePagedListDto.kt │ │ │ ├── PromptExampleDto.kt │ │ │ ├── ReissueTokenDto.kt │ │ │ ├── ServerAvailableDto.kt │ │ │ └── SignUpUserDto.kt │ │ ├── repositoryImpl │ │ ├── AuthRepositoryImpl.kt │ │ ├── CreateRepositoryImpl.kt │ │ ├── FeedRepositoryImpl.kt │ │ ├── GenerateRepositoryImpl.kt │ │ ├── InfoRepositoryImpl.kt │ │ ├── UploadRepositoryImpl.kt │ │ └── UserRepositoryImpl.kt │ │ ├── service │ │ ├── AuthService.kt │ │ ├── CreateService.kt │ │ ├── FeedService.kt │ │ ├── GenerateService.kt │ │ ├── InfoService.kt │ │ └── UploadService.kt │ │ └── util │ │ └── ContentUriRequestBody.kt │ └── test │ └── java │ └── kr │ └── genti │ └── data │ └── ExampleUnitTest.kt ├── domain ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── kr │ └── genti │ └── domain │ ├── entity │ ├── request │ │ ├── AuthRequestModel.kt │ │ ├── CreateRequestModel.kt │ │ ├── CreateTwoRequestModel.kt │ │ ├── ImageBucketRequestModel.kt │ │ ├── KeyRequestModel.kt │ │ ├── PurchaseValidRequestModel.kt │ │ ├── ReissueRequestModel.kt │ │ ├── ReportRequestModel.kt │ │ └── SignupRequestModel.kt │ └── response │ │ ├── AuthTokenModel.kt │ │ ├── FeedItemModel.kt │ │ ├── GenerateStatusModel.kt │ │ ├── ImageBucketModel.kt │ │ ├── ImageFileModel.kt │ │ ├── ImageModel.kt │ │ ├── OpenchatModel.kt │ │ ├── PicturePagedListModel.kt │ │ ├── PromptExampleModel.kt │ │ ├── ReissueTokenModel.kt │ │ ├── ServerAvailableModel.kt │ │ └── SignUpUserModel.kt │ ├── enums │ ├── FileType.kt │ ├── Gender.kt │ ├── GenerateStatus.kt │ ├── PictureNumber.kt │ └── PictureRatio.kt │ ├── repository │ ├── AuthRepository.kt │ ├── CreateRepository.kt │ ├── FeedRepository.kt │ ├── GenerateRepository.kt │ ├── InfoRepository.kt │ ├── UploadRepository.kt │ └── UserRepository.kt │ └── usecase │ ├── auth │ ├── GetNewTokensFromOauthUseCase.kt │ ├── ReissueOldTokensUseCase.kt │ └── SignUpUserUseCase.kt │ ├── feed │ └── GetFeedItemListUseCase.kt │ ├── generate │ ├── CheckPurchaseValidUseCase.kt │ ├── CheckServerAvailableUseCase.kt │ ├── GetPromptExampleListUseCase.kt │ ├── GetThreeImageBucketUseCase.kt │ └── SendGenerateRequestUseCase.kt │ ├── profile │ └── GetGeneratedPictureListUseCase.kt │ ├── result │ ├── ForceGenerateInDebugUseCase.kt │ ├── GetCurrentGenerateStatusUseCase.kt │ ├── ReportUnwantedResultUseCase.kt │ ├── ResetGenerateStatusUseCase.kt │ ├── SkipGenerateRatingUseCase.kt │ └── SubmitGenerateRatingUseCase.kt │ ├── setting │ ├── DeleteUserUseCase.kt │ └── LogoutUserUseCase.kt │ ├── upload │ └── UploadImageToBucketUseCase.kt │ └── verify │ ├── CheckUserVerifiedUseCase.kt │ ├── CheckVerifyImageUploadedUseCase.kt │ └── GetVerifyImageBucketUseCase.kt ├── feature ├── feed │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── feed │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── feed │ │ │ ├── FeedIntent.kt │ │ │ ├── FeedScreen.kt │ │ │ ├── FeedSideEffect.kt │ │ │ ├── FeedState.kt │ │ │ ├── FeedViewModel.kt │ │ │ ├── component │ │ │ ├── FeedBottomTooltip.kt │ │ │ ├── FeedHeader.kt │ │ │ ├── FeedImagesListScreen.kt │ │ │ └── FeedItem.kt │ │ │ └── navigation │ │ │ └── FeedNavigation.kt │ │ └── test │ │ └── java │ │ └── kr │ │ └── genti │ │ └── feed │ │ └── FeedViewModelTest.kt ├── generate │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── generate │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── generate │ │ │ ├── GenerateIntent.kt │ │ │ ├── GenerateRoute.kt │ │ │ ├── GenerateSideEffect.kt │ │ │ ├── GenerateState.kt │ │ │ ├── GenerateViewModel.kt │ │ │ ├── ImageSixSelectScreen.kt │ │ │ ├── ImageThreeSelectScreen.kt │ │ │ ├── NumberSelectScreen.kt │ │ │ ├── PromptInputScreen.kt │ │ │ ├── RatioSelectScreen.kt │ │ │ ├── billing │ │ │ ├── BillingCallback.kt │ │ │ └── BillingManager.kt │ │ │ ├── component │ │ │ ├── GenerateProgressBar.kt │ │ │ ├── ImagePersonItem.kt │ │ │ ├── ImageThreeExample.kt │ │ │ ├── ImageThreeSelected.kt │ │ │ ├── PromptExampleItem.kt │ │ │ └── PromptExamplePager.kt │ │ │ ├── model │ │ │ ├── GenerateStage.kt │ │ │ └── GenerateType.kt │ │ │ └── navigation │ │ │ └── GenerateNavigation.kt │ │ └── test │ │ └── java │ │ └── kr │ │ └── genti │ │ └── generate │ │ └── GenerateViewModelTest.kt ├── main │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── main │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── java │ │ │ └── kr │ │ │ │ └── genti │ │ │ │ └── main │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── MainIntent.kt │ │ │ │ ├── MainScreen.kt │ │ │ │ ├── MainSideEffect.kt │ │ │ │ ├── MainState.kt │ │ │ │ ├── MainViewModel.kt │ │ │ │ ├── component │ │ │ │ ├── GenerateForceButton.kt │ │ │ │ ├── GenerateSelectDialog.kt │ │ │ │ ├── GenerateSelectItem.kt │ │ │ │ ├── MainBottomBar.kt │ │ │ │ ├── MainBottomBtn.kt │ │ │ │ └── MainNavHost.kt │ │ │ │ ├── config │ │ │ │ └── GentiMessagingService.kt │ │ │ │ └── navigation │ │ │ │ ├── MainNavigator.kt │ │ │ │ └── MainTab.kt │ │ └── res │ │ │ └── drawable │ │ │ ├── menu_create.png │ │ │ ├── menu_feed_selected.xml │ │ │ ├── menu_feed_unselected.xml │ │ │ ├── menu_profile_selected.xml │ │ │ └── menu_profile_unselected.xml │ │ └── test │ │ └── java │ │ └── kr │ │ └── genti │ │ └── main │ │ └── MainViewModelTest.kt ├── onboarding │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── onboarding │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── onboarding │ │ │ ├── component │ │ │ ├── KakaoLoginButton.kt │ │ │ ├── MovingBackgroundImage.kt │ │ │ ├── TutorialFadeInBackground.kt │ │ │ └── TutorialItem.kt │ │ │ ├── login │ │ │ ├── LoginIntent.kt │ │ │ ├── LoginScreen.kt │ │ │ ├── LoginSideEffect.kt │ │ │ ├── LoginState.kt │ │ │ └── LoginViewModel.kt │ │ │ ├── model │ │ │ └── TutorialStage.kt │ │ │ ├── navigation │ │ │ └── OnboardingNavigation.kt │ │ │ ├── signup │ │ │ ├── SignupIntent.kt │ │ │ ├── SignupScreen.kt │ │ │ ├── SignupSideEffect.kt │ │ │ ├── SignupState.kt │ │ │ └── SignupViewModel.kt │ │ │ ├── splash │ │ │ ├── SplashIntent.kt │ │ │ ├── SplashScreen.kt │ │ │ ├── SplashSideEffect.kt │ │ │ ├── SplashState.kt │ │ │ └── SplashViewModel.kt │ │ │ └── tutorial │ │ │ ├── TutorialIntent.kt │ │ │ ├── TutorialScreen.kt │ │ │ ├── TutorialSideEffect.kt │ │ │ ├── TutorialState.kt │ │ │ └── TutorialViewModel.kt │ │ └── test │ │ └── java │ │ └── kr │ │ └── genti │ │ └── onboarding │ │ └── ExampleUnitTest.kt ├── profile │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── profile │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── profile │ │ │ ├── ProfileIntent.kt │ │ │ ├── ProfileScreen.kt │ │ │ ├── ProfileSideEffect.kt │ │ │ ├── ProfileState.kt │ │ │ ├── ProfileViewModel.kt │ │ │ ├── component │ │ │ ├── ProfileEmptyScreen.kt │ │ │ ├── ProfileGenerateItem.kt │ │ │ ├── ProfileGeneratingBanner.kt │ │ │ ├── ProfileImagesGridScreen.kt │ │ │ └── ProfileTopBar.kt │ │ │ └── navigation │ │ │ └── ProfileNavigation.kt │ │ └── test │ │ └── java │ │ └── kr │ │ └── genti │ │ └── profile │ │ └── ProfileViewModelTest.kt ├── result │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── result │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ └── java │ │ │ └── kr │ │ │ └── genti │ │ │ └── result │ │ │ ├── component │ │ │ └── BackgroundBlurImage.kt │ │ │ ├── finished │ │ │ ├── FinishedIntent.kt │ │ │ ├── FinishedScreen.kt │ │ │ ├── FinishedSideEffect.kt │ │ │ ├── FinishedState.kt │ │ │ └── FinishedViewModel.kt │ │ │ ├── navigation │ │ │ └── ResultNavigation.kt │ │ │ ├── verify │ │ │ ├── VerifyAfterScreen.kt │ │ │ ├── VerifyBeforeScreen.kt │ │ │ ├── VerifyIntent.kt │ │ │ ├── VerifyRoute.kt │ │ │ ├── VerifySideEffect.kt │ │ │ ├── VerifyState.kt │ │ │ └── VerifyViewModel.kt │ │ │ └── waiting │ │ │ ├── WaitingIntent.kt │ │ │ ├── WaitingScreen.kt │ │ │ ├── WaitingSideEffect.kt │ │ │ ├── WaitingState.kt │ │ │ └── WaitingViewModel.kt │ │ └── test │ │ └── java │ │ └── kr │ │ └── genti │ │ └── result │ │ └── ExampleUnitTest.kt └── setting │ ├── build.gradle.kts │ ├── consumer-rules.pro │ └── src │ ├── androidTest │ └── java │ │ └── kr │ │ └── genti │ │ └── setting │ │ └── ExampleInstrumentedTest.kt │ ├── main │ └── java │ │ └── kr │ │ └── genti │ │ └── setting │ │ ├── SettingIntent.kt │ │ ├── SettingScreen.kt │ │ ├── SettingSideEffect.kt │ │ ├── SettingState.kt │ │ ├── SettingViewModel.kt │ │ ├── component │ │ └── SettingItem.kt │ │ └── navigation │ │ └── SettingNavigation.kt │ └── test │ └── java │ └── kr │ └── genti │ └── setting │ └── SettingViewModelTest.kt ├── gradle.properties ├── gradle ├── libs.versions.toml ├── projectDependencyGraph.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.github/ISSUE_TEMPLATE/genti-issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Genti issue template 3 | about: Genti issue template 4 | title: "[TAG] where / what" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 🚩 TO DO 11 | - [ ] task1 12 | - [ ] task2 13 | - [ ] task3 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - closed #issue number 2 | 3 | ## *⛳️ Work Description* 4 | - task1 5 | - task2 6 | - task3 7 | 8 | ## *📸 Screenshot* 9 | 10 | 11 | 12 | ## *📢 To Reviewers* 13 | - 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Genti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /app/src/androidTest/java/kr/genti/android/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.android 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("kr.genti.android", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/ic_genti_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/ic_genti_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/kr/genti/android/MyApp.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.android 2 | 3 | import android.app.Application 4 | import androidx.appcompat.app.AppCompatDelegate 5 | import com.kakao.sdk.common.KakaoSdk 6 | import dagger.hilt.android.HiltAndroidApp 7 | import kr.genti.android.BuildConfig.AMPLITUDE_KEY 8 | import kr.genti.android.BuildConfig.NATIVE_APP_KEY 9 | import kr.genti.common.manager.AmplitudeManager 10 | import kr.genti.common.manager.AppUpdateManager 11 | import kr.genti.common.manager.ImageManager 12 | import kr.genti.common.manager.PermissionManager 13 | import timber.log.Timber 14 | 15 | @HiltAndroidApp 16 | class MyApp : Application() { 17 | override fun onCreate() { 18 | super.onCreate() 19 | 20 | initTimber() 21 | initKakaoSDK() 22 | initManagers() 23 | setDayMode() 24 | } 25 | 26 | private fun initTimber() { 27 | if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) 28 | } 29 | 30 | private fun initKakaoSDK() { 31 | KakaoSdk.init(this, NATIVE_APP_KEY) 32 | } 33 | 34 | private fun initManagers() { 35 | AmplitudeManager.init(this, AMPLITUDE_KEY) 36 | AppUpdateManager.init(this) 37 | ImageManager.init(this) 38 | } 39 | 40 | private fun setDayMode() { 41 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_genti_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_genti_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_genti_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-hdpi/ic_genti_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_genti_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-hdpi/ic_genti_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_genti_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-hdpi/ic_genti_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_genti_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-mdpi/ic_genti_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_genti_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-mdpi/ic_genti_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_genti_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-mdpi/ic_genti_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_genti_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-xhdpi/ic_genti_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_genti_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-xhdpi/ic_genti_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_genti_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-xhdpi/ic_genti_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_genti_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-xxhdpi/ic_genti_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_genti_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-xxhdpi/ic_genti_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_genti_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-xxhdpi/ic_genti_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_genti_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-xxxhdpi/ic_genti_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_genti_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-xxxhdpi/ic_genti_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_genti_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/app/src/main/res/mipmap-xxxhdpi/ic_genti_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/test/java/kr/genti/android/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.android 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-logic/convention/src/main/java/kr/genti/convention/Constants.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.convention 2 | 3 | import org.gradle.api.JavaVersion 4 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 5 | 6 | object Constants { 7 | const val PACKAGE_NAME = "kr.genti.android" 8 | 9 | const val COMPILE_SDK = 35 10 | const val MIN_SDK = 28 11 | const val TARGET_SDK = 35 12 | 13 | const val VERSION_CODE = 26 14 | const val VERSION_NAME = "3.0.0" 15 | 16 | val JVM_VERSION = JvmTarget.JVM_11 17 | val JAVA_VERSION = JavaVersion.VERSION_11 18 | } 19 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/java/kr/genti/convention/config/CommonPlugin.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.convention.config 2 | 3 | import kr.genti.convention.extension.getLibrary 4 | import kr.genti.convention.extension.implementation 5 | import kr.genti.convention.extension.libs 6 | import org.gradle.api.Plugin 7 | import org.gradle.api.Project 8 | import org.gradle.kotlin.dsl.dependencies 9 | 10 | /** 11 | * 모든 Android 프로젝트에서 공통적으로 적용할 Gradle 플러그인을 정의하는 커스텀 플러그인입니다. 12 | */ 13 | class CommonPlugin : Plugin { 14 | override fun apply(target: Project) = with(target) { 15 | dependencies { 16 | implementation(libs.getLibrary("timber")) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/java/kr/genti/convention/config/ComposePlugin.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.convention.config 2 | 3 | import kr.genti.convention.extension.androidLibraryExtension 4 | import kr.genti.convention.extension.getBundle 5 | import kr.genti.convention.extension.getLibrary 6 | import kr.genti.convention.extension.getPlugin 7 | import kr.genti.convention.extension.implementation 8 | import kr.genti.convention.extension.libs 9 | import org.gradle.api.Plugin 10 | import org.gradle.api.Project 11 | import org.gradle.kotlin.dsl.dependencies 12 | 13 | /** 14 | * Jetpack Compose, Navigation 등 화면 관련 Gradle 플러그인을 정의하는 커스텀 플러그인입니다. 15 | */ 16 | class ComposePlugin : Plugin { 17 | override fun apply(target: Project) = with(target) { 18 | pluginManager.apply { 19 | apply(libs.getPlugin("kotlin-compose")) 20 | } 21 | 22 | androidLibraryExtension.apply { 23 | buildFeatures { 24 | compose = true 25 | } 26 | } 27 | 28 | dependencies { 29 | implementation(platform(libs.getLibrary("androidx-compose-bom"))) 30 | implementation(libs.getBundle("compose")) 31 | implementation(libs.getBundle("navigation")) 32 | implementation(libs.getBundle("ui")) 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /build-logic/convention/src/main/java/kr/genti/convention/config/HiltPlugin.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.convention.config 2 | 3 | import kr.genti.convention.extension.getLibrary 4 | import kr.genti.convention.extension.getPlugin 5 | import kr.genti.convention.extension.implementation 6 | import kr.genti.convention.extension.ksp 7 | import kr.genti.convention.extension.libs 8 | import org.gradle.api.Plugin 9 | import org.gradle.api.Project 10 | import org.gradle.kotlin.dsl.dependencies 11 | 12 | /** 13 | * Hilt 관련 Gradle 플러그인을 정의하는 커스텀 플러그인입니다. 14 | */ 15 | class HiltPlugin : Plugin { 16 | override fun apply(target: Project) = with(target) { 17 | pluginManager.apply { 18 | apply(libs.getPlugin("ksp")) 19 | apply(libs.getPlugin("hilt")) 20 | } 21 | 22 | dependencies { 23 | implementation(libs.getLibrary("hilt-android")) 24 | ksp(libs.getLibrary("hilt-android-compiler")) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/java/kr/genti/convention/config/KotlinPlugin.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.convention.config 2 | 3 | import kr.genti.convention.Constants 4 | import kr.genti.convention.extension.applyKotlinCompilerOptions 5 | import kr.genti.convention.extension.getBundle 6 | import kr.genti.convention.extension.getPlugin 7 | import kr.genti.convention.extension.implementation 8 | import kr.genti.convention.extension.libs 9 | import org.gradle.api.Plugin 10 | import org.gradle.api.Project 11 | import org.gradle.kotlin.dsl.dependencies 12 | 13 | /** 14 | * Kotlin & Coroutines 관련 Gradle 플러그인을 정의하는 커스텀 플러그인입니다. 15 | */ 16 | class KotlinPlugin : Plugin { 17 | override fun apply(target: Project) = with(target) { 18 | pluginManager.apply { 19 | apply(libs.getPlugin("kotlin-android")) 20 | apply(libs.getPlugin("kotlin-parcelize")) 21 | apply(libs.getPlugin("kotlin-serialization")) 22 | } 23 | 24 | applyKotlinCompilerOptions(Constants.JVM_VERSION) 25 | 26 | dependencies { 27 | implementation(libs.getBundle("kotlinx")) 28 | implementation(libs.getBundle("coroutines")) 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /build-logic/convention/src/main/java/kr/genti/convention/config/TestPlugin.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.convention.config 2 | 3 | import kr.genti.convention.extension.androidTestImplementation 4 | import kr.genti.convention.extension.applyJUnitPlatform 5 | import kr.genti.convention.extension.getLibrary 6 | import kr.genti.convention.extension.libs 7 | import kr.genti.convention.extension.testImplementation 8 | import kr.genti.convention.extension.testRuntimeOnly 9 | import org.gradle.api.Plugin 10 | import org.gradle.api.Project 11 | import org.gradle.kotlin.dsl.dependencies 12 | 13 | /** 14 | * Test 관련 Gradle 플러그인을 정의하는 커스텀 플러그인입니다. 15 | */ 16 | class TestPlugin : Plugin { 17 | override fun apply(target: Project) = with(target) { 18 | 19 | applyJUnitPlatform() 20 | 21 | dependencies { 22 | testImplementation(platform(libs.getLibrary("junit-bom"))) 23 | testRuntimeOnly(libs.getLibrary("junit-jupiter-engine")) 24 | testImplementation(libs.getLibrary("junit-jupiter-api")) 25 | testImplementation(libs.getLibrary("junit-jupiter-params")) 26 | testImplementation(libs.getLibrary("junit-vintage-engine")) 27 | testImplementation(libs.getLibrary("junit-platform-launcher")) 28 | 29 | testImplementation(libs.getLibrary("mockk")) 30 | 31 | testImplementation(libs.getLibrary("junit")) 32 | androidTestImplementation(libs.getLibrary("androidx-junit")) 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /build-logic/convention/src/main/java/kr/genti/convention/config/VersionPlugin.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.convention.config 2 | 3 | import com.android.build.gradle.AppExtension 4 | import kr.genti.convention.Constants 5 | import org.gradle.api.Plugin 6 | import org.gradle.api.Project 7 | 8 | class VersionPlugin : Plugin { 9 | override fun apply(target: Project) = with(target) { 10 | with(extensions) { 11 | extraProperties["versionName"] = Constants.VERSION_NAME 12 | extraProperties["versionCode"] = Constants.VERSION_CODE 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /build-logic/convention/src/main/java/kr/genti/convention/extension/VersionCatalogExt.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.convention.extension 2 | 3 | import org.gradle.api.artifacts.ExternalModuleDependencyBundle 4 | import org.gradle.api.artifacts.MinimalExternalModuleDependency 5 | import org.gradle.api.artifacts.VersionCatalog 6 | import org.gradle.api.provider.Provider 7 | 8 | /** 9 | * Gradle의 Version Catalog(libs.versions.toml)를 간결하게 활용하기 위한 확장 함수 모음 10 | * 11 | * - 기존의 `findBundle(...)`, `findLibrary(...)`, `findPlugin(...)`을 더 직관적으로 사용할 수 있도록 개선합니다. 12 | * - Catalog에서 라이브러리, 번들, 플러그인을 조회할 때, 존재하지 않으면 예외를 던져 문제를 조기에 감지할 수 있습니다. 13 | */ 14 | 15 | fun VersionCatalog.getBundle(bundleName: String): Provider = 16 | findBundle(bundleName).orElseThrow { 17 | NoSuchElementException("Bundle with name $bundleName not found in the catalog") 18 | } 19 | 20 | fun VersionCatalog.getLibrary(libraryName: String): Provider = 21 | findLibrary(libraryName).orElseThrow { 22 | NoSuchElementException("Library with name $libraryName not found in the catalog") 23 | } 24 | 25 | fun VersionCatalog.getPlugin(pluginName: String): String = 26 | findPlugin(pluginName) 27 | .orElseThrow { 28 | NoSuchElementException("Plugin with name $pluginName not found in the catalog") 29 | } 30 | .get().pluginId -------------------------------------------------------------------------------- /build-logic/convention/src/main/java/kr/genti/convention/plugin/AndroidComposePlugin.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.convention.plugin 2 | 3 | import kr.genti.convention.config.ComposePlugin 4 | import org.gradle.api.Plugin 5 | import org.gradle.api.Project 6 | import org.gradle.kotlin.dsl.apply 7 | 8 | /** 9 | * Jetpack Compose 설정이 적용된 Android 라이브러리 모듈 Gradle 플러그인 10 | * 11 | * - 기존 AndroidLibraryPlugin에 ComposePlugin을 추가적으로 적용합니다. 12 | * - Jetpack Compose를 사용하는 Android 라이브러리 (ex. presentation 모듈)에 적용됩니다. 13 | */ 14 | class AndroidComposePlugin : Plugin { 15 | override fun apply(target: Project) { 16 | with(target) { 17 | pluginManager.apply { 18 | apply() 19 | apply() 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /build-logic/convention/src/main/java/kr/genti/convention/plugin/JavaLibraryPlugin.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.convention.plugin 2 | 3 | import kr.genti.convention.Constants 4 | import kr.genti.convention.extension.applyKotlinCompilerOptions 5 | import kr.genti.convention.extension.getBundle 6 | import kr.genti.convention.extension.getLibrary 7 | import kr.genti.convention.extension.getPlugin 8 | import kr.genti.convention.extension.implementation 9 | import kr.genti.convention.extension.javaLibraryExtension 10 | import kr.genti.convention.extension.libs 11 | import org.gradle.api.Plugin 12 | import org.gradle.api.Project 13 | import org.gradle.kotlin.dsl.dependencies 14 | 15 | /** 16 | * 순수 Kotlin/Java 모듈을 위한 Gradle 플러그인 17 | * 18 | * - Gradle 플러그인을 적용하여 Kotlin 및 Java의 설정을 자동으로 구성합니다. 19 | * - Android 관련 속성이 없는 순수 Kotlin/Java 라이브러리 모듈 (ex. domain 모듈)에 적용됩니다. 20 | */ 21 | class JavaLibraryPlugin : Plugin { 22 | override fun apply(target: Project) = 23 | with(target) { 24 | pluginManager.apply { 25 | apply(libs.getPlugin("java-library")) 26 | apply(libs.getPlugin("kotlin-jvm")) 27 | } 28 | 29 | javaLibraryExtension.apply { 30 | sourceCompatibility = Constants.JAVA_VERSION 31 | targetCompatibility = Constants.JAVA_VERSION 32 | } 33 | 34 | applyKotlinCompilerOptions(Constants.JVM_VERSION) 35 | 36 | dependencies { 37 | implementation(libs.getBundle("kotlinx")) 38 | implementation(libs.getLibrary("kotlinx-coroutines-core")) 39 | implementation(libs.getLibrary("javax-inject")) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /build-logic/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.parallel=true 2 | org.gradle.caching=true 3 | org.gradle.configureondemand=true -------------------------------------------------------------------------------- /build-logic/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 2 | 3 | dependencyResolutionManagement { 4 | repositories { 5 | google() 6 | mavenCentral() 7 | } 8 | versionCatalogs { 9 | create("libs") { 10 | from(files("../gradle/libs.versions.toml")) 11 | } 12 | } 13 | } 14 | 15 | rootProject.name = "build-logic" 16 | include(":convention") -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | plugins { 9 | alias(libs.plugins.android.application) apply false 10 | alias(libs.plugins.android.library) apply false 11 | alias(libs.plugins.kotlin.android) apply false 12 | alias(libs.plugins.kotlin.serialization) apply false 13 | alias(libs.plugins.kotlin.parcelize) apply false 14 | alias(libs.plugins.kotlin.compose) apply false 15 | alias(libs.plugins.kotlin.jvm) apply false 16 | alias(libs.plugins.ksp) apply false 17 | alias(libs.plugins.hilt) apply false 18 | alias(libs.plugins.google.services) apply false 19 | alias(libs.plugins.firebase.crashlytics) apply false 20 | } 21 | 22 | apply { 23 | from("gradle/projectDependencyGraph.gradle") 24 | } -------------------------------------------------------------------------------- /core/common/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidCompose") 3 | } 4 | 5 | android { 6 | namespace = "kr.genti.core.common" 7 | 8 | buildFeatures { 9 | buildConfig = true 10 | } 11 | } 12 | 13 | dependencies { 14 | implementation(libs.amplitude) 15 | implementation(libs.app.update) 16 | } 17 | -------------------------------------------------------------------------------- /core/common/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/common/consumer-rules.pro -------------------------------------------------------------------------------- /core/common/src/androidTest/java/kr/genti/common/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.common 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.march.common.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /core/common/src/main/java/kr/genti/common/extension/ContextExt.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.common.extension 2 | 3 | import android.content.Context 4 | import android.widget.Toast 5 | 6 | fun Context.toast(message: String) { 7 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show() 8 | } 9 | 10 | fun Context.longToast(message: String) { 11 | Toast.makeText(this, message, Toast.LENGTH_LONG).show() 12 | } 13 | -------------------------------------------------------------------------------- /core/common/src/main/java/kr/genti/common/extension/StringExt.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.common.extension 2 | 3 | import java.text.BreakIterator 4 | 5 | fun String.getGraphemeLength(): Int { 6 | val breakIterator: BreakIterator = BreakIterator.getCharacterInstance() 7 | 8 | breakIterator.setText(this) 9 | 10 | var count = 0 11 | while (breakIterator.next() != BreakIterator.DONE) { 12 | count++ 13 | } 14 | 15 | return count 16 | } 17 | 18 | fun String.breakLines(): String = this.replace(Regex("\\s+"), " ") -------------------------------------------------------------------------------- /core/common/src/main/java/kr/genti/common/extension/UriExt.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.common.extension 2 | 3 | import android.content.ContentResolver 4 | import android.net.Uri 5 | import android.provider.OpenableColumns 6 | 7 | fun Uri.getFileName(contentResolver: ContentResolver): String? { 8 | var result: String? = null 9 | if (this.scheme == "content") { 10 | val cursor = contentResolver.query(this, null, null, null, null) 11 | try { 12 | if (cursor != null && cursor.moveToFirst()) { 13 | val columnIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) 14 | if (columnIndex != -1) result = cursor.getString(columnIndex) 15 | } 16 | } finally { 17 | cursor?.close() 18 | } 19 | } 20 | if (result == null) { 21 | result = this.path 22 | val cut = result?.lastIndexOf('/') 23 | if (cut != -1) result = result?.substring(cut!! + 1) 24 | } 25 | return result 26 | } 27 | -------------------------------------------------------------------------------- /core/common/src/main/java/kr/genti/common/util/DoubleBackHandler.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.common.util 2 | 3 | import androidx.activity.compose.BackHandler 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.getValue 6 | import androidx.compose.runtime.mutableLongStateOf 7 | import androidx.compose.runtime.remember 8 | import androidx.compose.runtime.setValue 9 | import androidx.navigation.NavController 10 | 11 | @Composable 12 | fun DoubleBackHandler( 13 | navController: NavController, 14 | timeInterval: Long = 2_000L, 15 | onFirstBack: () -> Unit, 16 | onSecondBack: () -> Unit 17 | ) { 18 | var lastBackTime by remember { mutableLongStateOf(0L) } 19 | 20 | BackHandler { 21 | val now = System.currentTimeMillis() 22 | 23 | when { 24 | navController.previousBackStackEntry != null -> navController.popBackStack() 25 | 26 | now - lastBackTime >= timeInterval -> { 27 | lastBackTime = now 28 | onFirstBack() 29 | } 30 | 31 | else -> onSecondBack() 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /core/common/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /core/common/src/test/java/kr/genti/common/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.common 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 | } -------------------------------------------------------------------------------- /core/datastore/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidLibrary") 3 | } 4 | 5 | android { 6 | namespace = "kr.genti.core.datastore" 7 | } -------------------------------------------------------------------------------- /core/datastore/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/datastore/consumer-rules.pro -------------------------------------------------------------------------------- /core/datastore/src/androidTest/java/kr/genti/datastore/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.datastore 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("kr.genti.datastore.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /core/datastore/src/main/java/kr/genti/datastore/di/SharedPrefModule.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.datastore.di 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import dagger.Module 6 | import dagger.Provides 7 | import dagger.hilt.InstallIn 8 | import dagger.hilt.android.qualifiers.ApplicationContext 9 | import dagger.hilt.components.SingletonComponent 10 | import kr.genti.datastore.local.UserSharedPref 11 | import kr.genti.datastore.local.UserSharedPrefImpl 12 | import javax.inject.Singleton 13 | 14 | @Module 15 | @InstallIn(SingletonComponent::class) 16 | object SharedPrefModule { 17 | @Provides 18 | @Singleton 19 | fun provideSharedPreferences( 20 | @ApplicationContext context: Context, 21 | ): SharedPreferences = context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE) 22 | 23 | @Provides 24 | @Singleton 25 | fun provideSharedPref(sharedPrefImpl: UserSharedPrefImpl): UserSharedPref = sharedPrefImpl 26 | } 27 | -------------------------------------------------------------------------------- /core/datastore/src/main/java/kr/genti/datastore/local/UserSharedPref.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.datastore.local 2 | 3 | interface UserSharedPref { 4 | var accessToken: String 5 | var refreshToken: String 6 | var userRole: String 7 | 8 | fun clearInfo() 9 | } 10 | -------------------------------------------------------------------------------- /core/datastore/src/main/java/kr/genti/datastore/local/UserSharedPrefImpl.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.datastore.local 2 | 3 | import android.content.SharedPreferences 4 | import androidx.core.content.edit 5 | import javax.inject.Inject 6 | 7 | class UserSharedPrefImpl 8 | @Inject 9 | constructor( 10 | private val dataStore: SharedPreferences, 11 | ) : UserSharedPref { 12 | override var accessToken: String 13 | get() = dataStore.getString(ACCESS_TOKEN, "").orEmpty() 14 | set(value) = dataStore.edit { putString(ACCESS_TOKEN, value) } 15 | 16 | override var refreshToken: String 17 | get() = dataStore.getString(REFRESH_TOKEN, "").orEmpty() 18 | set(value) = dataStore.edit { putString(REFRESH_TOKEN, value) } 19 | 20 | override var userRole: String 21 | get() = dataStore.getString(USER_ROLE, "").orEmpty() 22 | set(value) = dataStore.edit { putString(USER_ROLE, value) } 23 | 24 | override fun clearInfo() { 25 | dataStore.edit().clear().apply() 26 | } 27 | 28 | companion object { 29 | private const val ACCESS_TOKEN = "ACCESS_TOKEN" 30 | private const val REFRESH_TOKEN = "REFRESH_TOKEN" 31 | private const val USER_ROLE = "USER_ROLE" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/datastore/src/test/java/kr/genti/datastore/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.datastore 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 | } -------------------------------------------------------------------------------- /core/designsystem/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidCompose") 3 | } 4 | 5 | android { 6 | namespace = "kr.genti.core.designsystem" 7 | } 8 | 9 | dependencies { 10 | implementation(projects.core.common) 11 | } -------------------------------------------------------------------------------- /core/designsystem/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/consumer-rules.pro -------------------------------------------------------------------------------- /core/designsystem/src/androidTest/java/kr/genti/designsystem/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.designsystem 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("kr.genti.designsystem.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /core/designsystem/src/main/java/kr/genti/designsystem/component/button/CloseButton.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.designsystem.component.button 2 | 3 | import androidx.compose.foundation.layout.BoxScope 4 | import androidx.compose.foundation.layout.padding 5 | import androidx.compose.material3.Icon 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Alignment 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | import androidx.compose.ui.res.vectorResource 11 | import androidx.compose.ui.unit.dp 12 | import kr.genti.common.extension.noRippleClickable 13 | import kr.genti.core.designsystem.R 14 | import kr.genti.designsystem.theme.White 15 | 16 | @Composable 17 | fun BoxScope.CloseButton( 18 | modifier: Modifier = Modifier, 19 | onCloseBtnClicked: () -> Unit = {} 20 | ) { 21 | Icon( 22 | imageVector = ImageVector.vectorResource(id = R.drawable.ic_close), 23 | contentDescription = null, 24 | tint = White, 25 | modifier = modifier 26 | .padding(16.dp) 27 | .padding(top = 16.dp) 28 | .align(Alignment.TopEnd) 29 | .noRippleClickable { onCloseBtnClicked() } 30 | ) 31 | } -------------------------------------------------------------------------------- /core/designsystem/src/main/java/kr/genti/designsystem/component/item/GentiRatingBar.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.designsystem.component.item 2 | 3 | import androidx.compose.foundation.layout.Row 4 | import androidx.compose.material3.Icon 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.graphics.vector.ImageVector 8 | import androidx.compose.ui.res.vectorResource 9 | import androidx.compose.ui.tooling.preview.Preview 10 | import kr.genti.common.extension.noRippleClickable 11 | import kr.genti.core.designsystem.R 12 | import kr.genti.designsystem.theme.Disabled 13 | import kr.genti.designsystem.theme.GentiGreen 14 | 15 | @Composable 16 | fun GentiRatingBar( 17 | rating: Int = 5, 18 | onRatingChanged: (Int) -> Unit = {}, 19 | ) { 20 | Row { 21 | for (index in 1..5) { 22 | Icon( 23 | imageVector = ImageVector.vectorResource(R.drawable.ic_star), 24 | contentDescription = null, 25 | tint = if (index <= rating) GentiGreen else Disabled, 26 | modifier = Modifier.noRippleClickable { onRatingChanged(index) } 27 | ) 28 | } 29 | } 30 | } 31 | 32 | @Preview 33 | @Composable 34 | private fun PreviewGentiRatingBar() { 35 | GentiRatingBar( 36 | rating = 3 37 | ) 38 | } -------------------------------------------------------------------------------- /core/designsystem/src/main/java/kr/genti/designsystem/component/layout/GentiPullToRefreshBox.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.designsystem.component.layout 2 | 3 | import androidx.compose.foundation.layout.BoxScope 4 | import androidx.compose.material3.ExperimentalMaterial3Api 5 | import androidx.compose.material3.pulltorefresh.PullToRefreshBox 6 | import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator 7 | import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.ui.Alignment 10 | import androidx.compose.ui.Modifier 11 | import kr.genti.designsystem.theme.Gray 12 | import kr.genti.designsystem.theme.White80 13 | 14 | @OptIn(ExperimentalMaterial3Api::class) 15 | @Composable 16 | fun GentiPullToRefreshBox( 17 | modifier: Modifier = Modifier, 18 | isRefreshing: Boolean = false, 19 | onRefresh: () -> Unit = {}, 20 | content: @Composable BoxScope.() -> Unit = {} 21 | ) { 22 | val refreshState = rememberPullToRefreshState() 23 | 24 | PullToRefreshBox( 25 | modifier = modifier, 26 | isRefreshing = isRefreshing, 27 | state = refreshState, 28 | onRefresh = onRefresh, 29 | indicator = { 30 | Indicator( 31 | modifier = Modifier.align(Alignment.TopCenter), 32 | isRefreshing = isRefreshing, 33 | containerColor = White80, 34 | color = Gray, 35 | state = refreshState 36 | ) 37 | } 38 | ) { 39 | content() 40 | } 41 | } -------------------------------------------------------------------------------- /core/designsystem/src/main/java/kr/genti/designsystem/event/SnackbarTrigger.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.designsystem.event 2 | 3 | import androidx.compose.runtime.staticCompositionLocalOf 4 | 5 | val LocalSnackBarTrigger = staticCompositionLocalOf<(Int) -> Unit> { 6 | error("LocalSnackBarTrigger not provided") 7 | } -------------------------------------------------------------------------------- /core/designsystem/src/main/java/kr/genti/designsystem/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.designsystem.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | // Brand Color 6 | val GentiGreen = Color(0xFF49F155) 7 | val GentiGradationStart = Color(0xFF6CEE2A) 8 | val GentiGradationEnd = Color(0xFF1CF48B) 9 | val GentiGradationEnd66 = Color(0x661CF48B) 10 | val GentiGradationDarkStart = Color(0xFF06261C) 11 | val GentiGradationDarkEnd = Color(0xFF0E2612) 12 | 13 | // System Color 14 | val Black = Color(0xFF030F0F) 15 | val Gray = Color(0xFF192222) 16 | val GrayBtn = Color(0xFF0D2D2B) 17 | val Disabled = Color(0xFF495959) 18 | 19 | // Etc. 20 | val KakaoYellow = Color(0xFFFEE500) 21 | val Red = Color(0xFFF1532F) 22 | 23 | // White 24 | val White = Color(0xFFFFFFFF) 25 | val White80 = Color(0xCCFFFFFF) 26 | val White60 = Color(0x99FFFFFF) 27 | val White40 = Color(0x66FFFFFF) 28 | val White30 = Color(0x4DFFFFFF) 29 | val White20 = Color(0x33FFFFFF) 30 | val White10 = Color(0x1AFFFFFF) 31 | 32 | // Transparent 33 | val Transparent = Color(0x00000000) 34 | val Transparent70 = Color(0xB3000000) 35 | val Transparent60 = Color(0x99000000) 36 | val Transparent50 = Color(0x80000000) 37 | val Transparent30 = Color(0x4D000000) -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_download.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_download_no_border.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_drag_handle.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_female.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_genti_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/ic_genti_launcher.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_kakao.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_male.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_next.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_play.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_star.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/ic_verify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/ic_verify.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_alarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_alarm.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_alert_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_alert_circle.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_alert_triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_alert_triangle.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_empty_image.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_glow.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_gradation_black_bottom.xml: -------------------------------------------------------------------------------- 1 | 7 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_gradation_black_top.xml: -------------------------------------------------------------------------------- 1 | 7 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_gradation_image_bottom.xml: -------------------------------------------------------------------------------- 1 | 7 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_login_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_login_bg.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_number_one.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_onboarding_first.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_onboarding_first.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_onboarding_second.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_onboarding_second.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_onboarding_third.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_onboarding_third.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_parent_ex_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_parent_ex_1.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_parent_ex_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_parent_ex_2.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_parent_ex_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_parent_ex_3.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_photo_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_photo_add.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_profile_making.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_profile_making.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_ratio_2_3.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_ratio_3_2.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_selfie_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_selfie_one.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_selfie_three.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_selfie_three.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_selfie_two.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_selfie_two.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_shine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_shine.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_tooltip_feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_tooltip_feed.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_tooltip_finished.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_tooltip_finished.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_tooltip_verify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_tooltip_verify.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/img_verify_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/img_verify_bg.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/logo_genti_2d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/logo_genti_2d.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/mock_img_2_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/mock_img_2_3.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/drawable/mock_img_3_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/drawable/mock_img_3_2.png -------------------------------------------------------------------------------- /core/designsystem/src/main/res/font/pretendard_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/font/pretendard_bold.ttf -------------------------------------------------------------------------------- /core/designsystem/src/main/res/font/pretendard_light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/font/pretendard_light.ttf -------------------------------------------------------------------------------- /core/designsystem/src/main/res/font/pretendard_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/font/pretendard_medium.ttf -------------------------------------------------------------------------------- /core/designsystem/src/main/res/font/pretendard_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/font/pretendard_regular.ttf -------------------------------------------------------------------------------- /core/designsystem/src/main/res/font/pretendard_semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/designsystem/src/main/res/font/pretendard_semibold.ttf -------------------------------------------------------------------------------- /core/designsystem/src/test/java/kr/genti/designsystem/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.designsystem 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 | } -------------------------------------------------------------------------------- /core/navigation/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidCompose") 3 | } 4 | 5 | android { 6 | namespace = "kr.genti.core.navigation" 7 | } 8 | -------------------------------------------------------------------------------- /core/navigation/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/navigation/consumer-rules.pro -------------------------------------------------------------------------------- /core/navigation/src/androidTest/java/kr/genti/navigation/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.navigation 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("kr.genti.navigation.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /core/navigation/src/main/java/kr/genti/navigation/GenerateRoute.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.navigation 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | interface GenerateRoute : Route { 6 | @Serializable 7 | data class Generate(val isParentPic: Boolean) : GenerateRoute 8 | } -------------------------------------------------------------------------------- /core/navigation/src/main/java/kr/genti/navigation/MainTabRoute.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.navigation 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | interface MainTabRoute : Route { 6 | @Serializable 7 | data object Feed : MainTabRoute 8 | 9 | @Serializable 10 | data object Generate : MainTabRoute 11 | 12 | @Serializable 13 | data object Profile : MainTabRoute 14 | } -------------------------------------------------------------------------------- /core/navigation/src/main/java/kr/genti/navigation/OnboardingRoute.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.navigation 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | interface OnboardingRoute : Route { 6 | @Serializable 7 | data object Splash : OnboardingRoute 8 | 9 | @Serializable 10 | data object Login : OnboardingRoute 11 | 12 | @Serializable 13 | data object Signup : OnboardingRoute 14 | 15 | @Serializable 16 | data object Tutorial : OnboardingRoute 17 | } 18 | -------------------------------------------------------------------------------- /core/navigation/src/main/java/kr/genti/navigation/ResultRoute.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.navigation 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | interface ResultRoute : Route { 6 | @Serializable 7 | data object Verify : ResultRoute 8 | 9 | @Serializable 10 | data class Waiting( 11 | val isParentPic: Boolean 12 | ) : ResultRoute 13 | 14 | @Serializable 15 | data class Finished( 16 | val responseId: Long, 17 | val imageUrl: String, 18 | val isGaro: Boolean, 19 | val isParentPic: Boolean 20 | ) : ResultRoute 21 | } -------------------------------------------------------------------------------- /core/navigation/src/main/java/kr/genti/navigation/Route.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.navigation 2 | 3 | interface Route -------------------------------------------------------------------------------- /core/navigation/src/main/java/kr/genti/navigation/SettingRoute.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.navigation 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | interface SettingRoute : Route { 6 | @Serializable 7 | data object Setting : SettingRoute 8 | } -------------------------------------------------------------------------------- /core/navigation/src/test/java/kr/genti/navigation/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.navigation 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 | } -------------------------------------------------------------------------------- /core/network/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import kr.genti.convention.extension.implementation 2 | import java.util.Properties 3 | 4 | plugins { 5 | id("kr.genti.androidLibrary") 6 | } 7 | 8 | android { 9 | namespace = "kr.genti.core.network" 10 | 11 | val properties = Properties().apply { 12 | load(rootProject.file("local.properties").inputStream()) 13 | } 14 | 15 | buildTypes { 16 | debug { 17 | buildConfigField("String", "BASE_URL", properties["test.base.url"].toString()) 18 | } 19 | release { 20 | buildConfigField("String", "BASE_URL", properties["base.url"].toString()) 21 | } 22 | } 23 | 24 | buildFeatures { 25 | buildConfig = true 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation(projects.domain) 31 | 32 | implementation(platform(libs.okhttp.bom)) 33 | implementation(libs.bundles.okhttp) 34 | implementation(platform(libs.retrofit.bom)) 35 | implementation(libs.bundles.retrofit) 36 | implementation(libs.phoenix) 37 | } -------------------------------------------------------------------------------- /core/network/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/core/network/consumer-rules.pro -------------------------------------------------------------------------------- /core/network/src/androidTest/java/kr/genti/network/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.network 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("kr.genti.network.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /core/network/src/main/java/kr/genti/network/RetrofitQualifier.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.network 2 | 3 | import javax.inject.Qualifier 4 | 5 | object RetrofitQualifier { 6 | @Qualifier 7 | @Retention(AnnotationRetention.BINARY) 8 | annotation class JWT 9 | 10 | @Qualifier 11 | @Retention(AnnotationRetention.BINARY) 12 | annotation class NOTOKEN 13 | } 14 | -------------------------------------------------------------------------------- /core/network/src/main/java/kr/genti/network/StringExt.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.network 2 | 3 | fun String?.isJsonObject(): Boolean = this?.startsWith("{") == true && this.endsWith("}") 4 | 5 | fun String?.isJsonArray(): Boolean = this?.startsWith("[") == true && this.endsWith("]") -------------------------------------------------------------------------------- /core/network/src/test/java/kr/genti/network/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.network 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 | } -------------------------------------------------------------------------------- /data/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import kr.genti.convention.extension.implementation 2 | 3 | plugins { 4 | id("kr.genti.androidLibrary") 5 | } 6 | 7 | android { 8 | namespace = "kr.genti.data" 9 | } 10 | 11 | dependencies { 12 | implementation(projects.domain) 13 | implementation(projects.core.network) 14 | implementation(projects.core.datastore) 15 | 16 | implementation(platform(libs.okhttp.bom)) 17 | implementation(libs.bundles.okhttp) 18 | implementation(platform(libs.retrofit.bom)) 19 | implementation(libs.bundles.retrofit) 20 | } 21 | -------------------------------------------------------------------------------- /data/consumer-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 -------------------------------------------------------------------------------- /data/src/androidTest/java/kr/genti/data/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data 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("kr.genti.data", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dataSource/AuthDataSource.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dataSource 2 | 3 | import kr.genti.data.dto.BaseResponse 4 | import kr.genti.data.dto.request.AuthRequestDto 5 | import kr.genti.data.dto.request.ReissueRequestDto 6 | import kr.genti.data.dto.response.AuthTokenDto 7 | import kr.genti.data.dto.response.ReissueTokenDto 8 | 9 | interface AuthDataSource { 10 | suspend fun postReissueTokens(request: ReissueRequestDto): BaseResponse 11 | 12 | suspend fun postOauthDataToGetToken(request: AuthRequestDto): BaseResponse 13 | } 14 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dataSource/CreateDataSource.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dataSource 2 | 3 | import kr.genti.data.dto.BaseResponse 4 | import kr.genti.data.dto.request.CreateRequestDto 5 | import kr.genti.data.dto.request.CreateTwoRequestDto 6 | import kr.genti.data.dto.request.KeyRequestDto 7 | import kr.genti.data.dto.request.PurchaseValidRequestDto 8 | import kr.genti.data.dto.request.ImageBucketRequestDto 9 | import kr.genti.data.dto.response.PromptExampleDto 10 | import kr.genti.data.dto.response.ImageBucketDto 11 | 12 | interface CreateDataSource { 13 | suspend fun getSingleImageBucket(request: ImageBucketRequestDto): BaseResponse 14 | 15 | suspend fun getThreeImageBucket(request: List): BaseResponse> 16 | 17 | suspend fun postToCreate(request: CreateRequestDto): BaseResponse 18 | 19 | suspend fun postToCreateOne(request: CreateRequestDto): BaseResponse 20 | 21 | suspend fun postToCreateTwo(request: CreateTwoRequestDto): BaseResponse 22 | 23 | suspend fun postToVerify(request: KeyRequestDto): BaseResponse 24 | 25 | suspend fun getPromptExample(type: String): BaseResponse> 26 | 27 | suspend fun postToValidatePurchase(request: PurchaseValidRequestDto): BaseResponse 28 | } 29 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dataSource/FeedDataSource.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dataSource 2 | 3 | import kr.genti.data.dto.BaseResponse 4 | import kr.genti.data.dto.response.FeedItemDto 5 | 6 | interface FeedDataSource { 7 | suspend fun getExampleItems(): BaseResponse> 8 | } 9 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dataSource/GenerateDataSource.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dataSource 2 | 3 | import kr.genti.data.dto.BaseResponse 4 | import kr.genti.data.dto.request.ReportRequestDto 5 | import kr.genti.data.dto.response.GenerateStatusDto 6 | import kr.genti.data.dto.response.OpenchatDto 7 | import kr.genti.data.dto.response.PicturePagedListDto 8 | import kr.genti.data.dto.response.ServerAvailableDto 9 | 10 | interface GenerateDataSource { 11 | suspend fun getGenerateStatus(): BaseResponse 12 | 13 | suspend fun getGeneratedPictureList( 14 | page: Int, 15 | size: Int, 16 | ): BaseResponse 17 | 18 | suspend fun postGenerateReport(request: ReportRequestDto): BaseResponse 19 | 20 | suspend fun postGenerateRate( 21 | responseId: Int, 22 | star: Int, 23 | ): BaseResponse 24 | 25 | suspend fun postVerifyGenerateState(responseId: Int): BaseResponse 26 | 27 | suspend fun getCanceledToReset(requestId: String): BaseResponse 28 | 29 | suspend fun getOpenchatData(): BaseResponse 30 | 31 | suspend fun getIsUserVerified(): BaseResponse 32 | 33 | suspend fun getIsServerAvailable(): BaseResponse 34 | 35 | suspend fun patchStatusInDevelop(): BaseResponse 36 | } 37 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dataSource/InfoDataSource.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dataSource 2 | 3 | import kr.genti.data.dto.BaseResponse 4 | import kr.genti.data.dto.request.SignupRequestDto 5 | import kr.genti.data.dto.response.SignUpUserDto 6 | 7 | interface InfoDataSource { 8 | suspend fun postSignupData(request: SignupRequestDto): BaseResponse 9 | 10 | suspend fun postUserLogout(): BaseResponse 11 | 12 | suspend fun deleteUser(): BaseResponse 13 | } 14 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dataSourceImpl/AuthDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dataSourceImpl 2 | 3 | import kr.genti.data.dataSource.AuthDataSource 4 | import kr.genti.data.dto.BaseResponse 5 | import kr.genti.data.dto.request.AuthRequestDto 6 | import kr.genti.data.dto.request.ReissueRequestDto 7 | import kr.genti.data.dto.response.AuthTokenDto 8 | import kr.genti.data.dto.response.ReissueTokenDto 9 | import kr.genti.data.service.AuthService 10 | import javax.inject.Inject 11 | 12 | data class AuthDataSourceImpl 13 | @Inject 14 | constructor( 15 | private val authService: AuthService, 16 | ) : AuthDataSource { 17 | override suspend fun postReissueTokens(request: ReissueRequestDto): BaseResponse = 18 | authService.postReissueTokens(request) 19 | 20 | override suspend fun postOauthDataToGetToken(request: AuthRequestDto): BaseResponse = 21 | authService.postOauthDataToGetToken(request) 22 | } 23 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dataSourceImpl/FeedDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dataSourceImpl 2 | 3 | import kr.genti.data.dataSource.FeedDataSource 4 | import kr.genti.data.dto.BaseResponse 5 | import kr.genti.data.dto.response.FeedItemDto 6 | import kr.genti.data.service.FeedService 7 | import javax.inject.Inject 8 | 9 | data class FeedDataSourceImpl 10 | @Inject 11 | constructor( 12 | private val feedService: FeedService, 13 | ) : FeedDataSource { 14 | override suspend fun getExampleItems(): BaseResponse> = feedService.getExampleItems() 15 | } 16 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dataSourceImpl/InfoDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dataSourceImpl 2 | 3 | import kr.genti.data.dataSource.InfoDataSource 4 | import kr.genti.data.dto.BaseResponse 5 | import kr.genti.data.dto.request.SignupRequestDto 6 | import kr.genti.data.dto.response.SignUpUserDto 7 | import kr.genti.data.service.InfoService 8 | import javax.inject.Inject 9 | 10 | data class InfoDataSourceImpl 11 | @Inject 12 | constructor( 13 | private val infoService: InfoService, 14 | ) : InfoDataSource { 15 | override suspend fun postSignupData(request: SignupRequestDto): BaseResponse = infoService.postSignupData(request) 16 | 17 | override suspend fun postUserLogout(): BaseResponse = infoService.postUserLogout() 18 | 19 | override suspend fun deleteUser(): BaseResponse = infoService.deleteUser() 20 | } 21 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/di/DataSourceModule.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.di 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | import kr.genti.data.dataSource.AuthDataSource 8 | import kr.genti.data.dataSource.CreateDataSource 9 | import kr.genti.data.dataSource.FeedDataSource 10 | import kr.genti.data.dataSource.GenerateDataSource 11 | import kr.genti.data.dataSource.InfoDataSource 12 | import kr.genti.data.dataSourceImpl.AuthDataSourceImpl 13 | import kr.genti.data.dataSourceImpl.CreateDataSourceImpl 14 | import kr.genti.data.dataSourceImpl.FeedDataSourceImpl 15 | import kr.genti.data.dataSourceImpl.GenerateDataSourceImpl 16 | import kr.genti.data.dataSourceImpl.InfoDataSourceImpl 17 | import javax.inject.Singleton 18 | 19 | @Module 20 | @InstallIn(SingletonComponent::class) 21 | object DataSourceModule { 22 | @Provides 23 | @Singleton 24 | fun provideAuthDataSource(authDataSourceImpl: AuthDataSourceImpl): AuthDataSource = authDataSourceImpl 25 | 26 | @Provides 27 | @Singleton 28 | fun provideInfoDataSource(infoDataSourceImpl: InfoDataSourceImpl): InfoDataSource = infoDataSourceImpl 29 | 30 | @Provides 31 | @Singleton 32 | fun provideCreateDataSource(createDataSourceImpl: CreateDataSourceImpl): CreateDataSource = createDataSourceImpl 33 | 34 | @Provides 35 | @Singleton 36 | fun provideFeedDataSource(feedDataSourceImpl: FeedDataSourceImpl): FeedDataSource = feedDataSourceImpl 37 | 38 | @Provides 39 | @Singleton 40 | fun provideGenerateDataSource(generateDataSourceImpl: GenerateDataSourceImpl): GenerateDataSource = generateDataSourceImpl 41 | } 42 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/BaseResponse.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class BaseResponse( 8 | @SerialName("success") 9 | val success: Boolean, 10 | @SerialName("response") 11 | val response: T, 12 | @SerialName("errorCode") 13 | val errorCode: String?, 14 | @SerialName("errorMessage") 15 | val errorMessage: String?, 16 | ) 17 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/request/AuthRequestDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.request 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.request.AuthRequestModel 6 | 7 | @Serializable 8 | data class AuthRequestDto( 9 | @SerialName("accessToken") 10 | val accessToken: String, 11 | @SerialName("fcmToken") 12 | val fcmToken: String, 13 | ) { 14 | companion object { 15 | fun AuthRequestModel.toDto() = AuthRequestDto(accessToken, fcmToken) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/request/CreateRequestDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.request 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.data.dto.request.KeyRequestDto.Companion.toDto 6 | import kr.genti.domain.entity.request.CreateRequestModel 7 | import kr.genti.domain.enums.PictureRatio 8 | 9 | @Serializable 10 | data class CreateRequestDto( 11 | @SerialName("prompt") 12 | val prompt: String, 13 | @SerialName("facePictureList") 14 | val imageS3KeyList: List, 15 | @SerialName("pictureRatio") 16 | val pictureRatio: PictureRatio, 17 | ) { 18 | companion object { 19 | fun CreateRequestModel.toDto() = 20 | CreateRequestDto( 21 | prompt, 22 | imageS3KeyList.map { it.toDto() }, 23 | pictureRatio, 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/request/CreateTwoRequestDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.request 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.data.dto.request.KeyRequestDto.Companion.toDto 6 | import kr.genti.domain.entity.request.CreateTwoRequestModel 7 | import kr.genti.domain.enums.PictureRatio 8 | 9 | @Serializable 10 | data class CreateTwoRequestDto( 11 | @SerialName("prompt") 12 | val prompt: String, 13 | @SerialName("facePictureList") 14 | val facePictureList: List, 15 | @SerialName("otherFacePictureList") 16 | val otherFacePictureList: List, 17 | @SerialName("pictureRatio") 18 | val pictureRatio: PictureRatio, 19 | ) { 20 | companion object { 21 | fun CreateTwoRequestModel.toDto() = 22 | CreateTwoRequestDto( 23 | prompt, 24 | facePictureList.map { it.toDto() }, 25 | otherFacePictureList.map { it.toDto() }, 26 | pictureRatio, 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/request/ImageBucketRequestDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.request 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.request.ImageBucketRequestModel 6 | import kr.genti.domain.enums.FileType 7 | 8 | @Serializable 9 | data class ImageBucketRequestDto( 10 | @SerialName("fileType") 11 | val fileType: FileType, 12 | @SerialName("fileName") 13 | val fileName: String, 14 | ) { 15 | companion object { 16 | fun ImageBucketRequestModel.toDto() = ImageBucketRequestDto(fileType, fileName) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/request/KeyRequestDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.request 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.request.KeyRequestModel 6 | 7 | @Serializable 8 | data class KeyRequestDto( 9 | @SerialName("key") 10 | val key: String?, 11 | ) { 12 | companion object { 13 | fun KeyRequestModel.toDto() = KeyRequestDto(key) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/request/PurchaseValidRequestDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.request 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.request.PurchaseValidRequestModel 6 | 7 | @Serializable 8 | data class PurchaseValidRequestDto( 9 | @SerialName("packageName") 10 | val packageName: String, 11 | @SerialName("productId") 12 | val productId: String, 13 | @SerialName("purchaseToken") 14 | val purchaseToken: String, 15 | ) { 16 | companion object { 17 | fun PurchaseValidRequestModel.toDto() = PurchaseValidRequestDto( 18 | packageName, 19 | productId, 20 | purchaseToken 21 | ) 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/request/ReissueRequestDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.request 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.request.ReissueRequestModel 6 | 7 | @Serializable 8 | data class ReissueRequestDto( 9 | @SerialName("accessToken") 10 | val accessToken: String, 11 | @SerialName("refreshToken") 12 | val refreshToken: String, 13 | ) { 14 | companion object { 15 | fun ReissueRequestModel.toDto() = ReissueRequestDto(accessToken, refreshToken) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/request/ReportRequestDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.request 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.request.ReportRequestModel 6 | 7 | @Serializable 8 | data class ReportRequestDto( 9 | @SerialName("pictureGenerateResponseId") 10 | val pictureGenerateResponseId: Long, 11 | @SerialName("content") 12 | val content: String, 13 | ) { 14 | companion object { 15 | fun ReportRequestModel.toDto() = ReportRequestDto(pictureGenerateResponseId, content) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/request/SignupRequestDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.request 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.request.SignupRequestModel 6 | 7 | @Serializable 8 | data class SignupRequestDto( 9 | @SerialName("birthYear") 10 | val birthYear: String, 11 | @SerialName("sex") 12 | val sex: String, 13 | @SerialName("phoneNumber") 14 | val phoneNumber: String?, 15 | ) { 16 | companion object { 17 | fun SignupRequestModel.toDto() = SignupRequestDto(birthYear, sex, phoneNumber) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/AuthTokenDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.AuthTokenModel 6 | 7 | @Serializable 8 | data class AuthTokenDto( 9 | @SerialName("accessToken") 10 | val accessToken: String, 11 | @SerialName("refreshToken") 12 | val refreshToken: String, 13 | @SerialName("userRoleString") 14 | val userRoleString: String, 15 | ) { 16 | fun toModel() = AuthTokenModel(accessToken, refreshToken, userRoleString) 17 | } 18 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/FeedItemDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.FeedItemModel 6 | 7 | @Serializable 8 | data class FeedItemDto( 9 | @SerialName("picture") 10 | val picture: ImageDto, 11 | @SerialName("prompt") 12 | val prompt: String, 13 | ) { 14 | fun toModel() = 15 | FeedItemModel( 16 | picture.toModel(), 17 | prompt, 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/GenerateStatusDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.GenerateStatusModel 6 | import kr.genti.domain.enums.GenerateStatus 7 | 8 | @Serializable 9 | data class GenerateStatusDto( 10 | @SerialName("pictureGenerateRequestId") 11 | val pictureGenerateRequestId: Long?, 12 | @SerialName("status") 13 | val status: GenerateStatus, 14 | @SerialName("pictureGenerateResponse") 15 | val pictureGenerateResponse: GenerateResponseDto?, 16 | @SerialName("paid") 17 | val paid: Boolean?, 18 | ) { 19 | @Serializable 20 | data class GenerateResponseDto( 21 | @SerialName("pictureGenerateResponseId") 22 | val pictureGenerateResponseId: Long, 23 | @SerialName("pictureCompleted") 24 | val pictureCompleted: ImageDto?, 25 | ) { 26 | fun toModel() = 27 | GenerateStatusModel.GenerateResponseModel( 28 | pictureGenerateResponseId, 29 | pictureCompleted?.toModel(), 30 | ) 31 | } 32 | 33 | fun toModel() = 34 | GenerateStatusModel( 35 | pictureGenerateRequestId, 36 | status, 37 | pictureGenerateResponse?.toModel(), 38 | paid, 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/ImageBucketDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.ImageBucketModel 6 | 7 | @Serializable 8 | data class ImageBucketDto( 9 | @SerialName("fileName") 10 | val fileName: String, 11 | @SerialName("url") 12 | val presignedUrl: String, 13 | @SerialName("s3Key") 14 | val s3Key: String, 15 | ) { 16 | fun toModel() = ImageBucketModel(fileName, presignedUrl, s3Key) 17 | } 18 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/ImageDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.ImageModel 6 | import kr.genti.domain.enums.PictureRatio.Companion.toPictureRatio 7 | 8 | @Serializable 9 | data class ImageDto( 10 | @SerialName("id") 11 | val id: Long, 12 | @SerialName("url") 13 | val url: String, 14 | @SerialName("pictureRatio") 15 | val pictureRatio: String, 16 | ) { 17 | fun toModel() = 18 | ImageModel( 19 | id, 20 | url, 21 | pictureRatio.toPictureRatio(), 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/OpenchatDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.OpenchatModel 6 | 7 | @Serializable 8 | data class OpenchatDto( 9 | @SerialName("accessible") 10 | val accessible: Boolean, 11 | @SerialName("count") 12 | val count: Int?, 13 | @SerialName("url") 14 | val url: String?, 15 | ) { 16 | fun toModel() = OpenchatModel(accessible, count, url) 17 | } 18 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/PicturePagedListDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.PicturePagedListModel 6 | 7 | @Serializable 8 | data class PicturePagedListDto( 9 | @SerialName("totalPages") val totalPages: Int, 10 | @SerialName("content") val content: List, 11 | ) { 12 | fun toModel() = 13 | PicturePagedListModel( 14 | totalPages, 15 | content.map { it.toModel() }, 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/PromptExampleDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.PromptExampleModel 6 | 7 | @Serializable 8 | data class PromptExampleDto( 9 | @SerialName("url") 10 | val url: String, 11 | @SerialName("prompt") 12 | val prompt: String, 13 | ) { 14 | fun toModel() = PromptExampleModel(url, prompt) 15 | } -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/ReissueTokenDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.ReissueTokenModel 6 | 7 | @Serializable 8 | data class ReissueTokenDto( 9 | @SerialName("accessToken") 10 | val accessToken: String, 11 | @SerialName("refreshToken") 12 | val refreshToken: String, 13 | ) { 14 | fun toModel() = ReissueTokenModel(accessToken, refreshToken) 15 | } 16 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/ServerAvailableDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.ServerAvailableModel 6 | 7 | @Serializable 8 | data class ServerAvailableDto( 9 | @SerialName("status") 10 | val status: Boolean, 11 | @SerialName("message") 12 | val message: String?, 13 | ) { 14 | fun toModel() = ServerAvailableModel(status, message) 15 | } 16 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/dto/response/SignUpUserDto.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.dto.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kr.genti.domain.entity.response.SignUpUserModel 6 | 7 | @Serializable 8 | data class SignUpUserDto( 9 | @SerialName("email") 10 | val email: String, 11 | @SerialName("lastLoginOauthPlatform") 12 | val lastLoginOauthPlatform: String, 13 | @SerialName("nickname") 14 | val nickname: String, 15 | @SerialName("birthYear") 16 | val birthYear: String, 17 | @SerialName("sex") 18 | val sex: String, 19 | ) { 20 | fun toModel() = SignUpUserModel(email, lastLoginOauthPlatform, nickname, birthYear, sex) 21 | } 22 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/repositoryImpl/AuthRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.repositoryImpl 2 | 3 | import kr.genti.data.dataSource.AuthDataSource 4 | import kr.genti.data.dto.request.AuthRequestDto.Companion.toDto 5 | import kr.genti.data.dto.request.ReissueRequestDto.Companion.toDto 6 | import kr.genti.domain.entity.request.AuthRequestModel 7 | import kr.genti.domain.entity.request.ReissueRequestModel 8 | import kr.genti.domain.entity.response.AuthTokenModel 9 | import kr.genti.domain.entity.response.ReissueTokenModel 10 | import kr.genti.domain.repository.AuthRepository 11 | import javax.inject.Inject 12 | 13 | class AuthRepositoryImpl 14 | @Inject 15 | constructor( 16 | private val authDataSource: AuthDataSource, 17 | ) : AuthRepository { 18 | override suspend fun postReissueTokens(request: ReissueRequestModel): ReissueTokenModel = 19 | authDataSource.postReissueTokens(request.toDto()).response.toModel() 20 | 21 | override suspend fun postOauthDataToGetToken(request: AuthRequestModel): AuthTokenModel = 22 | authDataSource.postOauthDataToGetToken(request.toDto()).response.toModel() 23 | } 24 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/repositoryImpl/FeedRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.repositoryImpl 2 | 3 | import kr.genti.data.dataSource.FeedDataSource 4 | import kr.genti.domain.entity.response.FeedItemModel 5 | import kr.genti.domain.repository.FeedRepository 6 | import javax.inject.Inject 7 | 8 | class FeedRepositoryImpl 9 | @Inject 10 | constructor( 11 | private val feedDataSource: FeedDataSource, 12 | ) : FeedRepository { 13 | override suspend fun getExampleItems(): List = 14 | feedDataSource.getExampleItems().response.map { it.toModel() } 15 | } 16 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/repositoryImpl/InfoRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.repositoryImpl 2 | 3 | import kr.genti.data.dataSource.InfoDataSource 4 | import kr.genti.data.dto.request.SignupRequestDto.Companion.toDto 5 | import kr.genti.domain.entity.request.SignupRequestModel 6 | import kr.genti.domain.entity.response.SignUpUserModel 7 | import kr.genti.domain.repository.InfoRepository 8 | import javax.inject.Inject 9 | 10 | class InfoRepositoryImpl 11 | @Inject 12 | constructor( 13 | private val infoDataSource: InfoDataSource, 14 | ) : InfoRepository { 15 | override suspend fun postSignupData(request: SignupRequestModel): SignUpUserModel = 16 | infoDataSource.postSignupData(request.toDto()).response.toModel() 17 | 18 | override suspend fun postUserLogout(): Boolean = 19 | infoDataSource.postUserLogout().response 20 | 21 | override suspend fun deleteUser(): Boolean = 22 | infoDataSource.deleteUser().response 23 | } 24 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/repositoryImpl/UploadRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.repositoryImpl 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import dagger.hilt.android.qualifiers.ApplicationContext 6 | import kr.genti.data.service.UploadService 7 | import kr.genti.data.util.ContentUriRequestBody 8 | import kr.genti.domain.repository.UploadRepository 9 | import javax.inject.Inject 10 | 11 | class UploadRepositoryImpl 12 | @Inject 13 | constructor( 14 | @ApplicationContext private val context: Context, 15 | private val uploadService: UploadService, 16 | ) : UploadRepository { 17 | override suspend fun uploadImage( 18 | preSignedURL: String, 19 | imageUri: String, 20 | ): Unit = 21 | uploadService.putS3Image( 22 | preSignedURL, 23 | ContentUriRequestBody(context, Uri.parse(imageUri)), 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/repositoryImpl/UserRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.repositoryImpl 2 | 3 | import kr.genti.datastore.local.UserSharedPref 4 | import kr.genti.domain.repository.UserRepository 5 | import javax.inject.Inject 6 | 7 | class UserRepositoryImpl 8 | @Inject 9 | constructor( 10 | private val userSharedPref: UserSharedPref, 11 | ) : UserRepository { 12 | override fun getAccessToken(): String = userSharedPref.accessToken 13 | 14 | override fun getRefreshToken(): String = userSharedPref.refreshToken 15 | 16 | override fun getUserRole(): String = userSharedPref.userRole 17 | 18 | override fun setTokens( 19 | accessToken: String, 20 | refreshToken: String, 21 | ) { 22 | userSharedPref.accessToken = accessToken 23 | userSharedPref.refreshToken = refreshToken 24 | } 25 | 26 | override fun setUserRole(userRole: String) { 27 | userSharedPref.userRole = userRole 28 | } 29 | 30 | override fun clearInfo() { 31 | userSharedPref.clearInfo() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/service/AuthService.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.service 2 | 3 | import kr.genti.data.dto.BaseResponse 4 | import kr.genti.data.dto.request.AuthRequestDto 5 | import kr.genti.data.dto.request.ReissueRequestDto 6 | import kr.genti.data.dto.response.AuthTokenDto 7 | import kr.genti.data.dto.response.ReissueTokenDto 8 | import retrofit2.http.Body 9 | import retrofit2.http.POST 10 | 11 | interface AuthService { 12 | @POST("auth/v1/reissue") 13 | suspend fun postReissueTokens( 14 | @Body request: ReissueRequestDto, 15 | ): BaseResponse 16 | 17 | @POST("auth/v1/login/oauth2/token/kakao") 18 | suspend fun postOauthDataToGetToken( 19 | @Body request: AuthRequestDto, 20 | ): BaseResponse 21 | } 22 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/service/FeedService.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.service 2 | 3 | import kr.genti.data.dto.BaseResponse 4 | import kr.genti.data.dto.response.FeedItemDto 5 | import retrofit2.http.GET 6 | 7 | interface FeedService { 8 | @GET("api/v1/users/examples/with-picture") 9 | suspend fun getExampleItems(): BaseResponse> 10 | } 11 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/service/InfoService.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.service 2 | 3 | import kr.genti.data.dto.BaseResponse 4 | import kr.genti.data.dto.request.SignupRequestDto 5 | import kr.genti.data.dto.response.SignUpUserDto 6 | import retrofit2.http.Body 7 | import retrofit2.http.DELETE 8 | import retrofit2.http.POST 9 | 10 | interface InfoService { 11 | @POST("api/v2/users/signup") 12 | suspend fun postSignupData( 13 | @Body request: SignupRequestDto, 14 | ): BaseResponse 15 | 16 | @POST("api/v1/users/logout") 17 | suspend fun postUserLogout(): BaseResponse 18 | 19 | @DELETE("api/v1/users") 20 | suspend fun deleteUser(): BaseResponse 21 | } 22 | -------------------------------------------------------------------------------- /data/src/main/java/kr/genti/data/service/UploadService.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.data.service 2 | 3 | import okhttp3.RequestBody 4 | import retrofit2.http.Body 5 | import retrofit2.http.PUT 6 | import retrofit2.http.Url 7 | 8 | interface UploadService { 9 | @PUT 10 | suspend fun putS3Image( 11 | @Url preSignedURL: String, 12 | @Body image: RequestBody, 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /data/src/test/java/kr/genti/data/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.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/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.javaLibrary") 3 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/request/AuthRequestModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.request 2 | 3 | data class AuthRequestModel( 4 | val accessToken: String, 5 | val fcmToken: String, 6 | ) 7 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/request/CreateRequestModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.request 2 | 3 | import kr.genti.domain.enums.PictureRatio 4 | 5 | data class CreateRequestModel( 6 | val prompt: String, 7 | val imageS3KeyList: List, 8 | val pictureRatio: PictureRatio, 9 | ) 10 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/request/CreateTwoRequestModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.request 2 | 3 | import kr.genti.domain.enums.PictureRatio 4 | 5 | data class CreateTwoRequestModel( 6 | val prompt: String, 7 | val facePictureList: List, 8 | val otherFacePictureList: List, 9 | val pictureRatio: PictureRatio, 10 | ) 11 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/request/ImageBucketRequestModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.request 2 | 3 | import kr.genti.domain.enums.FileType 4 | 5 | data class ImageBucketRequestModel( 6 | val fileType: FileType, 7 | val fileName: String, 8 | ) 9 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/request/KeyRequestModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.request 2 | 3 | data class KeyRequestModel( 4 | val key: String?, 5 | ) 6 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/request/PurchaseValidRequestModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.request 2 | 3 | data class PurchaseValidRequestModel( 4 | val packageName: String, 5 | val productId: String, 6 | val purchaseToken: String, 7 | ) 8 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/request/ReissueRequestModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.request 2 | 3 | data class ReissueRequestModel( 4 | val accessToken: String, 5 | val refreshToken: String, 6 | ) 7 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/request/ReportRequestModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.request 2 | 3 | data class ReportRequestModel( 4 | val pictureGenerateResponseId: Long, 5 | val content: String, 6 | ) 7 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/request/SignupRequestModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.request 2 | 3 | data class SignupRequestModel( 4 | val birthYear: String, 5 | val sex: String, 6 | val phoneNumber: String?, 7 | ) 8 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/AuthTokenModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | data class AuthTokenModel( 4 | val accessToken: String, 5 | val refreshToken: String, 6 | val userRoleString: String, 7 | ) 8 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/FeedItemModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | data class FeedItemModel( 4 | val picture: ImageModel, 5 | val prompt: String, 6 | ) 7 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/GenerateStatusModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | import kr.genti.domain.enums.GenerateStatus 4 | 5 | data class GenerateStatusModel( 6 | val requestId: Long?, 7 | val status: GenerateStatus, 8 | val response: GenerateResponseModel?, 9 | val paid: Boolean?, 10 | ) { 11 | data class GenerateResponseModel( 12 | val responseId: Long, 13 | val picture: ImageModel?, 14 | ) 15 | 16 | companion object { 17 | fun emptyGenerateStatusModel() = GenerateStatusModel( 18 | requestId = null, 19 | status = GenerateStatus.EMPTY, 20 | response = null, 21 | paid = null, 22 | ) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/ImageBucketModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | data class ImageBucketModel( 4 | val fileName: String, 5 | val presignedUrl: String, 6 | val s3Key: String, 7 | ) 8 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/ImageFileModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | data class ImageFileModel( 4 | var id: Long, 5 | var name: String, 6 | var url: String, 7 | ) { 8 | companion object { 9 | fun emptyImageFileModel() = ImageFileModel(-1, "", "") 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/ImageModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | import kr.genti.domain.enums.PictureRatio 4 | 5 | data class ImageModel( 6 | val id: Long, 7 | val url: String, 8 | val pictureRatio: PictureRatio?, 9 | ) 10 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/OpenchatModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | data class OpenchatModel( 4 | val accessible: Boolean, 5 | val count: Int?, 6 | val url: String?, 7 | ) 8 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/PicturePagedListModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | data class PicturePagedListModel( 4 | val totalPages: Int, 5 | val content: List, 6 | ) 7 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/PromptExampleModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | data class PromptExampleModel( 4 | val url: String, 5 | val prompt: String, 6 | ) -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/ReissueTokenModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | data class ReissueTokenModel( 4 | val accessToken: String, 5 | val refreshToken: String, 6 | ) 7 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/ServerAvailableModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | data class ServerAvailableModel( 4 | val status: Boolean, 5 | val message: String?, 6 | ) 7 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/entity/response/SignUpUserModel.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.entity.response 2 | 3 | data class SignUpUserModel( 4 | val email: String, 5 | val lastLoginOauthPlatform: String, 6 | val nickname: String, 7 | val birthYear: String, 8 | val sex: String, 9 | ) 10 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/enums/FileType.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.enums 2 | 3 | enum class FileType { 4 | CREATED_IMAGE, 5 | ADMIN_UPLOADED_IMAGE, 6 | USER_UPLOADED_IMAGE, 7 | USER_VERIFICATION_IMAGE, 8 | DEV_USER_VERIFICATION_IMAGE, 9 | } 10 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/enums/Gender.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.enums 2 | 3 | enum class Gender { 4 | M, 5 | W, 6 | NONE, 7 | } 8 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/enums/GenerateStatus.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.enums 2 | 3 | enum class GenerateStatus { 4 | IN_PROGRESS, 5 | AWAIT_USER_VERIFICATION, 6 | NEW_REQUEST_AVAILABLE, 7 | CANCELED, 8 | EMPTY, 9 | } 10 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/enums/PictureNumber.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.enums 2 | 3 | enum class PictureNumber { 4 | ONE, 5 | TWO, 6 | NONE 7 | ; 8 | 9 | companion object { 10 | fun String.toPictureNumber(): PictureNumber = 11 | when (this) { 12 | "ONE" -> ONE 13 | "TWO" -> TWO 14 | else -> NONE 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/enums/PictureRatio.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.enums 2 | 3 | enum class PictureRatio { 4 | RATIO_SERO, 5 | RATIO_GARO, 6 | NONE 7 | ; 8 | 9 | companion object { 10 | fun String.toPictureRatio(): PictureRatio = 11 | when (this) { 12 | "RATIO_GARO" -> RATIO_GARO 13 | "RATIO_SERO" -> RATIO_SERO 14 | else -> NONE 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/repository/AuthRepository.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.repository 2 | 3 | import kr.genti.domain.entity.request.AuthRequestModel 4 | import kr.genti.domain.entity.request.ReissueRequestModel 5 | import kr.genti.domain.entity.response.AuthTokenModel 6 | import kr.genti.domain.entity.response.ReissueTokenModel 7 | 8 | interface AuthRepository { 9 | suspend fun postReissueTokens(request: ReissueRequestModel): ReissueTokenModel 10 | 11 | suspend fun postOauthDataToGetToken(request: AuthRequestModel): AuthTokenModel 12 | } 13 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/repository/CreateRepository.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.repository 2 | 3 | import kr.genti.domain.entity.request.CreateRequestModel 4 | import kr.genti.domain.entity.request.CreateTwoRequestModel 5 | import kr.genti.domain.entity.request.KeyRequestModel 6 | import kr.genti.domain.entity.request.PurchaseValidRequestModel 7 | import kr.genti.domain.entity.request.ImageBucketRequestModel 8 | import kr.genti.domain.entity.response.PromptExampleModel 9 | import kr.genti.domain.entity.response.ImageBucketModel 10 | 11 | interface CreateRepository { 12 | suspend fun getSingleImageBucket(request: ImageBucketRequestModel): ImageBucketModel 13 | 14 | suspend fun getThreeImageBucket(request: List): List 15 | 16 | suspend fun postToCreate(request: CreateRequestModel): Boolean 17 | 18 | suspend fun postToCreateOne(request: CreateRequestModel): Boolean 19 | 20 | suspend fun postToCreateTwo(request: CreateTwoRequestModel): Boolean 21 | 22 | suspend fun postToVerify(request: KeyRequestModel): Boolean 23 | 24 | suspend fun getPromptExample(type: String): List 25 | 26 | suspend fun postToValidatePurchase(request: PurchaseValidRequestModel): Boolean 27 | } 28 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/repository/FeedRepository.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.repository 2 | 3 | import kr.genti.domain.entity.response.FeedItemModel 4 | 5 | interface FeedRepository { 6 | suspend fun getExampleItems(): List 7 | } 8 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/repository/GenerateRepository.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.repository 2 | 3 | import kr.genti.domain.entity.request.ReportRequestModel 4 | import kr.genti.domain.entity.response.GenerateStatusModel 5 | import kr.genti.domain.entity.response.OpenchatModel 6 | import kr.genti.domain.entity.response.PicturePagedListModel 7 | import kr.genti.domain.entity.response.ServerAvailableModel 8 | 9 | interface GenerateRepository { 10 | suspend fun getGenerateStatus(): GenerateStatusModel 11 | 12 | suspend fun postGenerateReport(request: ReportRequestModel): Boolean 13 | 14 | suspend fun postGenerateRate(responseId: Int, star: Int): Boolean 15 | 16 | suspend fun postVerifyGenerateState(responseId: Int): Boolean 17 | 18 | suspend fun getCanceledToReset(requestId: String): Boolean 19 | 20 | suspend fun getGeneratedPictureList(page: Int, size: Int): PicturePagedListModel 21 | 22 | suspend fun getOpenchatData(): OpenchatModel 23 | 24 | suspend fun getIsUserVerified(): Boolean 25 | 26 | suspend fun getIsServerAvailable(): ServerAvailableModel 27 | 28 | suspend fun patchStatusInDevelop(): Boolean 29 | } 30 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/repository/InfoRepository.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.repository 2 | 3 | import kr.genti.domain.entity.request.SignupRequestModel 4 | import kr.genti.domain.entity.response.SignUpUserModel 5 | 6 | interface InfoRepository { 7 | suspend fun postSignupData(request: SignupRequestModel): SignUpUserModel 8 | 9 | suspend fun postUserLogout(): Boolean 10 | 11 | suspend fun deleteUser(): Boolean 12 | } 13 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/repository/UploadRepository.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.repository 2 | 3 | interface UploadRepository { 4 | suspend fun uploadImage( 5 | preSignedURL: String, 6 | imageUri: String, 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/repository/UserRepository.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.repository 2 | 3 | interface UserRepository { 4 | fun getAccessToken(): String 5 | 6 | fun getRefreshToken(): String 7 | 8 | fun getUserRole(): String 9 | 10 | fun setTokens( 11 | accessToken: String, 12 | refreshToken: String, 13 | ) 14 | 15 | fun setUserRole(userRole: String) 16 | 17 | fun clearInfo() 18 | } 19 | -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/auth/GetNewTokensFromOauthUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.auth 2 | 3 | import kr.genti.domain.entity.request.AuthRequestModel 4 | import kr.genti.domain.repository.AuthRepository 5 | import kr.genti.domain.repository.UserRepository 6 | import javax.inject.Inject 7 | 8 | class GetNewTokensFromOauthUseCase @Inject constructor( 9 | private val authRepository: AuthRepository, 10 | private val userRepository: UserRepository 11 | ) { 12 | suspend operator fun invoke( 13 | newAccessToken: String, 14 | fcmToken: String 15 | ): Result = 16 | runCatching { 17 | val request = AuthRequestModel(newAccessToken, fcmToken) 18 | val response = authRepository.postOauthDataToGetToken(request) 19 | with(userRepository) { 20 | setTokens(response.accessToken, response.refreshToken) 21 | setUserRole(response.userRoleString) 22 | } 23 | val isAssigned = response.userRoleString == ALREADY_ASSIGNED 24 | return@runCatching isAssigned 25 | } 26 | 27 | companion object { 28 | const val ALREADY_ASSIGNED = "USER" 29 | } 30 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/auth/ReissueOldTokensUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.auth 2 | 3 | import kr.genti.domain.entity.request.ReissueRequestModel 4 | import kr.genti.domain.entity.response.ReissueTokenModel 5 | import kr.genti.domain.repository.AuthRepository 6 | import kr.genti.domain.repository.UserRepository 7 | import javax.inject.Inject 8 | 9 | class ReissueOldTokensUseCase @Inject constructor( 10 | private val authRepository: AuthRepository, 11 | private val userRepository: UserRepository 12 | ) { 13 | suspend operator fun invoke(): Result = 14 | runCatching { 15 | val response = authRepository.postReissueTokens( 16 | ReissueRequestModel( 17 | userRepository.getAccessToken(), 18 | userRepository.getRefreshToken() 19 | ) 20 | ) 21 | userRepository.setTokens( 22 | response.accessToken, 23 | response.refreshToken 24 | ) 25 | return@runCatching response 26 | } 27 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/auth/SignUpUserUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.auth 2 | 3 | import kr.genti.domain.entity.request.SignupRequestModel 4 | import kr.genti.domain.entity.response.SignUpUserModel 5 | import kr.genti.domain.enums.Gender 6 | import kr.genti.domain.repository.InfoRepository 7 | import kr.genti.domain.repository.UserRepository 8 | import javax.inject.Inject 9 | 10 | class SignUpUserUseCase @Inject constructor( 11 | private val infoRepository: InfoRepository, 12 | private val userRepository: UserRepository 13 | ) { 14 | suspend operator fun invoke( 15 | birthYear: String, 16 | gender: Gender, 17 | phoneNumber: String? = null 18 | ): Result = 19 | runCatching { 20 | val request = SignupRequestModel( 21 | birthYear, 22 | gender.toString(), 23 | phoneNumber 24 | ) 25 | val response = infoRepository.postSignupData(request) 26 | userRepository.setUserRole(ROLE_USER) 27 | return@runCatching response 28 | } 29 | 30 | companion object { 31 | private const val ROLE_USER = "USER" 32 | } 33 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/feed/GetFeedItemListUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.feed 2 | 3 | import kr.genti.domain.entity.response.FeedItemModel 4 | import kr.genti.domain.repository.FeedRepository 5 | import javax.inject.Inject 6 | 7 | class GetFeedItemListUseCase @Inject constructor( 8 | private val feedRepository: FeedRepository 9 | ) { 10 | suspend operator fun invoke(): Result> = 11 | runCatching { 12 | val response = feedRepository.getExampleItems() 13 | return@runCatching response 14 | } 15 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/generate/CheckPurchaseValidUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.generate 2 | 3 | import kr.genti.domain.entity.request.PurchaseValidRequestModel 4 | import kr.genti.domain.repository.CreateRepository 5 | import javax.inject.Inject 6 | 7 | class CheckPurchaseValidUseCase @Inject constructor( 8 | private val createRepository: CreateRepository, 9 | ) { 10 | suspend operator fun invoke( 11 | packageName: String, 12 | productId: String, 13 | purchaseToken: String, 14 | ): Result = 15 | runCatching { 16 | val request = PurchaseValidRequestModel( 17 | packageName = packageName, 18 | productId = productId, 19 | purchaseToken = purchaseToken 20 | ) 21 | val isSuccess = createRepository.postToValidatePurchase(request) 22 | if (isSuccess) { 23 | return@runCatching true 24 | } else { 25 | throw Exception("checking purchase valid failed") 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/generate/CheckServerAvailableUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.generate 2 | 3 | import kr.genti.domain.entity.response.ServerAvailableModel 4 | import kr.genti.domain.repository.GenerateRepository 5 | import javax.inject.Inject 6 | 7 | class CheckServerAvailableUseCase @Inject constructor( 8 | private val generateRepository: GenerateRepository, 9 | ) { 10 | suspend operator fun invoke(): Result = 11 | runCatching { 12 | val response = generateRepository.getIsServerAvailable() 13 | return@runCatching response 14 | } 15 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/generate/GetPromptExampleListUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.generate 2 | 3 | import kr.genti.domain.entity.response.PromptExampleModel 4 | import kr.genti.domain.repository.CreateRepository 5 | import javax.inject.Inject 6 | 7 | class GetPromptExampleListUseCase @Inject constructor( 8 | private val createRepository: CreateRepository, 9 | ) { 10 | suspend operator fun invoke( 11 | generateType: String, 12 | ): Result> = 13 | runCatching { 14 | val response = createRepository.getPromptExample(generateType) 15 | return@runCatching response 16 | } 17 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/generate/GetThreeImageBucketUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.generate 2 | 3 | import kr.genti.domain.entity.request.ImageBucketRequestModel 4 | import kr.genti.domain.entity.response.ImageBucketModel 5 | import kr.genti.domain.entity.response.ImageFileModel 6 | import kr.genti.domain.enums.FileType 7 | import kr.genti.domain.repository.CreateRepository 8 | import javax.inject.Inject 9 | 10 | class GetThreeImageBucketUseCase @Inject constructor( 11 | private val createRepository: CreateRepository, 12 | ) { 13 | suspend operator fun invoke( 14 | selectedImageList: List 15 | ): Result> = 16 | runCatching { 17 | val request = selectedImageList.map { image -> 18 | ImageBucketRequestModel(FileType.USER_UPLOADED_IMAGE, image.name) 19 | } 20 | val response = createRepository.getThreeImageBucket(request) 21 | return@runCatching response 22 | } 23 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/profile/GetGeneratedPictureListUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.profile 2 | 3 | import kr.genti.domain.entity.response.PicturePagedListModel 4 | import kr.genti.domain.repository.GenerateRepository 5 | import javax.inject.Inject 6 | 7 | class GetGeneratedPictureListUseCase @Inject constructor( 8 | private val generateRepository: GenerateRepository 9 | ) { 10 | suspend operator fun invoke( 11 | page: Int, 12 | size: Int = 10 13 | ): Result = 14 | runCatching { 15 | val response = generateRepository.getGeneratedPictureList( 16 | page = page, 17 | size = size 18 | ) 19 | return@runCatching response 20 | } 21 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/result/ForceGenerateInDebugUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.result 2 | 3 | import kr.genti.domain.repository.GenerateRepository 4 | import javax.inject.Inject 5 | 6 | class ForceGenerateInDebugUseCase @Inject constructor( 7 | private val generateRepository: GenerateRepository 8 | ) { 9 | suspend operator fun invoke(): Result = 10 | runCatching { 11 | val isSuccess = generateRepository.patchStatusInDevelop() 12 | if (isSuccess) { 13 | return@runCatching true 14 | } else { 15 | throw Exception("force generate in debug failed") 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/result/GetCurrentGenerateStatusUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.result 2 | 3 | import kr.genti.domain.entity.response.GenerateStatusModel 4 | import kr.genti.domain.repository.GenerateRepository 5 | import javax.inject.Inject 6 | 7 | class GetCurrentGenerateStatusUseCase @Inject constructor( 8 | private val generateRepository: GenerateRepository, 9 | ) { 10 | suspend operator fun invoke(): Result = 11 | runCatching { 12 | val response = generateRepository.getGenerateStatus() 13 | return@runCatching response 14 | } 15 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/result/ReportUnwantedResultUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.result 2 | 3 | import kr.genti.domain.entity.request.ReportRequestModel 4 | import kr.genti.domain.repository.GenerateRepository 5 | import javax.inject.Inject 6 | 7 | class ReportUnwantedResultUseCase @Inject constructor( 8 | private val generateRepository: GenerateRepository 9 | ) { 10 | suspend operator fun invoke( 11 | imageResponseId: Long, 12 | reportText: String 13 | ): Result = 14 | runCatching { 15 | val request = ReportRequestModel( 16 | pictureGenerateResponseId = imageResponseId, 17 | content = reportText 18 | ) 19 | val isSuccess = generateRepository.postGenerateReport(request) 20 | if (isSuccess) { 21 | return@runCatching true 22 | } else { 23 | throw Exception("reporting generate result failed") 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/result/ResetGenerateStatusUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.result 2 | 3 | import kr.genti.domain.repository.GenerateRepository 4 | import javax.inject.Inject 5 | 6 | class ResetGenerateStatusUseCase @Inject constructor( 7 | private val generateRepository: GenerateRepository 8 | ) { 9 | suspend operator fun invoke( 10 | generateRequestId: Long? 11 | ): Result = 12 | runCatching { 13 | val isSuccess = generateRepository.getCanceledToReset( 14 | requestId = requireNotNull(generateRequestId) { "generateRequestId is null" }.toString() 15 | ) 16 | if (isSuccess) { 17 | return@runCatching true 18 | } else { 19 | throw Exception("resetting generate status failed") 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/result/SkipGenerateRatingUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.result 2 | 3 | import kr.genti.domain.repository.GenerateRepository 4 | import javax.inject.Inject 5 | 6 | class SkipGenerateRatingUseCase @Inject constructor( 7 | private val generateRepository: GenerateRepository 8 | ) { 9 | suspend operator fun invoke( 10 | imageResponseId: Long, 11 | ): Result = 12 | runCatching { 13 | val isSuccess = generateRepository.postVerifyGenerateState( 14 | responseId = imageResponseId.toInt() 15 | ) 16 | if (isSuccess) { 17 | return@runCatching true 18 | } else { 19 | throw Exception("skipping generate rating failed") 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/result/SubmitGenerateRatingUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.result 2 | 3 | import kr.genti.domain.repository.GenerateRepository 4 | import javax.inject.Inject 5 | 6 | class SubmitGenerateRatingUseCase @Inject constructor( 7 | private val generateRepository: GenerateRepository 8 | ) { 9 | suspend operator fun invoke( 10 | imageResponseId: Long, 11 | starRate: Int 12 | ): Result = 13 | runCatching { 14 | val isSuccess = generateRepository.postGenerateRate( 15 | responseId = imageResponseId.toInt(), 16 | star = starRate, 17 | ) 18 | if (isSuccess) { 19 | return@runCatching true 20 | } else { 21 | throw Exception("submitting generate rating failed") 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/setting/DeleteUserUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.setting 2 | 3 | import kr.genti.domain.repository.InfoRepository 4 | import kr.genti.domain.repository.UserRepository 5 | import javax.inject.Inject 6 | 7 | class DeleteUserUseCase @Inject constructor( 8 | private val infoRepository: InfoRepository, 9 | private val userRepository: UserRepository 10 | ) { 11 | suspend operator fun invoke(): Result = 12 | runCatching { 13 | val isSuccess = infoRepository.deleteUser() 14 | userRepository.clearInfo() 15 | if (isSuccess) { 16 | return@runCatching true 17 | } else { 18 | throw Exception("delete failed") 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/setting/LogoutUserUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.setting 2 | 3 | import kr.genti.domain.repository.InfoRepository 4 | import kr.genti.domain.repository.UserRepository 5 | import javax.inject.Inject 6 | 7 | class LogoutUserUseCase @Inject constructor( 8 | private val infoRepository: InfoRepository, 9 | private val userRepository: UserRepository 10 | ) { 11 | suspend operator fun invoke(): Result = 12 | runCatching { 13 | val isSuccess = infoRepository.postUserLogout() 14 | userRepository.clearInfo() 15 | if (isSuccess) { 16 | return@runCatching true 17 | } else { 18 | throw Exception("logout failed") 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/upload/UploadImageToBucketUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.upload 2 | 3 | import kr.genti.domain.repository.UploadRepository 4 | import javax.inject.Inject 5 | 6 | class UploadImageToBucketUseCase @Inject constructor( 7 | private val uploadRepository: UploadRepository, 8 | ) { 9 | suspend operator fun invoke( 10 | bucketUrl: String?, 11 | imageUrl: String?, 12 | ): Result = 13 | runCatching { 14 | val response = uploadRepository.uploadImage( 15 | preSignedURL = requireNotNull(bucketUrl) { "bucketUrl is null" }, 16 | imageUri = requireNotNull(imageUrl) { "imageUrl is null" }, 17 | ) 18 | return@runCatching response 19 | } 20 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/verify/CheckUserVerifiedUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.verify 2 | 3 | import kr.genti.domain.repository.GenerateRepository 4 | import javax.inject.Inject 5 | 6 | class CheckUserVerifiedUseCase @Inject constructor( 7 | private val generateRepository: GenerateRepository, 8 | ) { 9 | suspend operator fun invoke(): Result = 10 | runCatching { 11 | val isVerified = generateRepository.getIsUserVerified() 12 | return@runCatching isVerified 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/verify/CheckVerifyImageUploadedUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.verify 2 | 3 | import kr.genti.domain.entity.request.KeyRequestModel 4 | import kr.genti.domain.repository.CreateRepository 5 | import javax.inject.Inject 6 | 7 | class CheckVerifyImageUploadedUseCase @Inject constructor( 8 | private val createRepository: CreateRepository, 9 | ) { 10 | suspend operator fun invoke( 11 | bucketKey: String?, 12 | ): Result = 13 | runCatching { 14 | val request = KeyRequestModel(bucketKey) 15 | val isSuccess = createRepository.postToVerify(request) 16 | if (isSuccess) { 17 | return@runCatching true 18 | } else { 19 | throw Exception("checking verify image in bucket failed") 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /domain/src/main/kotlin/kr/genti/domain/usecase/verify/GetVerifyImageBucketUseCase.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.domain.usecase.verify 2 | 3 | import kr.genti.domain.entity.request.ImageBucketRequestModel 4 | import kr.genti.domain.entity.response.ImageBucketModel 5 | import kr.genti.domain.enums.FileType 6 | import kr.genti.domain.repository.CreateRepository 7 | import javax.inject.Inject 8 | 9 | class GetVerifyImageBucketUseCase @Inject constructor( 10 | private val createRepository: CreateRepository, 11 | ) { 12 | suspend operator fun invoke( 13 | imageName: String?, 14 | isDebugMode: Boolean, 15 | ): Result = 16 | runCatching { 17 | val request = ImageBucketRequestModel( 18 | fileType = if (isDebugMode) FileType.DEV_USER_VERIFICATION_IMAGE else FileType.USER_VERIFICATION_IMAGE, 19 | fileName = requireNotNull(imageName) { "imageName is null" }, 20 | ) 21 | val response = createRepository.getSingleImageBucket(request) 22 | return@runCatching response 23 | } 24 | } -------------------------------------------------------------------------------- /feature/feed/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidCompose") 3 | } 4 | 5 | android { 6 | namespace = "kr.genti.feature.feed" 7 | } 8 | 9 | dependencies { 10 | implementation(projects.domain) 11 | implementation(projects.core.common) 12 | implementation(projects.core.navigation) 13 | implementation(projects.core.designsystem) 14 | } -------------------------------------------------------------------------------- /feature/feed/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/feature/feed/consumer-rules.pro -------------------------------------------------------------------------------- /feature/feed/src/androidTest/java/kr/genti/feed/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.feed 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("kr.genti.feed.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /feature/feed/src/main/java/kr/genti/feed/FeedIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.feed 2 | 3 | sealed class FeedIntent { 4 | data object Init: FeedIntent() 5 | data object Refresh: FeedIntent() 6 | data object InfoBtnClick: FeedIntent() 7 | data object TooltipClick: FeedIntent() 8 | data object ListScroll: FeedIntent() 9 | data object BottomSheetDismiss: FeedIntent() 10 | data object MoreBtnClick: FeedIntent() 11 | } -------------------------------------------------------------------------------- /feature/feed/src/main/java/kr/genti/feed/FeedSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.feed 2 | 3 | sealed class FeedSideEffect { 4 | data object ShowErrorToast : FeedSideEffect() 5 | data class NavigateToWebsite(val url: String) : FeedSideEffect() 6 | } -------------------------------------------------------------------------------- /feature/feed/src/main/java/kr/genti/feed/FeedState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.feed 2 | 3 | import kotlinx.collections.immutable.ImmutableList 4 | import kotlinx.collections.immutable.persistentListOf 5 | import kr.genti.domain.entity.response.FeedItemModel 6 | 7 | data class FeedState( 8 | val itemList: ImmutableList = persistentListOf(), 9 | val isRefreshing: Boolean = false, 10 | val isTooltipVisible: Boolean = false, 11 | val isTooltipClosed: Boolean = false, 12 | val isBottomSheetVisible: Boolean = false, 13 | val isLoading: Boolean = false, 14 | ) -------------------------------------------------------------------------------- /feature/feed/src/main/java/kr/genti/feed/component/FeedBottomTooltip.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.feed.component 2 | 3 | import androidx.compose.animation.AnimatedVisibility 4 | import androidx.compose.animation.fadeIn 5 | import androidx.compose.animation.fadeOut 6 | import androidx.compose.foundation.Image 7 | import androidx.compose.foundation.layout.Box 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.foundation.layout.width 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.res.painterResource 13 | import androidx.compose.ui.tooling.preview.Preview 14 | import androidx.compose.ui.unit.dp 15 | import kr.genti.common.extension.noRippleClickable 16 | import kr.genti.core.designsystem.R 17 | 18 | @Composable 19 | internal fun FeedBottomTooltip( 20 | modifier: Modifier = Modifier, 21 | isTooltipVisible: Boolean = false, 22 | onTooltipClick: () -> Unit = {} 23 | ) { 24 | Box(modifier = modifier) { 25 | AnimatedVisibility( 26 | visible = isTooltipVisible, 27 | enter = fadeIn(), 28 | exit = fadeOut() 29 | ) { 30 | Image( 31 | painter = painterResource(R.drawable.img_tooltip_feed), 32 | contentDescription = null, 33 | modifier = Modifier 34 | .width(174.dp) 35 | .padding(bottom = 16.dp) 36 | .noRippleClickable { onTooltipClick() } 37 | ) 38 | } 39 | } 40 | } 41 | 42 | @Preview 43 | @Composable 44 | private fun FeedBottomTooltipPreview() { 45 | FeedBottomTooltip(isTooltipVisible = true) 46 | } -------------------------------------------------------------------------------- /feature/feed/src/main/java/kr/genti/feed/navigation/FeedNavigation.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.feed.navigation 2 | 3 | import androidx.compose.foundation.layout.PaddingValues 4 | import androidx.navigation.NavController 5 | import androidx.navigation.NavGraphBuilder 6 | import androidx.navigation.NavOptions 7 | import androidx.navigation.compose.composable 8 | import kr.genti.feed.FeedRoute 9 | import kr.genti.navigation.MainTabRoute 10 | 11 | fun NavController.navigateToFeed( 12 | navOptions: NavOptions? = null 13 | ) { 14 | navigate(MainTabRoute.Feed, navOptions) 15 | } 16 | 17 | fun NavGraphBuilder.feedNavGraph( 18 | paddingValues: PaddingValues 19 | ) { 20 | composable { 21 | FeedRoute(paddingValues) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /feature/generate/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidCompose") 3 | } 4 | 5 | android { 6 | namespace = "kr.genti.feature.generate" 7 | } 8 | 9 | dependencies { 10 | implementation(projects.domain) 11 | implementation(projects.core.common) 12 | implementation(projects.core.navigation) 13 | implementation(projects.core.designsystem) 14 | 15 | implementation(libs.billing.client) 16 | } -------------------------------------------------------------------------------- /feature/generate/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/feature/generate/consumer-rules.pro -------------------------------------------------------------------------------- /feature/generate/src/androidTest/java/kr/genti/generate/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.generate 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("kr.genti.generate.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /feature/generate/src/main/java/kr/genti/generate/GenerateIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.generate 2 | 3 | import android.net.Uri 4 | import com.android.billingclient.api.Purchase 5 | import kr.genti.domain.enums.PictureNumber 6 | import kr.genti.domain.enums.PictureRatio 7 | 8 | sealed class GenerateIntent { 9 | data class Init(val isParentPic: Boolean) : GenerateIntent() 10 | data object BackBtnClick : GenerateIntent() 11 | data object NextBtnClick : GenerateIntent() 12 | data class NumberSelect(val pictureNumber: PictureNumber) : GenerateIntent() 13 | data object PromptExampleSwipe : GenerateIntent() 14 | data class PromptChange(val prompt: String) : GenerateIntent() 15 | data class RatioSelect(val pictureRatio: PictureRatio) : GenerateIntent() 16 | data class TextFieldFocused(val isFocused: Boolean) : GenerateIntent() 17 | data class ImageSelectBtnClick(val isExtra: Boolean) : GenerateIntent() 18 | data class ImageSelect(val uriList: List) : GenerateIntent() 19 | data class PurchaseSuccess(val purchase: Purchase) : GenerateIntent() 20 | data object PurchaseFailure : GenerateIntent() 21 | } -------------------------------------------------------------------------------- /feature/generate/src/main/java/kr/genti/generate/GenerateSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.generate 2 | 3 | sealed class GenerateSideEffect { 4 | data object ShowErrorToast : GenerateSideEffect() 5 | data class NavigateToWaiting(val isParentPic: Boolean) : GenerateSideEffect() 6 | data object NavigateToBack : GenerateSideEffect() 7 | data object StartImageSelect : GenerateSideEffect() 8 | data object StartPurchaseProduct : GenerateSideEffect() 9 | } -------------------------------------------------------------------------------- /feature/generate/src/main/java/kr/genti/generate/GenerateState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.generate 2 | 3 | import kotlinx.collections.immutable.ImmutableList 4 | import kotlinx.collections.immutable.persistentListOf 5 | import kr.genti.domain.entity.response.ImageFileModel 6 | import kr.genti.domain.entity.response.PromptExampleModel 7 | import kr.genti.domain.enums.PictureNumber 8 | import kr.genti.domain.enums.PictureRatio 9 | import kr.genti.generate.model.GenerateStage 10 | import kr.genti.generate.model.GenerateType 11 | import kr.genti.generate.model.GenerateType.Companion.getGenerateType 12 | 13 | data class GenerateState( 14 | val currentStage: GenerateStage = GenerateStage.INIT, 15 | val currentStep: Int = 0, 16 | val isParentPic: Boolean = false, 17 | val isRequestLoading: Boolean = false, 18 | val isBillingLoading: Boolean = false, 19 | val pictureNumber: PictureNumber = PictureNumber.NONE, 20 | val exampleList: ImmutableList = persistentListOf(), 21 | val prompt: String = "", 22 | val isFocusClearNeeded: Boolean = false, 23 | val pictureRatio: PictureRatio = PictureRatio.NONE, 24 | val isSelectingExtra: Boolean = false, 25 | val imageList: List = listOf(), 26 | val extraImageList: List = listOf(), 27 | ) { 28 | val progress: Float 29 | get() = currentStep / if (!isParentPic) 3F else 4F 30 | 31 | val generateType: GenerateType 32 | get() = getGenerateType(isParentPic, pictureNumber) 33 | } -------------------------------------------------------------------------------- /feature/generate/src/main/java/kr/genti/generate/billing/BillingCallback.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.generate.billing 2 | 3 | import com.android.billingclient.api.Purchase 4 | 5 | interface BillingCallback { 6 | fun onBillingSuccess(purchase: Purchase) 7 | fun onBillingFailure(responseCode: Int) 8 | } -------------------------------------------------------------------------------- /feature/generate/src/main/java/kr/genti/generate/component/GenerateProgressBar.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.generate.component 2 | 3 | import androidx.compose.animation.core.animateFloatAsState 4 | import androidx.compose.animation.core.tween 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.material3.LinearProgressIndicator 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.runtime.getValue 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.tooling.preview.Preview 11 | import androidx.compose.ui.unit.dp 12 | import kr.genti.designsystem.theme.GentiGreen 13 | import kr.genti.designsystem.theme.GentiTheme 14 | import kr.genti.designsystem.theme.White20 15 | 16 | @Composable 17 | internal fun GenerateProgressBar( 18 | modifier: Modifier = Modifier, 19 | progress: Float = 0f, 20 | ) { 21 | val animatedProgress by animateFloatAsState( 22 | targetValue = progress, 23 | animationSpec = tween(durationMillis = 300), 24 | label = "" 25 | ) 26 | 27 | LinearProgressIndicator( 28 | progress = { animatedProgress }, 29 | modifier = modifier.fillMaxWidth(), 30 | color = GentiGreen, 31 | trackColor = White20, 32 | gapSize = 0.dp, 33 | drawStopIndicator = { } 34 | ) 35 | } 36 | 37 | @Preview 38 | @Composable 39 | private fun GenerateProgressBarPreview() { 40 | GentiTheme { 41 | GenerateProgressBar( 42 | progress = 0.33F 43 | ) 44 | } 45 | } -------------------------------------------------------------------------------- /feature/generate/src/main/java/kr/genti/generate/model/GenerateStage.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.generate.model 2 | 3 | import kr.genti.domain.enums.PictureNumber 4 | 5 | enum class GenerateStage { 6 | INIT, NUMBER_SELECT, PROMPT_INPUT, RATIO_SELECT, IMAGE_THREE_SELECT, IMAGE_SIX_SELECT, RESULT; 7 | 8 | fun prevStage(isParentPic: Boolean): GenerateStage = when (this) { 9 | NUMBER_SELECT -> INIT 10 | PROMPT_INPUT -> if (isParentPic) NUMBER_SELECT else INIT 11 | RATIO_SELECT -> PROMPT_INPUT 12 | IMAGE_THREE_SELECT -> RATIO_SELECT 13 | IMAGE_SIX_SELECT -> RATIO_SELECT 14 | else -> RESULT 15 | } 16 | 17 | fun nextStage(pictureNumber: PictureNumber): GenerateStage = when (this) { 18 | NUMBER_SELECT -> PROMPT_INPUT 19 | PROMPT_INPUT -> RATIO_SELECT 20 | RATIO_SELECT -> if (pictureNumber != PictureNumber.TWO) IMAGE_THREE_SELECT else IMAGE_SIX_SELECT 21 | else -> RESULT 22 | } 23 | } -------------------------------------------------------------------------------- /feature/generate/src/main/java/kr/genti/generate/model/GenerateType.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.generate.model 2 | 3 | import kr.genti.domain.enums.PictureNumber 4 | 5 | enum class GenerateType { 6 | FREE_ONE, 7 | PAID_ONE, 8 | PAID_TWO, 9 | NONE; 10 | 11 | companion object { 12 | fun getGenerateType(isParent: Boolean, pictureNumber: PictureNumber): GenerateType = 13 | when { 14 | isParent && pictureNumber == PictureNumber.TWO -> PAID_TWO 15 | isParent -> PAID_ONE 16 | else -> FREE_ONE 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /feature/generate/src/main/java/kr/genti/generate/navigation/GenerateNavigation.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.generate.navigation 2 | 3 | import androidx.navigation.NavController 4 | import androidx.navigation.NavGraphBuilder 5 | import androidx.navigation.NavOptions 6 | import androidx.navigation.compose.composable 7 | import androidx.navigation.toRoute 8 | import kr.genti.generate.GenerateRoute 9 | import kr.genti.navigation.GenerateRoute 10 | 11 | fun NavController.navigateToGenerate( 12 | isParentPic: Boolean, 13 | navOptions: NavOptions? = null 14 | ) { 15 | navigate(GenerateRoute.Generate(isParentPic), navOptions) 16 | } 17 | 18 | fun NavGraphBuilder.generateNavGraph( 19 | navigateToWaiting: (Boolean) -> Unit = {}, 20 | navigateToBack: () -> Unit = {}, 21 | ) { 22 | composable { backStackEntry -> 23 | val items = backStackEntry.toRoute() 24 | GenerateRoute( 25 | isParentPic = items.isParentPic, 26 | navigateToWaiting = navigateToWaiting, 27 | navigateToBack = navigateToBack 28 | ) 29 | } 30 | } -------------------------------------------------------------------------------- /feature/main/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidCompose") 3 | } 4 | 5 | android { 6 | namespace = "kr.genti.feature.main" 7 | } 8 | 9 | dependencies { 10 | implementation(projects.domain) 11 | implementation(projects.core.common) 12 | implementation(projects.core.navigation) 13 | implementation(projects.core.designsystem) 14 | implementation(projects.feature.feed) 15 | implementation(projects.feature.profile) 16 | implementation(projects.feature.onboarding) 17 | implementation(projects.feature.generate) 18 | implementation(projects.feature.result) 19 | implementation(projects.feature.setting) 20 | 21 | implementation(platform(libs.firebase.bom)) 22 | implementation(libs.bundles.firebase) 23 | } 24 | -------------------------------------------------------------------------------- /feature/main/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/feature/main/consumer-rules.pro -------------------------------------------------------------------------------- /feature/main/src/androidTest/java/kr/genti/main/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.main 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("kr.genti.main.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /feature/main/src/main/java/kr/genti/main/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.main 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import androidx.activity.ComponentActivity 7 | import androidx.activity.compose.setContent 8 | import androidx.activity.enableEdgeToEdge 9 | import dagger.hilt.android.AndroidEntryPoint 10 | import kr.genti.designsystem.theme.GentiTheme 11 | import kr.genti.main.navigation.rememberMainNavigator 12 | 13 | @AndroidEntryPoint 14 | class MainActivity : ComponentActivity() { 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | 18 | enableEdgeToEdge() 19 | 20 | val intentData = intent.getStringExtra(EXTRA_TYPE) 21 | val isFromNotification = intentData != null 22 | 23 | setContent { 24 | GentiTheme { 25 | val navigator = rememberMainNavigator(isFromNotification = isFromNotification) 26 | MainRoute(navigator = navigator, intentData = intentData) 27 | } 28 | } 29 | } 30 | 31 | companion object { 32 | private const val EXTRA_TYPE = "EXTRA_DEFAULT" 33 | 34 | @JvmStatic 35 | fun getIntent( 36 | context: Context, 37 | type: String? = null, 38 | ) = Intent(context, MainActivity::class.java).apply { 39 | putExtra(EXTRA_TYPE, type) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /feature/main/src/main/java/kr/genti/main/MainIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.main 2 | 3 | import kr.genti.main.navigation.MainTab 4 | 5 | sealed class MainIntent { 6 | data class TabSelect(val tab: MainTab) : MainIntent() 7 | data object GenerateBtnClick : MainIntent() 8 | data class PushAlarmReceived(val type: String?) : MainIntent() 9 | data class NetworkChangeMonitored(val isConnected: Boolean) : MainIntent() 10 | data object DialogDismiss : MainIntent() 11 | data object RegenerateDialogBtnClick : MainIntent() 12 | data object FinishedDialogBtnClick : MainIntent() 13 | data class SelectDialogBtnClick(val isParentPic: Boolean) : MainIntent() 14 | data object DebugPatchBtnClick : MainIntent() 15 | } -------------------------------------------------------------------------------- /feature/main/src/main/java/kr/genti/main/MainSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.main 2 | 3 | import kr.genti.main.navigation.MainTab 4 | 5 | sealed class MainSideEffect { 6 | data object ShowErrorToast : MainSideEffect() 7 | data object ShowStatusChangedToast : MainSideEffect() 8 | data class NavigateToTab(val tab: MainTab) : MainSideEffect() 9 | data class NavigateToGenerate(val isParentPic: Boolean) : MainSideEffect() 10 | data object NavigateToVerify : MainSideEffect() 11 | data class NavigateToWaiting(val isParentPic: Boolean) : MainSideEffect() 12 | data class NavigateToFinished( 13 | val responseId: Long, 14 | val imageUrl: String, 15 | val isGaro: Boolean, 16 | val isParentPic: Boolean 17 | ) : MainSideEffect() 18 | } -------------------------------------------------------------------------------- /feature/main/src/main/java/kr/genti/main/MainState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.main 2 | 3 | import kr.genti.domain.entity.response.GenerateStatusModel 4 | import kr.genti.domain.entity.response.GenerateStatusModel.Companion.emptyGenerateStatusModel 5 | import kr.genti.domain.enums.GenerateStatus 6 | 7 | data class MainState( 8 | val currentGenerateStatus: GenerateStatus = GenerateStatus.EMPTY, 9 | val isNotificationReceived: Boolean = false, 10 | val serverUnableMessage: String = "", 11 | val generatedImage: GenerateStatusModel = emptyGenerateStatusModel(), 12 | val isFinishedDialogVisible: Boolean = false, 13 | val isErrorDialogVisible: Boolean = false, 14 | val isUnableDialogVisible: Boolean = false, 15 | val isSelectDialogVisible: Boolean = false, 16 | val isNetworkDialogVisible: Boolean = false 17 | ) -------------------------------------------------------------------------------- /feature/main/src/main/java/kr/genti/main/component/GenerateForceButton.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.main.component 2 | 3 | import androidx.compose.animation.AnimatedVisibility 4 | import androidx.compose.animation.fadeIn 5 | import androidx.compose.animation.fadeOut 6 | import androidx.compose.animation.slideIn 7 | import androidx.compose.animation.slideOut 8 | import androidx.compose.foundation.Image 9 | import androidx.compose.foundation.layout.navigationBarsPadding 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.res.painterResource 14 | import androidx.compose.ui.unit.IntOffset 15 | import androidx.compose.ui.unit.dp 16 | import kr.genti.common.extension.noRippleClickable 17 | import kr.genti.core.designsystem.R 18 | 19 | @Composable 20 | internal fun GenerateForceButton( 21 | modifier: Modifier = Modifier, 22 | isVisible: Boolean = false, 23 | onDebugPatchBtnClicked: () -> Unit = {} 24 | ) { 25 | AnimatedVisibility( 26 | visible = isVisible, 27 | enter = fadeIn() + slideIn { IntOffset(0, it.height) }, 28 | exit = fadeOut() + slideOut { IntOffset(0, it.height) }, 29 | modifier = modifier 30 | .noRippleClickable { onDebugPatchBtnClicked() } 31 | .navigationBarsPadding() 32 | .padding(bottom = 90.dp) 33 | ) { 34 | Image( 35 | painter = painterResource(id = R.drawable.ic_download), 36 | contentDescription = null, 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /feature/main/src/main/java/kr/genti/main/component/MainBottomBtn.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.main.component 2 | 3 | import androidx.compose.animation.AnimatedVisibility 4 | import androidx.compose.animation.fadeIn 5 | import androidx.compose.animation.fadeOut 6 | import androidx.compose.animation.slideIn 7 | import androidx.compose.animation.slideOut 8 | import androidx.compose.foundation.Image 9 | import androidx.compose.foundation.layout.navigationBarsPadding 10 | import androidx.compose.foundation.layout.offset 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.res.painterResource 14 | import androidx.compose.ui.unit.IntOffset 15 | import androidx.compose.ui.unit.dp 16 | import kr.genti.common.extension.noRippleClickable 17 | import kr.genti.feature.main.R 18 | 19 | @Composable 20 | internal fun MainBottomBtn( 21 | modifier: Modifier = Modifier, 22 | visible: Boolean = true, 23 | onButtonClick: () -> Unit = {}, 24 | ) { 25 | AnimatedVisibility( 26 | visible = visible, 27 | enter = fadeIn() + slideIn { IntOffset(0, it.height) }, 28 | exit = fadeOut() + slideOut { IntOffset(0, it.height) }, 29 | modifier = modifier 30 | .navigationBarsPadding() 31 | .offset(y = 24.dp) 32 | ) { 33 | Image( 34 | painter = painterResource(R.drawable.menu_create), 35 | contentDescription = null, 36 | modifier = Modifier.noRippleClickable { onButtonClick() } 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /feature/main/src/main/java/kr/genti/main/navigation/MainTab.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.main.navigation 2 | 3 | import androidx.annotation.DrawableRes 4 | import androidx.compose.runtime.Composable 5 | import kr.genti.feature.main.R 6 | import kr.genti.navigation.MainTabRoute 7 | import kr.genti.navigation.Route 8 | 9 | enum class MainTab( 10 | val title: String, 11 | @DrawableRes val selectedIconResource: Int, 12 | @DrawableRes val unselectedIconResource: Int, 13 | val route: MainTabRoute, 14 | ) { 15 | FEED( 16 | "피드", 17 | R.drawable.menu_feed_selected, 18 | R.drawable.menu_feed_unselected, 19 | MainTabRoute.Feed 20 | ), 21 | GENERATE( 22 | "생성", 23 | R.drawable.menu_feed_selected, 24 | R.drawable.menu_feed_unselected, 25 | MainTabRoute.Generate 26 | ), 27 | PROFILE( 28 | "프로필", 29 | R.drawable.menu_profile_selected, 30 | R.drawable.menu_profile_unselected, 31 | MainTabRoute.Profile 32 | ); 33 | 34 | companion object { 35 | @Composable 36 | fun find(predicate: @Composable (MainTabRoute) -> Boolean): MainTab? { 37 | return entries.find { predicate(it.route) } 38 | } 39 | 40 | @Composable 41 | fun contains(predicate: @Composable (Route) -> Boolean): Boolean { 42 | return entries.map { it.route }.any { predicate(it) } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /feature/main/src/main/res/drawable/menu_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/feature/main/src/main/res/drawable/menu_create.png -------------------------------------------------------------------------------- /feature/main/src/main/res/drawable/menu_feed_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /feature/main/src/main/res/drawable/menu_feed_unselected.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /feature/main/src/main/res/drawable/menu_profile_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /feature/main/src/main/res/drawable/menu_profile_unselected.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /feature/onboarding/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidCompose") 3 | } 4 | 5 | android { 6 | namespace = "kr.genti.feature.onboarding" 7 | } 8 | 9 | dependencies { 10 | implementation(projects.domain) 11 | implementation(projects.core.common) 12 | implementation(projects.core.navigation) 13 | implementation(projects.core.designsystem) 14 | 15 | implementation(libs.kakao) 16 | implementation(platform(libs.firebase.bom)) 17 | implementation(libs.bundles.firebase) 18 | implementation(libs.phoenix) 19 | } -------------------------------------------------------------------------------- /feature/onboarding/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/feature/onboarding/consumer-rules.pro -------------------------------------------------------------------------------- /feature/onboarding/src/androidTest/java/kr/genti/onboarding/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding 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("kr.genti.onboarding.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/component/TutorialFadeInBackground.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.component 2 | 3 | import androidx.compose.animation.AnimatedVisibility 4 | import androidx.compose.animation.core.tween 5 | import androidx.compose.animation.fadeIn 6 | import androidx.compose.animation.fadeOut 7 | import androidx.compose.foundation.Image 8 | import androidx.compose.foundation.layout.fillMaxWidth 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.res.painterResource 12 | import kr.genti.core.designsystem.R 13 | import kr.genti.onboarding.model.TutorialStage 14 | 15 | @Composable 16 | fun TutorialFadeInBackground( 17 | modifier: Modifier = Modifier, 18 | currentStage: TutorialStage = TutorialStage.FIRST, 19 | ) { 20 | AnimatedVisibility( 21 | visible = currentStage == TutorialStage.THIRD, 22 | enter = fadeIn(animationSpec = tween(durationMillis = 500)), 23 | exit = fadeOut(), 24 | modifier = modifier.fillMaxWidth() 25 | ) { 26 | Image( 27 | painter = painterResource(id = R.drawable.img_onboarding_third), 28 | contentDescription = null, 29 | modifier = Modifier.fillMaxWidth() 30 | ) 31 | } 32 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/login/LoginIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.login 2 | 3 | sealed class LoginIntent { 4 | data class Init(val isAppLoginAvailable: Boolean) : LoginIntent() 5 | data object LoginBtnClick : LoginIntent() 6 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/login/LoginSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.login 2 | 3 | import com.kakao.sdk.auth.model.OAuthToken 4 | 5 | sealed class LoginSideEffect { 6 | data object ShowErrorToast : LoginSideEffect() 7 | 8 | data object NavigateToSignup : LoginSideEffect() 9 | 10 | data object NavigateToFeed : LoginSideEffect() 11 | 12 | data class StartKakaoAppLogin( 13 | val appLoginCallback: (OAuthToken?, Throwable?) -> Unit 14 | ) : LoginSideEffect() 15 | 16 | data class StartKakaoWebLogin( 17 | val webLoginCallback: (OAuthToken?, Throwable?) -> Unit 18 | ) : LoginSideEffect() 19 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/login/LoginState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.login 2 | 3 | data class LoginState( 4 | val isAppLoginAvailable: Boolean = false, 5 | val isLoading: Boolean = false, 6 | ) -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/model/TutorialStage.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.model 2 | 3 | enum class TutorialStage { 4 | FIRST, SECOND, THIRD; 5 | 6 | companion object { 7 | fun getTutorialList() = entries 8 | } 9 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/signup/SignupIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.signup 2 | 3 | import kr.genti.domain.enums.Gender 4 | 5 | sealed class SignupIntent { 6 | data object Init : SignupIntent() 7 | data class GenderSelect(val gender: Gender) : SignupIntent() 8 | data class YearChange(val year: String) : SignupIntent() 9 | data class TextFieldFocused(val isFocused: Boolean) : SignupIntent() 10 | data object SignupBtnClick : SignupIntent() 11 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/signup/SignupSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.signup 2 | 3 | sealed class SignupSideEffect { 4 | data object ShowErrorToast : SignupSideEffect() 5 | data object NavigateToTutorial : SignupSideEffect() 6 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/signup/SignupState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.signup 2 | 3 | import kr.genti.domain.enums.Gender 4 | 5 | data class SignupState( 6 | val selectedGender: Gender = Gender.NONE, 7 | val selectedYear: String = "", 8 | val isGenderSelected: Boolean = false, 9 | val isYearSelected: Boolean = false, 10 | val isAllSelected: Boolean = false, 11 | val isFocusClearNeeded: Boolean = false, 12 | val isLoading: Boolean = false, 13 | ) -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/splash/SplashIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.splash 2 | 3 | sealed class SplashIntent { 4 | data object Init : SplashIntent() 5 | data object LottiePlayFinish : SplashIntent() 6 | data class AppUpdateFinish(val isAppUpdateSuccess: Boolean) : SplashIntent() 7 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/splash/SplashSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.splash 2 | 3 | sealed class SplashSideEffect { 4 | data object ShowErrorToast : SplashSideEffect() 5 | data object NavigateToLogin : SplashSideEffect() 6 | data object NavigateToFeed : SplashSideEffect() 7 | data object StartAppUpdate : SplashSideEffect() 8 | data object RestartApp : SplashSideEffect() 9 | data object FinishApp : SplashSideEffect() 10 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/splash/SplashState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.splash 2 | 3 | data class SplashState( 4 | val isLottieFinished: Boolean = false, 5 | val isCheckFinished: Boolean = false, 6 | val isUserSigned: Boolean = false, 7 | ) -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/tutorial/TutorialIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.tutorial 2 | 3 | sealed class TutorialIntent { 4 | data object NextBtnClick : TutorialIntent() 5 | data object CloseBtnClick : TutorialIntent() 6 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/tutorial/TutorialSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.tutorial 2 | 3 | sealed class TutorialSideEffect { 4 | data object NavigateToFeed : TutorialSideEffect() 5 | } -------------------------------------------------------------------------------- /feature/onboarding/src/main/java/kr/genti/onboarding/tutorial/TutorialState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding.tutorial 2 | 3 | import kr.genti.onboarding.model.TutorialStage 4 | 5 | data class TutorialState( 6 | val currentStage: TutorialStage = TutorialStage.FIRST, 7 | ) -------------------------------------------------------------------------------- /feature/onboarding/src/test/java/kr/genti/onboarding/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.onboarding 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 | } -------------------------------------------------------------------------------- /feature/profile/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidCompose") 3 | } 4 | 5 | android { 6 | namespace = "kr.genti.feature.profile" 7 | } 8 | 9 | dependencies { 10 | implementation(projects.domain) 11 | implementation(projects.core.common) 12 | implementation(projects.core.navigation) 13 | implementation(projects.core.designsystem) 14 | } -------------------------------------------------------------------------------- /feature/profile/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/feature/profile/consumer-rules.pro -------------------------------------------------------------------------------- /feature/profile/src/androidTest/java/kr/genti/profile/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.profile 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("kr.genti.profile.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /feature/profile/src/main/java/kr/genti/profile/ProfileIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.profile 2 | 3 | import kr.genti.domain.entity.response.ImageModel 4 | 5 | sealed class ProfileIntent { 6 | data object Init: ProfileIntent() 7 | data object Refresh: ProfileIntent() 8 | data class ImageItemClick(val item: ImageModel): ProfileIntent() 9 | data object GenerateBtnClick: ProfileIntent() 10 | data object SettingBtnClick: ProfileIntent() 11 | data object LastColumnLoaded: ProfileIntent() 12 | data object SaveBtnClick: ProfileIntent() 13 | data object ShareBtnClick: ProfileIntent() 14 | data object DialogDismiss: ProfileIntent() 15 | } -------------------------------------------------------------------------------- /feature/profile/src/main/java/kr/genti/profile/ProfileSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.profile 2 | 3 | import android.net.Uri 4 | 5 | sealed class ProfileSideEffect { 6 | data object ShowErrorToast : ProfileSideEffect() 7 | data object ShowDownloadToast : ProfileSideEffect() 8 | data object NavigateToGenerate : ProfileSideEffect() 9 | data object NavigateToSetting : ProfileSideEffect() 10 | data object StartPermissionLauncher : ProfileSideEffect() 11 | data class NavigateToShare(val imageUri: Uri) : ProfileSideEffect() 12 | } -------------------------------------------------------------------------------- /feature/profile/src/main/java/kr/genti/profile/ProfileState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.profile 2 | 3 | import kotlinx.collections.immutable.ImmutableList 4 | import kotlinx.collections.immutable.persistentListOf 5 | import kr.genti.domain.entity.response.ImageModel 6 | 7 | data class ProfileState( 8 | val itemList: ImmutableList = persistentListOf(), 9 | val totalPage: Int = 0, 10 | val currentPage: Int = -1, 11 | val isPagingFinish: Boolean = false, 12 | val detailImageId: Long = -1L, 13 | val detailImageUrl: String = "", 14 | val isDetailImageGaro: Boolean = false, 15 | val isDetailDialogShown: Boolean = false, 16 | val isGenerating: Boolean = false, 17 | val isLoading: Boolean = false, 18 | val isRefreshing: Boolean = false 19 | ) -------------------------------------------------------------------------------- /feature/profile/src/main/java/kr/genti/profile/component/ProfileGenerateItem.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.profile.component 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.aspectRatio 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.layout.ContentScale 8 | import androidx.compose.ui.res.painterResource 9 | import androidx.compose.ui.tooling.preview.Preview 10 | import kr.genti.common.extension.noRippleClickable 11 | import kr.genti.core.designsystem.R 12 | 13 | @Composable 14 | internal fun ProfileGenerateItem( 15 | modifier: Modifier = Modifier, 16 | isGenerating: Boolean = false, 17 | onBtnClick: () -> Unit = {}, 18 | ) { 19 | Image( 20 | painter = painterResource(id = if (!isGenerating) R.drawable.img_profile_create_active else R.drawable.img_profile_create_inactive), 21 | contentDescription = null, 22 | contentScale = ContentScale.Crop, 23 | modifier = modifier 24 | .aspectRatio(1F) 25 | .noRippleClickable { onBtnClick() } 26 | ) 27 | } 28 | 29 | @Preview 30 | @Composable 31 | private fun ProfileGenerateItemPreviewActive() { 32 | ProfileGenerateItem( 33 | isGenerating = false 34 | ) 35 | } 36 | 37 | @Preview 38 | @Composable 39 | private fun ProfileGenerateItemPreviewInactive() { 40 | ProfileGenerateItem( 41 | isGenerating = true 42 | ) 43 | } -------------------------------------------------------------------------------- /feature/profile/src/main/java/kr/genti/profile/component/ProfileGeneratingBanner.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.profile.component 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.res.painterResource 8 | import kr.genti.core.designsystem.R 9 | 10 | @Composable 11 | internal fun ProfileGenerationBanner( 12 | modifier: Modifier = Modifier, 13 | isGenerating: Boolean = false 14 | ) { 15 | if (isGenerating) { 16 | Image( 17 | painter = painterResource(R.drawable.img_profile_making), 18 | contentDescription = null, 19 | modifier = modifier.fillMaxWidth() 20 | ) 21 | } 22 | } -------------------------------------------------------------------------------- /feature/profile/src/main/java/kr/genti/profile/navigation/ProfileNavigation.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.profile.navigation 2 | 3 | import androidx.compose.foundation.layout.PaddingValues 4 | import androidx.navigation.NavController 5 | import androidx.navigation.NavGraphBuilder 6 | import androidx.navigation.NavOptions 7 | import androidx.navigation.compose.composable 8 | import kr.genti.navigation.MainTabRoute 9 | import kr.genti.profile.ProfileRoute 10 | 11 | fun NavController.navigateToProfile( 12 | navOptions: NavOptions? = null 13 | ) { 14 | navigate(MainTabRoute.Profile, navOptions) 15 | } 16 | 17 | fun NavGraphBuilder.profileNavGraph( 18 | paddingValues: PaddingValues, 19 | navigateToGenerate: () -> Unit = {}, 20 | navigateToSetting: () -> Unit = {} 21 | ) { 22 | composable { 23 | ProfileRoute( 24 | paddingValues = paddingValues, 25 | navigateToGenerate = navigateToGenerate, 26 | navigateToSetting = navigateToSetting 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /feature/result/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidCompose") 3 | } 4 | 5 | android { 6 | namespace = "kr.genti.feature.result" 7 | } 8 | 9 | dependencies { 10 | implementation(projects.domain) 11 | implementation(projects.core.common) 12 | implementation(projects.core.navigation) 13 | implementation(projects.core.designsystem) 14 | } -------------------------------------------------------------------------------- /feature/result/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/feature/result/consumer-rules.pro -------------------------------------------------------------------------------- /feature/result/src/androidTest/java/kr/genti/result/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result 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("kr.genti.result.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /feature/result/src/main/java/kr/genti/result/finished/FinishedIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result.finished 2 | 3 | sealed class FinishedIntent { 4 | data class Init(val responseId: Long, val isParentPic: Boolean, val imageUrl: String) : 5 | FinishedIntent() 6 | 7 | data object ImageClick : FinishedIntent() 8 | data object BackButtonClick : FinishedIntent() 9 | data object ReportButtonClick : FinishedIntent() 10 | data object ShareButtonClick : FinishedIntent() 11 | data object DownloadButtonClick : FinishedIntent() 12 | data object DialogDismiss : FinishedIntent() 13 | data class ReportTextChange(val text: String) : FinishedIntent() 14 | data object ReportSubmitButtonClick : FinishedIntent() 15 | data object FinishButtonClick : FinishedIntent() 16 | data class RatingChange(val rating: Int) : FinishedIntent() 17 | data object RatingSubmitButtonClick : FinishedIntent() 18 | data object RatingSkipButtonClick : FinishedIntent() 19 | } -------------------------------------------------------------------------------- /feature/result/src/main/java/kr/genti/result/finished/FinishedSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result.finished 2 | 3 | import android.net.Uri 4 | 5 | sealed class FinishedSideEffect { 6 | data object ShowErrorToast : FinishedSideEffect() 7 | data object ShowDownloadToast : FinishedSideEffect() 8 | data object NavigateToBack : FinishedSideEffect() 9 | data object StartPermissionLauncher : FinishedSideEffect() 10 | data class NavigateToShare(val imageUri: Uri) : FinishedSideEffect() 11 | } -------------------------------------------------------------------------------- /feature/result/src/main/java/kr/genti/result/finished/FinishedState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result.finished 2 | 3 | data class FinishedState( 4 | val responseId: Long = -1, 5 | val isParentPic: Boolean = false, 6 | val imageUrl: String = "", 7 | val reportText: String = "", 8 | val rating: Int = 5, 9 | val isReportSubmitted: Boolean = false, 10 | val isDetailDialogVisible: Boolean = false, 11 | val isReportDialogVisible: Boolean = false, 12 | val isRatingDialogVisible: Boolean = false, 13 | ) -------------------------------------------------------------------------------- /feature/result/src/main/java/kr/genti/result/verify/VerifyIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result.verify 2 | 3 | sealed class VerifyIntent { 4 | data class CameraButtonClick(val isFirst: Boolean) : VerifyIntent() 5 | data object CameraPermissionGrant : VerifyIntent() 6 | data object CameraResultSuccess : VerifyIntent() 7 | data object FinishButtonClick : VerifyIntent() 8 | data object BackButtonClick : VerifyIntent() 9 | data object ExitButtonClick : VerifyIntent() 10 | data object ExitDialogDismiss : VerifyIntent() 11 | } -------------------------------------------------------------------------------- /feature/result/src/main/java/kr/genti/result/verify/VerifySideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result.verify 2 | 3 | sealed class VerifySideEffect { 4 | data object ShowErrorToast : VerifySideEffect() 5 | data object NavigateToBack : VerifySideEffect() 6 | data object StartPermissionLauncher : VerifySideEffect() 7 | data object StartCameraLauncher : VerifySideEffect() 8 | data object VerifySuccess: VerifySideEffect() 9 | } -------------------------------------------------------------------------------- /feature/result/src/main/java/kr/genti/result/verify/VerifyState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result.verify 2 | 3 | import android.net.Uri 4 | 5 | data class VerifyState( 6 | val isPhotoTaken: Boolean = false, 7 | val imageUri: Uri? = null, 8 | val imageName: String? = null, 9 | val isLoading: Boolean = false, 10 | val isExitDialogVisible: Boolean = false, 11 | ) -------------------------------------------------------------------------------- /feature/result/src/main/java/kr/genti/result/waiting/WaitingIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result.waiting 2 | 3 | sealed class WaitingIntent { 4 | data class Init(val isParentPic: Boolean) : WaitingIntent() 5 | data object ReturnButtonClick : WaitingIntent() 6 | data class AlarmPermissionNeeded(val isNeeded: Boolean) : WaitingIntent() 7 | data class NavigatedToSetting(val isNavigated: Boolean) : WaitingIntent() 8 | data object AlarmDialogRequestButtonClick : WaitingIntent() 9 | data object AlarmDialogReturnButtonClick : WaitingIntent() 10 | data object AlarmRequestGrant : WaitingIntent() 11 | data object AlarmDialogDismiss : WaitingIntent() 12 | } -------------------------------------------------------------------------------- /feature/result/src/main/java/kr/genti/result/waiting/WaitingSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result.waiting 2 | 3 | sealed class WaitingSideEffect { 4 | data object NavigateToBack : WaitingSideEffect() 5 | data object CheckPermission : WaitingSideEffect() 6 | data object StartPermissionLauncher : WaitingSideEffect() 7 | data object GrantPermission : WaitingSideEffect() 8 | } -------------------------------------------------------------------------------- /feature/result/src/main/java/kr/genti/result/waiting/WaitingState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result.waiting 2 | 3 | data class WaitingState( 4 | val isParentPic: Boolean = false, 5 | val isAlarmDialogVisible: Boolean = false, 6 | val isNavigatedToSetting: Boolean = false, 7 | ) -------------------------------------------------------------------------------- /feature/result/src/test/java/kr/genti/result/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.result 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 | } -------------------------------------------------------------------------------- /feature/setting/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kr.genti.androidCompose") 3 | id("kr.genti.version") 4 | } 5 | 6 | android { 7 | namespace = "kr.genti.feature.setting" 8 | 9 | defaultConfig { 10 | buildConfigField("String", "VERSION_NAME", "\"${extra["versionName"]}\"") 11 | buildConfigField("String", "VERSION_CODE", "\"${extra["versionCode"]}\"") 12 | } 13 | 14 | buildFeatures { 15 | buildConfig = true 16 | } 17 | } 18 | 19 | dependencies { 20 | implementation(projects.domain) 21 | implementation(projects.core.common) 22 | implementation(projects.core.navigation) 23 | implementation(projects.core.designsystem) 24 | 25 | implementation(libs.phoenix) 26 | } 27 | -------------------------------------------------------------------------------- /feature/setting/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/feature/setting/consumer-rules.pro -------------------------------------------------------------------------------- /feature/setting/src/androidTest/java/kr/genti/setting/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.setting 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("kr.genti.setting.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /feature/setting/src/main/java/kr/genti/setting/SettingIntent.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.setting 2 | 3 | sealed class SettingIntent { 4 | data object BackButtonClick : SettingIntent() 5 | data object TermButtonClick : SettingIntent() 6 | data object PrivacyButtonClick : SettingIntent() 7 | data object CompanyButtonClick : SettingIntent() 8 | data object QuestionButtonClick : SettingIntent() 9 | data object LogoutButtonClick : SettingIntent() 10 | data object QuitButtonClick : SettingIntent() 11 | data object LogoutDialogDismiss : SettingIntent() 12 | data object QuitDialogDismiss : SettingIntent() 13 | data object LogoutRequest : SettingIntent() 14 | data object QuitRequest : SettingIntent() 15 | } -------------------------------------------------------------------------------- /feature/setting/src/main/java/kr/genti/setting/SettingSideEffect.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.setting 2 | 3 | sealed class SettingSideEffect { 4 | data object ShowErrorToast : SettingSideEffect() 5 | data object NavigateToBack : SettingSideEffect() 6 | data class NavigateToWeb(val url: String) : SettingSideEffect() 7 | data object RestartApp : SettingSideEffect() 8 | } -------------------------------------------------------------------------------- /feature/setting/src/main/java/kr/genti/setting/SettingState.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.setting 2 | 3 | data class SettingState( 4 | val isLogoutDialogVisible: Boolean = false, 5 | val isQuitDialogVisible: Boolean = false, 6 | ) -------------------------------------------------------------------------------- /feature/setting/src/main/java/kr/genti/setting/navigation/SettingNavigation.kt: -------------------------------------------------------------------------------- 1 | package kr.genti.setting.navigation 2 | 3 | import androidx.navigation.NavController 4 | import androidx.navigation.NavGraphBuilder 5 | import androidx.navigation.NavOptions 6 | import androidx.navigation.compose.composable 7 | import kr.genti.navigation.SettingRoute 8 | import kr.genti.setting.SettingRoute 9 | 10 | fun NavController.navigateToSetting( 11 | navOptions: NavOptions? = null 12 | ) { 13 | navigate(SettingRoute.Setting, navOptions) 14 | } 15 | 16 | fun NavGraphBuilder.settingNavGraph( 17 | navigateToBack: () -> Unit = {} 18 | ) { 19 | composable { 20 | SettingRoute( 21 | navigateToBack = navigateToBack 22 | ) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /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. For more details, visit 12 | # https://developer.android.com/r/tools/gradle-multi-project-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/Genti2024/Genti-Android/22801f3c40931e51c3df07457ddb2c78c0c3ecf6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed May 01 02:58:14 KST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 2 | 3 | pluginManagement { 4 | 5 | includeBuild("build-logic") 6 | 7 | repositories { 8 | google { 9 | content { 10 | includeGroupByRegex("com\\.android.*") 11 | includeGroupByRegex("com\\.google.*") 12 | includeGroupByRegex("androidx.*") 13 | } 14 | } 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | } 19 | 20 | dependencyResolutionManagement { 21 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 22 | repositories { 23 | google() 24 | mavenCentral() 25 | 26 | // KakaoSDK repository 27 | maven(url = "https://devrepo.kakao.com/nexus/content/groups/public/") 28 | } 29 | } 30 | 31 | rootProject.name = "Genti-Android" 32 | 33 | include(":app") 34 | include(":domain") 35 | include(":data") 36 | include(":core:common") 37 | include(":core:datastore") 38 | include(":core:designsystem") 39 | include(":core:navigation") 40 | include(":core:network") 41 | include(":feature:onboarding") 42 | include(":feature:feed") 43 | include(":feature:main") 44 | include(":feature:profile") 45 | include(":feature:generate") 46 | include(":feature:result") 47 | include(":feature:setting") 48 | --------------------------------------------------------------------------------