├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ └── 이슈-생성-템플릿.md
├── PULL_REQUEST_TEMPLATE.md
├── actions
│ └── setup
│ │ └── action.yml
└── workflows
│ └── ktlint and build.yml
├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── kr
│ │ └── boostcamp_2024
│ │ └── course
│ │ └── wequiz
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── kr
│ │ │ └── boostcamp_2024
│ │ │ └── course
│ │ │ └── wequiz
│ │ │ ├── WeQuizApplication.kt
│ │ │ └── ui
│ │ │ ├── MainActivity.kt
│ │ │ ├── WeQuizApp.kt
│ │ │ └── WeQuizNavHost.kt
│ └── res
│ │ ├── drawable
│ │ └── ic_launcher_foreground.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values-ko
│ │ └── strings.xml
│ │ ├── values-zh
│ │ └── strings.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ └── test
│ └── java
│ └── kr
│ └── boostcamp_2024
│ └── course
│ └── wequiz
│ └── ExampleUnitTest.kt
├── build-logic
├── .gitignore
├── build.gradle.kts
├── gradle.properties
├── settings.gradle.kts
└── src
│ └── main
│ └── java
│ ├── convention.android.application.gradle.kts
│ ├── convention.android.compose.gradle.kts
│ ├── convention.android.feature.gradle.kts
│ ├── convention.android.hilt.gradle.kts
│ ├── convention.android.library.gradle.kts
│ ├── convention.firebase.gradle.kts
│ └── kr
│ └── boostcamp_2024
│ └── course
│ └── build_logic
│ ├── ComposeAndroid.kt
│ ├── Extension.kt
│ ├── Firebase.kt
│ ├── HiltAndroid.kt
│ └── KotlinAndroid.kt
├── build.gradle.kts
├── core
├── data
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── kr
│ │ │ └── boostcamp_2024
│ │ │ └── course
│ │ │ └── data
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── kr
│ │ │ └── boostcamp_2024
│ │ │ └── course
│ │ │ └── data
│ │ │ ├── di
│ │ │ ├── AddHeadInterceptor.kt
│ │ │ ├── AppModule.kt
│ │ │ └── RepositoryModule.kt
│ │ │ ├── model
│ │ │ ├── AiQuestionRequest.kt
│ │ │ ├── AiQuestionResponse.kt
│ │ │ ├── BlankQuestionDTO.kt
│ │ │ ├── CategoryDTO.kt
│ │ │ ├── ChoiceQuestionDTO.kt
│ │ │ ├── NotificationDTO.kt
│ │ │ ├── QuizDTO.kt
│ │ │ ├── RealTimeQuizDTO.kt
│ │ │ ├── StudyGroupDTO.kt
│ │ │ ├── UserDTO.kt
│ │ │ └── UserOmrDTO.kt
│ │ │ ├── network
│ │ │ └── AiService.kt
│ │ │ └── repository
│ │ │ ├── AiRepositoryImpl.kt
│ │ │ ├── AuthRepositoryImpl.kt
│ │ │ ├── CategoryRepositoryImpl.kt
│ │ │ ├── GuideRepositoryImpl.kt
│ │ │ ├── NotificationRepositoryImpl.kt
│ │ │ ├── QuestionRepositoryImpl.kt
│ │ │ ├── QuizRepositoryImpl.kt
│ │ │ ├── StorageRepositoryImpl.kt
│ │ │ ├── StudyGroupRepositoryImpl.kt
│ │ │ ├── UserOmrRepositoryImpl.kt
│ │ │ └── UserRepositoryImpl.kt
│ │ └── test
│ │ └── java
│ │ └── kr
│ │ └── boostcamp_2024
│ │ └── course
│ │ └── data
│ │ └── ExampleUnitTest.kt
├── designsystem
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── kr
│ │ │ └── boostcamp_2024
│ │ │ └── course
│ │ │ └── designsystem
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── kr
│ │ │ │ └── boostcamp_2024
│ │ │ │ └── course
│ │ │ │ └── designsystem
│ │ │ │ └── ui
│ │ │ │ ├── annotation
│ │ │ │ └── PreviewAnnotations.kt
│ │ │ │ └── theme
│ │ │ │ ├── Colors.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── component
│ │ │ │ ├── WeQuizBaseDialog.kt
│ │ │ │ ├── WeQuizChatBubble.kt
│ │ │ │ ├── WeQuizImageLargeTopAppBar.kt
│ │ │ │ ├── WeQuizLoadingIndicator.kt
│ │ │ │ ├── WeQuizPhotoPicker.kt
│ │ │ │ ├── WeQuizRoundedImage.kt
│ │ │ │ ├── WeQuizTextField.kt
│ │ │ │ └── WeQuizValidateTextField.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ ├── img_error.xml
│ │ │ ├── img_photo_picker.xml
│ │ │ └── outline_cancel_24.xml
│ │ │ ├── values-ko
│ │ │ └── strings.xml
│ │ │ ├── values-zh
│ │ │ └── strings.xml
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── kr
│ │ └── boostcamp_2024
│ │ └── course
│ │ └── designsystem
│ │ └── ExampleUnitTest.kt
└── domain
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ ├── main
│ └── java
│ │ └── kr
│ │ └── boostcamp_2024
│ │ └── course
│ │ └── domain
│ │ ├── enum
│ │ ├── BlankContentType.kt
│ │ ├── QuestionType.kt
│ │ └── QuizType.kt
│ │ ├── model
│ │ ├── AiQuestion.kt
│ │ ├── BlankQuestion.kt
│ │ ├── BlankQuestionCreationInfo.kt
│ │ ├── BlankQuestionManager.kt
│ │ ├── Category.kt
│ │ ├── ChoiceQuestion.kt
│ │ ├── ChoiceQuestionCreationInfo.kt
│ │ ├── Notification.kt
│ │ ├── NotificationWithGroupInfo.kt
│ │ ├── Quiz.kt
│ │ ├── QuizCreationInfo.kt
│ │ ├── QuizNotFoundException.kt
│ │ ├── QuizResult.kt
│ │ ├── RealtimeQuiz.kt
│ │ ├── StudyGroup.kt
│ │ ├── StudyGroupCreationInfo.kt
│ │ ├── StudyGroupUpdatedInfo.kt
│ │ ├── User.kt
│ │ ├── UserOmr.kt
│ │ ├── UserOmrCreationInfo.kt
│ │ └── UserSubmitInfo.kt
│ │ └── repository
│ │ ├── AiRepository.kt
│ │ ├── AuthRepository.kt
│ │ ├── CategoryRepository.kt
│ │ ├── GuideRepository.kt
│ │ ├── NotificationRepository.kt
│ │ ├── QuestionRepository.kt
│ │ ├── QuizRepository.kt
│ │ ├── StorageRepository.kt
│ │ ├── StudyGroupRepository.kt
│ │ ├── UserOmrRepository.kt
│ │ └── UserRepository.kt
│ └── test
│ └── java
│ └── BlankQuestionQuizWithAnswerTest.kt
├── feature
├── category
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── kr
│ │ │ └── boostcamp_2024
│ │ │ └── course
│ │ │ └── category
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── kr
│ │ │ │ └── boostcamp_2024
│ │ │ │ └── course
│ │ │ │ └── category
│ │ │ │ ├── component
│ │ │ │ └── CategorySettingMenu.kt
│ │ │ │ ├── navigation
│ │ │ │ └── CategoryNavigation.kt
│ │ │ │ ├── presentation
│ │ │ │ ├── CategoryScreen.kt
│ │ │ │ └── CreateCategoryScreen.kt
│ │ │ │ ├── viewModel
│ │ │ │ └── CategoryViewModel.kt
│ │ │ │ └── viewmodel
│ │ │ │ └── CreateCategoryViewModel.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ ├── default_profile_image.xml
│ │ │ ├── sample_profile.png
│ │ │ └── sample_profile1.png
│ │ │ ├── values-ko
│ │ │ └── strings.xml
│ │ │ ├── values-zh
│ │ │ └── strings.xml
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── kr
│ │ └── boostcamp_2024
│ │ └── course
│ │ └── category
│ │ └── ExampleUnitTest.kt
├── login
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── kr
│ │ │ └── boostcamp_2024
│ │ │ └── course
│ │ │ └── login
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── kr
│ │ │ │ └── boostcamp_2024
│ │ │ │ └── course
│ │ │ │ └── login
│ │ │ │ ├── model
│ │ │ │ └── UserUiModel.kt
│ │ │ │ ├── navigation
│ │ │ │ ├── CustomNavType.kt
│ │ │ │ └── LoginNavigation.kt
│ │ │ │ ├── presentation
│ │ │ │ ├── LoginScreen.kt
│ │ │ │ └── SignUpScreen.kt
│ │ │ │ └── viewmodel
│ │ │ │ ├── LoginViewModel.kt
│ │ │ │ └── SignUpViewModel.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ ├── baseline_visibility_24.xml
│ │ │ ├── baseline_visibility_off_24.xml
│ │ │ ├── img_app_logo.png
│ │ │ ├── img_google_light_btn_login.xml
│ │ │ └── outline_cancel_on_surface_variant.xml
│ │ │ ├── values-ko
│ │ │ └── strings.xml
│ │ │ ├── values-zh
│ │ │ └── strings.xml
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── kr
│ │ └── boostcamp_2024
│ │ └── course
│ │ └── login
│ │ └── ExampleUnitTest.kt
├── main
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── kr
│ │ │ └── boostcamp_2024
│ │ │ └── course
│ │ │ └── main
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── kr
│ │ │ │ └── boostcamp_2024
│ │ │ │ └── course
│ │ │ │ └── main
│ │ │ │ ├── component
│ │ │ │ ├── BaseGuideScreen.kt
│ │ │ │ ├── GuideDialog.kt
│ │ │ │ ├── MainDropDownMenu.kt
│ │ │ │ ├── NotificationItem.kt
│ │ │ │ ├── NotificationTopAppBar.kt
│ │ │ │ └── StudyGroupItem.kt
│ │ │ │ ├── navigation
│ │ │ │ └── MainNavigation.kt
│ │ │ │ ├── presentation
│ │ │ │ ├── MainScreen.kt
│ │ │ │ └── NotificationScreen.kt
│ │ │ │ └── viewmodel
│ │ │ │ ├── MainViewModel.kt
│ │ │ │ └── NotificationViewModel.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── sample_profile1.png
│ │ │ ├── raw
│ │ │ └── anim_arrow.json
│ │ │ ├── values-ko
│ │ │ └── strings.xml
│ │ │ ├── values-zh
│ │ │ └── strings.xml
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── kr
│ │ └── boostcamp_2024
│ │ └── course
│ │ └── main
│ │ └── ExampleUnitTest.kt
├── quiz
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── kr
│ │ │ └── boostcamp_2024
│ │ │ └── course
│ │ │ └── quiz
│ │ │ ├── CreateQuestionScreenTest.kt
│ │ │ ├── QuestionDetailScreenTest.kt
│ │ │ └── QuizDataButtonTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── kr
│ │ │ │ └── boostcamp_2024
│ │ │ │ └── course
│ │ │ │ └── quiz
│ │ │ │ ├── component
│ │ │ │ ├── BlankQuestionContent.kt
│ │ │ │ ├── BlankQuestionContentUi.kt
│ │ │ │ ├── BlankQuestionDescription.kt
│ │ │ │ ├── ChoiceQuestionContent.kt
│ │ │ │ ├── CreateBlankQuestionContent.kt
│ │ │ │ ├── CreateChoiceItems.kt
│ │ │ │ ├── CreateQuestionContent.kt
│ │ │ │ ├── Question.kt
│ │ │ │ ├── QuestionConent.kt
│ │ │ │ ├── QuestionDescription.kt
│ │ │ │ ├── QuestionDetailTopAppBar.kt
│ │ │ │ ├── QuestionItems.kt
│ │ │ │ ├── QuestionSolution.kt
│ │ │ │ ├── QuestionTextBox.kt
│ │ │ │ ├── QuestionTitle.kt
│ │ │ │ ├── QuestionTitleAndDetail.kt
│ │ │ │ ├── QuestionTopBar.kt
│ │ │ │ ├── QuizAiDialog.kt
│ │ │ │ ├── QuizDataButton.kt
│ │ │ │ ├── QuizDataChip.kt
│ │ │ │ ├── QuizDatePickerTextField.kt
│ │ │ │ ├── QuizOwnerDialog.kt
│ │ │ │ ├── QuizSolveTimeSlider.kt
│ │ │ │ ├── QuizTopAppBar.kt
│ │ │ │ ├── RadioTextButton.kt
│ │ │ │ ├── RealTimeQuestion.kt
│ │ │ │ └── RealTimeQuizGuideContent.kt
│ │ │ │ ├── navigation
│ │ │ │ └── QuizNavigation.kt
│ │ │ │ ├── presentation
│ │ │ │ ├── question
│ │ │ │ │ ├── AiLoadingIndicator.kt
│ │ │ │ │ ├── CreateQuestionScreen.kt
│ │ │ │ │ ├── GeneralQuestionScreen.kt
│ │ │ │ │ ├── OwnerQuestionScreen.kt
│ │ │ │ │ ├── PieChartScreen.kt
│ │ │ │ │ ├── QuestionDetailScreen.kt
│ │ │ │ │ ├── QuestionScreen.kt
│ │ │ │ │ └── UserQuestionScreen.kt
│ │ │ │ └── quiz
│ │ │ │ │ ├── CreateQuizScreen.kt
│ │ │ │ │ ├── GeneralQuizResultScreen.kt
│ │ │ │ │ ├── OwnerQuizResultScreen.kt
│ │ │ │ │ ├── QuizResultScreen.kt
│ │ │ │ │ ├── QuizScreen.kt
│ │ │ │ │ └── QuizStatisticsDialog.kt
│ │ │ │ ├── utils
│ │ │ │ ├── QuestionParameterProvider.kt
│ │ │ │ ├── QuizContentPreviewParameterProvider.kt
│ │ │ │ ├── QuizParameterProvider.kt
│ │ │ │ └── TimerFormat.kt
│ │ │ │ └── viewmodel
│ │ │ │ ├── CreateQuestionViewModel.kt
│ │ │ │ ├── CreateQuizViewModel.kt
│ │ │ │ ├── OwnerQuestionViewModel.kt
│ │ │ │ ├── QuestionDetailViewModel.kt
│ │ │ │ ├── QuestionViewModel.kt
│ │ │ │ ├── QuizResultViewModel.kt
│ │ │ │ ├── QuizViewModel.kt
│ │ │ │ └── UserQuestionViewModel.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ ├── baseline_dehaze_24.xml
│ │ │ ├── create_study_character.png
│ │ │ ├── edit_24.xml
│ │ │ ├── image_ai.xml
│ │ │ ├── image_guide.xml
│ │ │ ├── img_clock_character.png
│ │ │ ├── outline_cancel_on_surface_variant.xml
│ │ │ ├── quiz_create_ai_profile.png
│ │ │ ├── quiz_system_profile.png
│ │ │ ├── rounded_directions_walk_24.xml
│ │ │ ├── sample_profile.png
│ │ │ ├── sample_profile1.png
│ │ │ └── search_24.xml
│ │ │ ├── raw
│ │ │ └── anim_ai.json
│ │ │ ├── values-ko
│ │ │ └── strings.xml
│ │ │ ├── values-zh
│ │ │ └── strings.xml
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── kr
│ │ └── boostcamp_2024
│ │ └── course
│ │ └── quiz
│ │ └── ExampleUnitTest.kt
└── study
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ ├── androidTest
│ └── java
│ │ └── kr
│ │ └── boostcamp_2024
│ │ └── course
│ │ └── study
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── kr
│ │ │ └── boostcamp_2024
│ │ │ └── course
│ │ │ └── study
│ │ │ ├── component
│ │ │ ├── CategoryItem.kt
│ │ │ ├── CreateStudyTopAppBar.kt
│ │ │ ├── CustomPropertyTab.kt
│ │ │ ├── StudyGroupMemberItem.kt
│ │ │ └── StudySubmitButton.kt
│ │ │ ├── navigation
│ │ │ ├── DetailScreenRouter.kt
│ │ │ └── StudyNavigation.kt
│ │ │ ├── presentation
│ │ │ ├── CategoryListScreen.kt
│ │ │ ├── CreateGroupDialog.kt
│ │ │ ├── CreateStudyScreen.kt
│ │ │ ├── DetailStudyScreen.kt
│ │ │ ├── GroupListScreen.kt
│ │ │ └── StudyScreen.kt
│ │ │ └── viewmodel
│ │ │ ├── CreateStudyViewModel.kt
│ │ │ └── DetailStudyViewModel.kt
│ └── res
│ │ ├── drawable
│ │ ├── baseline_account_circle_24.xml
│ │ ├── baseline_add_circle_outline_24.xml
│ │ ├── baseline_arrow_back_24.xml
│ │ ├── baseline_dehaze_24.xml
│ │ ├── baseline_remove_24.xml
│ │ ├── baseline_settings_24.xml
│ │ ├── create_study_character.png
│ │ ├── img_photo_picker.xml
│ │ ├── member_invite_character.png
│ │ └── rounded_directions_walk_24.xml
│ │ ├── values-ko
│ │ └── strings.xml
│ │ ├── values-zh
│ │ └── strings.xml
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── kr
│ └── boostcamp_2024
│ └── course
│ └── study
│ └── ExampleUnitTest.kt
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | max_line_length = 300
8 | trim_trailing_whitespace = true
9 | ktlint_function_naming_ignore_when_annotated_with = Composable, Test
10 | ktlint_standard_package-name = disabled
11 |
12 | [*.{kt,kts}]
13 | ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
14 | ij_kotlin_name_count_to_use_star_import = 2147483647
15 | ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
16 | ij_kotlin_packages_to_use_import_on_demand = unset
17 |
18 | ktlint_code_style = ktlint_official
19 | ktlint_standard_string-template-indent = disabled
20 | ktlint_standard_multiline-expression-wrapping = disabled
21 | ktlint_standard_max-line-length = disabled
22 | ktlint_standard_no-wildcard-imports = disabled
23 | ktlint_standard_annotation = disabled
24 | ktlint_standard_function-signature = disabled
25 | ktlint_standard_import-ordering = disabled
26 | ktlint_standard_value-argument-comment = disabled
27 | ktlint_standard_comment-spacing = disabled
28 | ktlint_standard_no-empty-first-line-in-method-block = disabled
29 | ktlint_standard_no-blank-line-before-rbrace = disabled
30 | ktlint_no-multi-spaces = disabled
31 | ij_kotlin_allow_trailing_comma = true
32 | ij_kotlin_allow_trailing_comma_on_call_site = true
33 | ktlint_standard_multiline_expression_wrapping = disabled
34 | ktlint_standard_value-parameter-comment = disabled
35 | ktlint_standard_chain-method-continuation = disabled
36 |
37 | [*.{yml,yaml}]
38 | indent_size = 2
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/이슈-생성-템플릿.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 이슈 생성 템플릿
3 | about: 작업 전 이슈 생성 템플릿입니다!
4 | title: 'Issue: 이슈 생성 템플릿'
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ### 🪴 기능 설명
11 | (진행할 작업을 설명해주세요.)
12 |
13 | ### 📷 참고 이미지
14 | (작업 참고 디자인을 첨부해주세요.)
15 |
16 |
17 |
18 | ### 🗣️ 필요 태스크
19 | (해당 작업에 대한 상세 체크 리스트를 작성해주세요.)
20 | - [ ] 이거 해야 해요!!
21 | - [ ]
22 | - [ ]
23 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: PR 생성 템플릿
3 | about: PR 작성 템플릿입니다!
4 | title: 'feat: 작업 제목'
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ### 👩🌾 진행한 작업
11 | ✅ 로그인 기능 구현
12 | (필요에 따라 구체적인 과정을 추가해주세요!)
13 |
14 | ### 📷 작업 결과(사진)
15 |
16 |
17 | ### 🗣️ 공유할 내용
18 | (해당 작업에 대해 전달할 내용을 적어주세요!)
19 |
20 | closed #이슈 번호
21 |
--------------------------------------------------------------------------------
/.github/actions/setup/action.yml:
--------------------------------------------------------------------------------
1 | name: setup
2 |
3 | inputs:
4 | base-url:
5 | description: 'Base URL for the AI API'
6 | required: true
7 | post-url:
8 | description: 'Post URL for the AI API'
9 | required: true
10 | google-services-json:
11 | description: 'Google Services JSON'
12 | required: true
13 | x-ncp-clovastudio-api-key:
14 | description: 'X-NCP-CLOVASTUDIO-API-KEY'
15 | required: true
16 | x-ncp-apigw-api-key:
17 | description: 'X-NCP-APIGW-API-KEY'
18 | required: true
19 | x-ncp-clovastudio-request-id:
20 | description: 'X-NCP-CLOVASTUDIO-REQUEST-ID'
21 | required: true
22 |
23 | runs:
24 | using: "composite"
25 | steps:
26 | - name: Set up JDK 17
27 | uses: actions/setup-java@v4
28 | with:
29 | java-version: '17'
30 | distribution: 'temurin'
31 | cache: gradle
32 |
33 | - name: Gradle Caching
34 | uses: actions/cache@v4
35 | with:
36 | path: |
37 | ~/.gradle/caches
38 | ~/.gradle/wrapper
39 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
40 | restore-keys: |
41 | ${{ runner.os }}-gradle-
42 |
43 | - name: Decode Google Services JSON
44 | env:
45 | GOOGLE_SERVICES_JSON: ${{ inputs.google-services-json }}
46 | run: echo $GOOGLE_SERVICES_JSON > ./app/google-services.json
47 | shell: bash
48 |
49 | - name: Configure local.properties
50 | env:
51 | BASE_URL: ${{ inputs.base-url }}
52 | POST_URL: ${{ inputs.post-url }}
53 | X_NCP_CLOVASTUDIO_API_KEY: ${{ inputs.x-ncp-clovastudio-api-key }}
54 | X_NCP_APIGW_API_KEY: ${{ inputs.x-ncp-apigw-api-key }}
55 | X_NCP_CLOVASTUDIO_REQUEST_ID: ${{ inputs.x-ncp-clovastudio-request-id }}
56 | run: |
57 | echo "base_url=${BASE_URL}" >> local.properties
58 | echo "post_url=${POST_URL}" >> local.properties
59 | echo "X-NCP-CLOVASTUDIO-API-KEY=${X_NCP_CLOVASTUDIO_API_KEY}" >> local.properties
60 | echo "X-NCP-APIGW-API-KEY=${X_NCP_APIGW_API_KEY}" >> local.properties
61 | echo "X-NCP-CLOVASTUDIO-REQUEST-ID=${X_NCP_CLOVASTUDIO_REQUEST_ID}" >> local.properties
62 | shell: bash
63 |
--------------------------------------------------------------------------------
/.github/workflows/ktlint and build.yml:
--------------------------------------------------------------------------------
1 | name: ktlint and build
2 |
3 | on:
4 | pull_request:
5 | branches: [ "main", "dev" ]
6 |
7 | jobs:
8 | build:
9 | name: Check Code Quality and Build
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v4
14 |
15 | - uses: ./.github/actions/setup
16 | with:
17 | base-url: ${{ secrets.BASE_URL }}
18 | post-url: ${{ secrets.POST_URL }}
19 | google-services-json: ${{ secrets.GOOGLE_SERVICES_JSON }}
20 | x-ncp-clovastudio-api-key: ${{ secrets.X_NCP_CLOVASTUDIO_API_KEY }}
21 | x-ncp-apigw-api-key: ${{ secrets.X_NCP_APIGW_API_KEY }}
22 | x-ncp-clovastudio-request-id: ${{ secrets.X_NCP_CLOVASTUDIO_REQUEST_ID }}
23 |
24 | - name: Ktlint Check
25 | uses: ScaCap/action-ktlint@master
26 | with:
27 | github_token: ${{ secrets.GITHUB_TOKEN }}
28 | reporter: github-pr-review
29 | android: true
30 | fail_on_error: true
31 | level: warning
32 |
33 | - name: Build and run unit tests
34 | run: |
35 | ./gradlew build --daemon
36 | ./gradlew test
37 |
38 | androidTest:
39 | name: Run Instrumentation Test
40 | runs-on: ubuntu-latest
41 | strategy:
42 | matrix:
43 | api-level: [ 26, 35 ]
44 |
45 | steps:
46 | - uses: actions/checkout@v4
47 |
48 | - uses: ./.github/actions/setup
49 | with:
50 | base-url: ${{ secrets.BASE_URL }}
51 | post-url: ${{ secrets.POST_URL }}
52 | google-services-json: ${{ secrets.GOOGLE_SERVICES_JSON }}
53 | x-ncp-clovastudio-api-key: ${{ secrets.X_NCP_CLOVASTUDIO_API_KEY }}
54 | x-ncp-apigw-api-key: ${{ secrets.X_NCP_APIGW_API_KEY }}
55 | x-ncp-clovastudio-request-id: ${{ secrets.X_NCP_CLOVASTUDIO_REQUEST_ID }}
56 |
57 | - name: Enable KVM
58 | run: |
59 | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
60 | sudo udevadm control --reload-rules
61 | sudo udevadm trigger --name-match=kvm
62 |
63 | - name: Run instrumentation tests
64 | uses: reactivecircus/android-emulator-runner@v2
65 | with:
66 | api-level: ${{ matrix.api-level }}
67 | arch: x86_64
68 | script: ./gradlew connectedDebugAndroidTest
69 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /google-services.json
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("convention.android.application")
3 | }
4 |
5 | android {
6 | namespace = "kr.boostcamp_2024.course.wequiz"
7 |
8 | defaultConfig {
9 | applicationId = "kr.boostcamp_2024.course.wequiz"
10 | targetSdk = 35
11 | versionCode = 6
12 | versionName = "1.2.1"
13 | }
14 | }
15 |
16 | dependencies {
17 | implementation(project(":feature:login"))
18 | implementation(project(":feature:main"))
19 | implementation(project(":feature:study"))
20 | implementation(project(":feature:category"))
21 | implementation(project(":feature:quiz"))
22 |
23 | implementation(project(":core:data"))
24 | implementation(project(":core:domain"))
25 | implementation(project(":core:designsystem"))
26 |
27 | implementation(libs.androidx.core.ktx)
28 | implementation(libs.androidx.lifecycle.runtime.ktx)
29 |
30 | testImplementation(libs.junit)
31 | androidTestImplementation(libs.androidx.junit)
32 | androidTestImplementation(libs.androidx.ui.test.junit4)
33 | }
34 |
--------------------------------------------------------------------------------
/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/boostcamp_2024/course/wequiz/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.wequiz
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.boostcamp_2024.course.wequiz", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/java/kr/boostcamp_2024/course/wequiz/WeQuizApplication.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.wequiz
2 |
3 | import android.app.Application
4 | import com.google.firebase.FirebaseApp
5 | import dagger.hilt.android.HiltAndroidApp
6 |
7 | @HiltAndroidApp
8 | class WeQuizApplication : Application() {
9 | override fun onCreate() {
10 | super.onCreate()
11 | FirebaseApp.initializeApp(this)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/kr/boostcamp_2024/course/wequiz/ui/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.wequiz.ui
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.activity.enableEdgeToEdge
7 | import dagger.hilt.android.AndroidEntryPoint
8 |
9 | @AndroidEntryPoint
10 | class MainActivity : ComponentActivity() {
11 | override fun onCreate(savedInstanceState: Bundle?) {
12 | super.onCreate(savedInstanceState)
13 | enableEdgeToEdge()
14 | setContent {
15 | WeQuizApp()
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/kr/boostcamp_2024/course/wequiz/ui/WeQuizApp.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.wequiz.ui
2 |
3 | import androidx.compose.foundation.layout.fillMaxSize
4 | import androidx.compose.material3.SnackbarHostState
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.runtime.remember
7 | import androidx.compose.runtime.rememberCoroutineScope
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.platform.LocalContext
10 | import com.google.firebase.firestore.FirebaseFirestoreException
11 | import kotlinx.coroutines.launch
12 | import kr.boostcamp_2024.course.designsystem.ui.theme.WeQuizTheme
13 | import kr.boostcamp_2024.course.wequiz.R
14 |
15 | @Composable
16 | fun WeQuizApp() {
17 | val snackbarHostState = remember { SnackbarHostState() }
18 | val coroutineScope = rememberCoroutineScope()
19 | val localContextResource = LocalContext.current.resources
20 | val onShowErrorSnackbar: (throwable: Throwable) -> Unit = { throwable ->
21 | coroutineScope.launch {
22 | snackbarHostState.showSnackbar(
23 | when (throwable) {
24 | is FirebaseFirestoreException ->
25 | when (throwable.code) {
26 | FirebaseFirestoreException.Code.PERMISSION_DENIED -> localContextResource.getString(R.string.permission_denied_error_message)
27 | else -> throwable.message ?: localContextResource.getString(R.string.default_error_message)
28 | }
29 |
30 | else -> throwable.message ?: localContextResource.getString(R.string.default_error_message)
31 | },
32 | )
33 | }
34 | }
35 |
36 | WeQuizTheme {
37 | WeQuizNavHost(
38 | snackbarHostState = snackbarHostState,
39 | modifier = Modifier
40 | .fillMaxSize(),
41 | onShowErrorSnackbar = onShowErrorSnackbar,
42 | )
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values-ko/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 오류가 발생했습니다.\n다시 시도해 주세요.
4 | 해당 기능은 회원가입 후 사용해주세요.
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 发生错误\n请重试。
4 | 请注册后使用此功能。
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #F5FBF6
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | wequiz
3 | An Error occured.\nPlease try again.
4 | Please sign up to use this feature.
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/test/java/kr/boostcamp_2024/course/wequiz/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.wequiz
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/build-logic/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/build-logic/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | }
4 |
5 | dependencies {
6 | implementation(libs.android.gradlePlugin)
7 | implementation(libs.android.desugarJdkLibs)
8 | implementation(libs.kotlin.gradlePlugin)
9 | compileOnly(libs.compose.compiler.gradle.plugin)
10 | }
--------------------------------------------------------------------------------
/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 | dependencyResolutionManagement {
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 | versionCatalogs {
8 | create("libs") {
9 | from(files("../gradle/libs.versions.toml"))
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/build-logic/src/main/java/convention.android.application.gradle.kts:
--------------------------------------------------------------------------------
1 | import kr.boostcamp_2024.course.build_logic.androidExtension
2 | import kr.boostcamp_2024.course.build_logic.configureKotlinAndroid
3 | import kr.boostcamp_2024.course.build_logic.libs
4 |
5 | plugins {
6 | id("com.android.application")
7 | id("convention.android.compose")
8 | id("convention.android.hilt")
9 | id("convention.firebase")
10 | }
11 |
12 | configureKotlinAndroid()
13 |
14 | androidExtension.apply {
15 | pluginManager.apply("org.jetbrains.kotlin.plugin.serialization")
16 |
17 | dependencies {
18 | val libs = project.extensions.libs
19 | implementation(libs.findLibrary("androidx.navigation.compose").get())
20 | implementation(libs.findLibrary("kotlinx.serialization.json").get())
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/build-logic/src/main/java/convention.android.compose.gradle.kts:
--------------------------------------------------------------------------------
1 | import kr.boostcamp_2024.course.build_logic.configureComposeAndroid
2 |
3 | configureComposeAndroid()
--------------------------------------------------------------------------------
/build-logic/src/main/java/convention.android.feature.gradle.kts:
--------------------------------------------------------------------------------
1 | import kr.boostcamp_2024.course.build_logic.androidExtension
2 | import kr.boostcamp_2024.course.build_logic.libs
3 |
4 | plugins {
5 | id("convention.android.library")
6 | id("convention.android.compose")
7 | id("convention.android.hilt")
8 | }
9 |
10 | androidExtension.apply {
11 | pluginManager.apply("org.jetbrains.kotlin.plugin.serialization")
12 |
13 | dependencies {
14 | val libs = project.extensions.libs
15 | implementation(project(":core:domain"))
16 | implementation(project(":core:designsystem"))
17 | implementation(libs.findLibrary("androidx.navigation.compose").get())
18 | implementation(libs.findLibrary("kotlinx.serialization.json").get())
19 | implementation(libs.findLibrary("coil").get())
20 | implementation(libs.findLibrary("preferences.datastore").get())
21 | implementation(libs.findLibrary("mpandroidchart").get())
22 | }
23 | }
24 |
25 |
26 |
--------------------------------------------------------------------------------
/build-logic/src/main/java/convention.android.hilt.gradle.kts:
--------------------------------------------------------------------------------
1 | import kr.boostcamp_2024.course.build_logic.configureHiltAndroid
2 |
3 | configureHiltAndroid()
--------------------------------------------------------------------------------
/build-logic/src/main/java/convention.android.library.gradle.kts:
--------------------------------------------------------------------------------
1 | import kr.boostcamp_2024.course.build_logic.configureKotlinAndroid
2 |
3 | plugins {
4 | id("com.android.library")
5 | }
6 |
7 | configureKotlinAndroid()
--------------------------------------------------------------------------------
/build-logic/src/main/java/convention.firebase.gradle.kts:
--------------------------------------------------------------------------------
1 | import kr.boostcamp_2024.course.build_logic.configureFirebase
2 |
3 | configureFirebase()
--------------------------------------------------------------------------------
/build-logic/src/main/java/kr/boostcamp_2024/course/build_logic/ComposeAndroid.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.build_logic
2 |
3 | import org.gradle.api.Project
4 | import org.gradle.kotlin.dsl.dependencies
5 | import org.gradle.kotlin.dsl.getByType
6 | import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension
7 |
8 | internal fun Project.configureComposeAndroid() {
9 | with(pluginManager) {
10 | apply("org.jetbrains.kotlin.plugin.compose")
11 | }
12 |
13 | androidExtension.apply {
14 | buildFeatures.compose = true
15 | }
16 |
17 | dependencies {
18 | val libs = project.extensions.libs
19 | val bom = libs.findLibrary("androidx-compose-bom").get()
20 | "implementation"(platform(bom))
21 | "androidTestImplementation"(platform(bom))
22 | "implementation"(libs.findLibrary("androidx.material3").get())
23 | "implementation"(libs.findLibrary("androidx.material.icons.extended").get())
24 | "implementation"(libs.findLibrary("androidx.ui").get())
25 | "implementation"(libs.findLibrary("androidx.ui.tooling.preview").get())
26 | "implementation"(libs.findLibrary("androidx.ui.graphics").get())
27 | "implementation"(libs.findLibrary("coil.network.okhttp").get())
28 | "implementation"(libs.findLibrary("coil").get())
29 | "androidTestImplementation"(libs.findLibrary("androidx.ui.test.junit4").get())
30 | "debugImplementation"(libs.findLibrary("androidx.ui.test.manifest").get())
31 | "debugImplementation"(libs.findLibrary("androidx.ui.tooling").get())
32 | }
33 |
34 | extensions.getByType().apply {
35 | includeSourceInformation.set(true)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/build-logic/src/main/java/kr/boostcamp_2024/course/build_logic/Extension.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.build_logic
2 |
3 | import com.android.build.api.dsl.ApplicationExtension
4 | import com.android.build.api.dsl.CommonExtension
5 | import com.android.build.api.dsl.LibraryExtension
6 | import org.gradle.api.Project
7 | import org.gradle.api.artifacts.VersionCatalog
8 | import org.gradle.api.artifacts.VersionCatalogsExtension
9 | import org.gradle.api.plugins.ExtensionContainer
10 | import org.gradle.kotlin.dsl.getByType
11 |
12 | internal val Project.applicationExtension: CommonExtension<*, *, *, *, *, *>
13 | get() = extensions.getByType()
14 |
15 | internal val Project.libraryExtension: CommonExtension<*, *, *, *, *, *>
16 | get() = extensions.getByType()
17 |
18 | internal val Project.androidExtension: CommonExtension<*, *, *, *, *, *>
19 | get() = runCatching { libraryExtension }
20 | .recoverCatching { applicationExtension }
21 | .onFailure { println("Could not find Library or Application extension from this project") }
22 | .getOrThrow()
23 |
24 | internal val ExtensionContainer.libs: VersionCatalog
25 | get() = getByType().named("libs")
--------------------------------------------------------------------------------
/build-logic/src/main/java/kr/boostcamp_2024/course/build_logic/Firebase.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.build_logic
2 |
3 | import org.gradle.api.Project
4 | import org.gradle.kotlin.dsl.dependencies
5 |
6 | internal fun Project.configureFirebase() {
7 | with(pluginManager) {
8 | apply("com.google.gms.google-services")
9 | }
10 |
11 | dependencies {
12 | val libs = project.extensions.libs
13 | val bom = libs.findLibrary("firebase-bom").get()
14 | "implementation"(platform(bom))
15 | "implementation"(libs.findLibrary("firebase.auth").get())
16 | "implementation"(libs.findLibrary("firebase.firestore").get())
17 | "implementation"(libs.findLibrary("firebase.storage").get())
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/build-logic/src/main/java/kr/boostcamp_2024/course/build_logic/HiltAndroid.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.build_logic
2 |
3 | import org.gradle.api.Project
4 | import org.gradle.kotlin.dsl.dependencies
5 |
6 | internal fun Project.configureHiltAndroid() {
7 | with(pluginManager) {
8 | apply("dagger.hilt.android.plugin")
9 | apply("com.google.devtools.ksp")
10 | }
11 |
12 | dependencies {
13 | val libs = project.extensions.libs
14 | "implementation"(libs.findLibrary("hilt.android").get())
15 | "implementation"(libs.findLibrary("hilt.navigation.compose").get())
16 | "ksp"(libs.findLibrary("hilt.android.compiler").get())
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/build-logic/src/main/java/kr/boostcamp_2024/course/build_logic/KotlinAndroid.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.build_logic
2 |
3 | import org.gradle.api.JavaVersion
4 | import org.gradle.api.Project
5 | import org.gradle.kotlin.dsl.dependencies
6 | import org.gradle.kotlin.dsl.provideDelegate
7 | import org.gradle.kotlin.dsl.withType
8 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget
9 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
10 |
11 | internal fun Project.configureKotlinAndroid() {
12 | with(pluginManager) {
13 | apply("org.jetbrains.kotlin.android")
14 | }
15 |
16 | androidExtension.apply {
17 | compileSdk = 35
18 |
19 | defaultConfig {
20 | minSdk = 24
21 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
22 | }
23 |
24 | compileOptions {
25 | sourceCompatibility = JavaVersion.VERSION_17
26 | targetCompatibility = JavaVersion.VERSION_17
27 | isCoreLibraryDesugaringEnabled = true
28 | }
29 |
30 | buildTypes {
31 | getByName("release") {
32 | isMinifyEnabled = false
33 | proguardFiles(
34 | getDefaultProguardFile("proguard-android-optimize.txt"),
35 | "proguard-rules.pro",
36 | )
37 | }
38 | }
39 | testOptions {
40 | unitTests {
41 | isIncludeAndroidResources = true
42 | }
43 | }
44 | }
45 |
46 | configureKotlin()
47 |
48 | val libs = extensions.libs
49 | dependencies {
50 | "coreLibraryDesugaring"(libs.findLibrary("android.desugarJdkLibs").get())
51 | "androidTestImplementation"(libs.findLibrary("androidx.espresso.core").get())
52 | }
53 | }
54 |
55 | internal fun Project.configureKotlin() {
56 | tasks.withType().configureEach {
57 | compilerOptions {
58 | jvmTarget.set(JvmTarget.JVM_17)
59 | // Treat all Kotlin warnings as errors (disabled by default)
60 | // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
61 | val warningsAsErrors: String? by project
62 | allWarningsAsErrors.set(warningsAsErrors.toBoolean())
63 | freeCompilerArgs.set(
64 | freeCompilerArgs.get() + listOf(
65 | "-opt-in=kotlin.RequiresOptIn",
66 | ),
67 | )
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
2 |
3 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
4 | plugins {
5 | alias(libs.plugins.android.application) apply false
6 | alias(libs.plugins.kotlin.android) apply false
7 | alias(libs.plugins.kotlin.compose) apply false
8 | alias(libs.plugins.android.library) apply false
9 | alias(libs.plugins.jetbrains.kotlin.jvm) apply false
10 | alias(libs.plugins.kotlin.serialization) apply false
11 | alias(libs.plugins.google.gms.google.services) apply false
12 | alias(libs.plugins.ksp) apply false
13 | alias(libs.plugins.hilt.android) apply false
14 | id("org.jlleitschuh.gradle.ktlint") version "12.1.1"
15 | }
16 | subprojects {
17 | apply(plugin = "org.jlleitschuh.gradle.ktlint")
18 |
19 | configure {
20 | android = true
21 | ignoreFailures = false
22 | reporters {
23 | reporter(ReporterType.PLAIN)
24 | reporter(ReporterType.CHECKSTYLE)
25 | reporter(ReporterType.SARIF)
26 | reporter(ReporterType.HTML)
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/data/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.konan.properties.Properties
2 | import java.io.FileInputStream
3 |
4 | plugins {
5 | id("convention.android.library")
6 | id("convention.firebase")
7 | id("convention.android.hilt")
8 | alias(libs.plugins.kotlin.serialization)
9 | }
10 | var properties = Properties()
11 | properties.load(FileInputStream("local.properties"))
12 | android {
13 | namespace = "kr.boostcamp_2024.course.data"
14 | defaultConfig {
15 | buildConfigField("String", "X_NCP_CLOVASTUDIO_API_KEY", properties.getProperty("X-NCP-CLOVASTUDIO-API-KEY"))
16 | buildConfigField("String", "X_NCP_APIGW_API_KEY", properties.getProperty("X-NCP-APIGW-API-KEY"))
17 | buildConfigField("String", "X_NCP_CLOVASTUDIO_REQUEST_ID", properties.getProperty("X-NCP-CLOVASTUDIO-REQUEST-ID"))
18 | }
19 |
20 | buildFeatures {
21 | buildConfig = true
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation(project(":core:domain"))
27 | implementation(libs.preferences.datastore)
28 |
29 | implementation(libs.androidx.core.ktx)
30 | implementation(libs.androidx.appcompat)
31 | implementation(libs.material)
32 | testImplementation(libs.junit)
33 | androidTestImplementation(libs.androidx.junit)
34 | implementation(libs.kotlinx.coroutines)
35 | implementation(libs.retrofit)
36 | implementation(libs.okhttp)
37 | implementation(libs.logging.interceptor)
38 | implementation(libs.kotlinx.serialization.json)
39 | implementation(libs.converter.kotlinx.serialization)
40 | }
41 |
--------------------------------------------------------------------------------
/core/data/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2024/and02_weQuiz/b300f5ebc15fc5700e9b3850ef4bd2b5021705e0/core/data/consumer-rules.pro
--------------------------------------------------------------------------------
/core/data/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
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
--------------------------------------------------------------------------------
/core/data/src/androidTest/java/kr/boostcamp_2024/course/data/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.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.boostcamp_2024.course.data.test", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/core/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/core/data/src/main/java/kr/boostcamp_2024/course/data/di/AddHeadInterceptor.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.data.di
2 |
3 | import kr.boostcamp_2024.course.data.BuildConfig
4 | import okhttp3.Interceptor
5 | import okhttp3.Response
6 |
7 | class AddHeadInterceptor : Interceptor {
8 | override fun intercept(chain: Interceptor.Chain): Response {
9 | val request = chain.request().newBuilder()
10 | .addHeader("X-NCP-CLOVASTUDIO-API-KEY", BuildConfig.X_NCP_CLOVASTUDIO_API_KEY)
11 | .addHeader("X-NCP-APIGW-API-KEY", BuildConfig.X_NCP_APIGW_API_KEY)
12 | .addHeader("X-NCP-CLOVASTUDIO-REQUEST-ID", BuildConfig.X_NCP_CLOVASTUDIO_REQUEST_ID)
13 | .addHeader("content-type", "application/json")
14 | .build()
15 | return chain.proceed(request)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/data/src/main/java/kr/boostcamp_2024/course/data/model/AiQuestionResponse.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.data.model
2 |
3 | import android.annotation.SuppressLint
4 | import kotlinx.serialization.Serializable
5 | import kr.boostcamp_2024.course.domain.model.AiQuestion
6 |
7 | @SuppressLint("UnsafeOptInUsageError")
8 | @Serializable
9 | data class AiQuestionResponse(
10 | val result: ResultDTO,
11 | val status: StatusDTO,
12 | )
13 |
14 | @SuppressLint("UnsafeOptInUsageError")
15 | @Serializable
16 | data class StatusDTO(
17 | val code: String,
18 | val message: String,
19 | )
20 |
21 | @SuppressLint("UnsafeOptInUsageError")
22 | @Serializable
23 | data class ResultDTO(
24 | val message: MessageDTO,
25 | val inputLength: Long,
26 | val outputLength: Long,
27 | val stopReason: String,
28 | val seed: Long,
29 | )
30 |
31 | @SuppressLint("UnsafeOptInUsageError")
32 | @Serializable
33 | data class MessageDTO(
34 | val content: String,
35 | val role: String,
36 | )
37 |
38 | @SuppressLint("UnsafeOptInUsageError")
39 | @Serializable
40 | data class ContentDTO(
41 | val answer: String,
42 | val choices: List,
43 | val description: String,
44 | val title: String,
45 | val explanation: String,
46 | ) {
47 | fun toVO() = AiQuestion(
48 | answer = answer,
49 | choices = choices,
50 | description = description,
51 | title = title,
52 | solution = explanation,
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/core/data/src/main/java/kr/boostcamp_2024/course/data/model/BlankQuestionDTO.kt:
--------------------------------------------------------------------------------
1 | package kr.boostcamp_2024.course.data.model
2 |
3 | import com.google.firebase.firestore.PropertyName
4 | import kr.boostcamp_2024.course.domain.model.BlankQuestion
5 | import kr.boostcamp_2024.course.domain.model.BlankQuestionCreationInfo
6 | import kr.boostcamp_2024.course.domain.model.Question
7 |
8 | sealed interface QuestionDTO {
9 | fun toVO(questionId: String): Question
10 | }
11 |
12 | data class BlankQuestionDTO(
13 | val title: String? = null,
14 | val solution: String? = null,
15 | @get:PropertyName("question_content")
16 | @set:PropertyName("question_content")
17 | var questionContent: List