├── .github ├── ISSUE_TEMPLATE │ └── santa-issue-template.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── sopt │ │ └── santamanitto │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── org │ │ │ └── sopt │ │ │ └── santamanitto │ │ │ ├── MetricsExt.kt │ │ │ ├── NetworkViewModel.kt │ │ │ ├── SantaManittoApplication.kt │ │ │ ├── SplashActivity.kt │ │ │ ├── SplashViewModel.kt │ │ │ ├── admob │ │ │ ├── AdHelperModule.kt │ │ │ ├── AdmobInterstitialAdHelper.kt │ │ │ └── InterstitialAdHelper.kt │ │ │ ├── analytics │ │ │ ├── AmplitudeManager.kt │ │ │ └── EventType.kt │ │ │ ├── auth │ │ │ └── data │ │ │ │ ├── request │ │ │ │ ├── SignInRequestModel.kt │ │ │ │ └── SignUpRequestModel.kt │ │ │ │ └── response │ │ │ │ ├── SignInResponseModel.kt │ │ │ │ └── SignUpResponseModel.kt │ │ │ ├── main │ │ │ ├── MainActivity.kt │ │ │ ├── MainFragment.kt │ │ │ ├── MainViewModel.kt │ │ │ └── list │ │ │ │ ├── AdapterModule.kt │ │ │ │ ├── BasicMyManittoViewHolder.kt │ │ │ │ ├── ExpiredMyManittoViewHolder.kt │ │ │ │ ├── MyManittoInfoModel.kt │ │ │ │ ├── MyManittoListAdapter.kt │ │ │ │ ├── RemovedMyManittoViewHolder.kt │ │ │ │ └── RoomState.kt │ │ │ ├── network │ │ │ ├── NetworkModule.kt │ │ │ ├── Response.kt │ │ │ ├── ResponseCallback.kt │ │ │ ├── RetrofitQualifier.kt │ │ │ └── SimpleResponse.kt │ │ │ ├── preference │ │ │ ├── SharedPreferenceManager.kt │ │ │ ├── SharedPreferenceManagerImpl.kt │ │ │ ├── SharedPreferenceModule.kt │ │ │ └── UserPreferenceManager.kt │ │ │ ├── room │ │ │ ├── create │ │ │ │ ├── CreateRoomActivity.kt │ │ │ │ ├── ExpirationExt.kt │ │ │ │ ├── adaptor │ │ │ │ │ ├── AddMissionViewHolder.kt │ │ │ │ │ ├── CreateConfirmAdaptor.kt │ │ │ │ │ ├── CreateMissionAdaptor.kt │ │ │ │ │ └── CreateMissionViewHolder.kt │ │ │ │ ├── data │ │ │ │ │ ├── CreateMissionLiveList.kt │ │ │ │ │ └── ExpirationLiveData.kt │ │ │ │ ├── fragment │ │ │ │ │ ├── CreateConfirmFragment.kt │ │ │ │ │ ├── CreateMissionsFragment.kt │ │ │ │ │ └── CreateRoomFragment.kt │ │ │ │ ├── network │ │ │ │ │ ├── CreateRoomModel.kt │ │ │ │ │ ├── CreateRoomRequestModel.kt │ │ │ │ │ └── ModifyRoomRequestModel.kt │ │ │ │ └── viewmodel │ │ │ │ │ └── CreateRoomAndMissionViewModel.kt │ │ │ ├── data │ │ │ │ ├── MissionContentModel.kt │ │ │ │ ├── MyManittoModel.kt │ │ │ │ └── PersonalRoomModel.kt │ │ │ ├── join │ │ │ │ ├── JoinRoomFragment.kt │ │ │ │ ├── JoinRoomViewModel.kt │ │ │ │ └── network │ │ │ │ │ ├── JoinRoomErrorModel.kt │ │ │ │ │ ├── JoinRoomModel.kt │ │ │ │ │ └── JoinRoomRequestModel.kt │ │ │ ├── manittoroom │ │ │ │ ├── ManittoRoomActivity.kt │ │ │ │ ├── ManittoRoomViewModel.kt │ │ │ │ ├── MemberAdapter.kt │ │ │ │ ├── MemberViewHolder.kt │ │ │ │ ├── ResultAdapter.kt │ │ │ │ ├── ResultViewHolder.kt │ │ │ │ ├── fragment │ │ │ │ │ ├── FinishFragment.kt │ │ │ │ │ ├── MatchedFragment.kt │ │ │ │ │ ├── MatchingFragment.kt │ │ │ │ │ └── WaitingRoomFragment.kt │ │ │ │ └── network │ │ │ │ │ ├── ManittoRoomMember.kt │ │ │ │ │ └── ManittoRoomModel.kt │ │ │ └── network │ │ │ │ ├── ExitRoomRequestModel.kt │ │ │ │ ├── RoomNetworkModule.kt │ │ │ │ ├── RoomRequest.kt │ │ │ │ └── RoomService.kt │ │ │ ├── setting │ │ │ ├── SettingFragment.kt │ │ │ └── SettingListView.kt │ │ │ ├── update │ │ │ └── version │ │ │ │ └── Version.kt │ │ │ ├── user │ │ │ ├── UserModule.kt │ │ │ ├── data │ │ │ │ ├── UserInfoModel.kt │ │ │ │ ├── controller │ │ │ │ │ ├── UserAuthController.kt │ │ │ │ │ └── UserController.kt │ │ │ │ └── source │ │ │ │ │ ├── CachedMainUserDataSource.kt │ │ │ │ │ ├── CachedUserMetadataSource.kt │ │ │ │ │ └── UserMetadataSource.kt │ │ │ ├── mypage │ │ │ │ ├── EditNameFragment.kt │ │ │ │ ├── EditNameViewModel.kt │ │ │ │ ├── UserNameModel.kt │ │ │ │ └── UserNameRequestModel.kt │ │ │ ├── network │ │ │ │ ├── UserAuthService.kt │ │ │ │ ├── UserNetworkModule.kt │ │ │ │ └── UserService.kt │ │ │ └── signin │ │ │ │ ├── SignInActivity.kt │ │ │ │ ├── fragment │ │ │ │ ├── ConditionFragment.kt │ │ │ │ ├── EnterNameFragment.kt │ │ │ │ └── WebLinkFragment.kt │ │ │ │ └── viewmodel │ │ │ │ ├── ConditionViewModel.kt │ │ │ │ └── EnterNameViewModel.kt │ │ │ ├── util │ │ │ ├── BindingAdapters.kt │ │ │ ├── ClipBoardUtil.kt │ │ │ ├── FragmentUtil.kt │ │ │ ├── ItemDiffCallback.kt │ │ │ ├── MetricsUtil.kt │ │ │ ├── RandomUtil.kt │ │ │ ├── RoomSortUtil.kt │ │ │ ├── TimeUtil.kt │ │ │ └── base │ │ │ │ └── BaseFragment.kt │ │ │ └── view │ │ │ ├── LiveCheckbox.kt │ │ │ ├── SantaBackground.kt │ │ │ ├── SantaBackgroundBindingAdapter.kt │ │ │ ├── SantaBottomButton.kt │ │ │ ├── SantaCardView.kt │ │ │ ├── SantaCheckBox.kt │ │ │ ├── SantaCheckBoxBindingAdapter.kt │ │ │ ├── SantaEditText.kt │ │ │ ├── SantaEditTextBindingAdapter.kt │ │ │ ├── SantaImageRoundButton.kt │ │ │ ├── SantaIndicator.kt │ │ │ ├── SantaLoading.kt │ │ │ ├── SantaNameInput.kt │ │ │ ├── SantaPeriodPicker.kt │ │ │ ├── SantaSmallButton.kt │ │ │ ├── SantaSwitch.kt │ │ │ ├── TextObservable.kt │ │ │ ├── ViewExt.kt │ │ │ ├── dialog │ │ │ ├── RoundDialog.kt │ │ │ ├── RoundDialogBuilder.kt │ │ │ ├── RoundDialogButton.kt │ │ │ ├── exit │ │ │ │ └── ExitDialogCreator.kt │ │ │ └── withdraw │ │ │ │ └── WithdrawDialogCreator.kt │ │ │ ├── recyclerview │ │ │ ├── BaseAdapter.kt │ │ │ ├── BaseViewHolder.kt │ │ │ ├── MarginDecoration.kt │ │ │ └── RecyclerViewExt.kt │ │ │ └── santanumberpicker │ │ │ ├── SantaNumberPicker.kt │ │ │ └── SantaNumberPickerSnapScrollListener.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── background_invitation_code_button.xml │ │ ├── background_round_text.xml │ │ ├── color_santaedittext_edittext.xml │ │ ├── cursor_edittext.xml │ │ ├── ic_alert.xml │ │ ├── ic_arrow.xml │ │ ├── ic_arrow_right.xml │ │ ├── ic_btn_add.xml │ │ ├── ic_btn_back.xml │ │ ├── ic_btn_cancel.xml │ │ ├── ic_check.xml │ │ ├── ic_check_selected.xml │ │ ├── ic_circle_30.xml │ │ ├── ic_delete.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_logo.xml │ │ ├── ic_minus_dark_gray.xml │ │ ├── ic_minus_light_gray.xml │ │ ├── ic_my_page.xml │ │ ├── ic_plus_dark_gray.xml │ │ ├── ic_plus_light_gray.xml │ │ ├── ic_refresh.xml │ │ ├── ic_roulette.xml │ │ ├── ic_rudolf.xml │ │ ├── ic_rudolf_head.xml │ │ ├── ic_rudolf_ic.xml │ │ ├── ic_santa.xml │ │ ├── ic_santa_head.xml │ │ ├── ic_santa_head_long.xml │ │ ├── ic_santa_ic.xml │ │ ├── ic_santa_widht_chimney.xml │ │ ├── ic_setting.xml │ │ ├── ic_snow.xml │ │ ├── ic_snow_matching.xml │ │ ├── ic_snowman.xml │ │ ├── ic_socks_green.xml │ │ ├── ic_socks_red.xml │ │ ├── ic_socks_with_line.xml │ │ ├── ic_socks_with_line_green.xml │ │ ├── ic_splash_snow.xml │ │ ├── ic_tree.xml │ │ ├── img_matching.xml │ │ ├── round_ractangle_background.xml │ │ ├── round_ractangle_background_30.xml │ │ ├── round_ractangle_background_4.xml │ │ ├── round_ractangle_background_4_border.xml │ │ ├── round_ractangle_bottom_background.xml │ │ ├── scrollbar_round_30.xml │ │ ├── selector_santa_check_box.xml │ │ ├── shape_dark_gray_fill_10_rect.xml │ │ └── shape_light_gray_fill_4_rect.xml │ │ ├── font │ │ ├── pretendard_bold.ttf │ │ ├── pretendard_font_family.xml │ │ ├── pretendard_medium.ttf │ │ ├── pretendard_regular.ttf │ │ └── pretendard_semi_bold.ttf │ │ ├── layout │ │ ├── activity_create_room.xml │ │ ├── activity_main.xml │ │ ├── activity_manitto_room.xml │ │ ├── activity_sign_in.xml │ │ ├── activity_splash.xml │ │ ├── dialog_create_room_time_picker.xml │ │ ├── dialog_invitation_code.xml │ │ ├── dialog_text_content_layout.xml │ │ ├── fragment_condition.xml │ │ ├── fragment_create_confirm.xml │ │ ├── fragment_create_mission.xml │ │ ├── fragment_create_room.xml │ │ ├── fragment_edit_name.xml │ │ ├── fragment_enter_name.xml │ │ ├── fragment_join_room.xml │ │ ├── fragment_main.xml │ │ ├── fragment_manitto_room_finish.xml │ │ ├── fragment_matched.xml │ │ ├── fragment_matching.xml │ │ ├── fragment_setting.xml │ │ ├── fragment_waiting_room.xml │ │ ├── fragment_webview.xml │ │ ├── item_add_mission.xml │ │ ├── item_create_mission.xml │ │ ├── item_member.xml │ │ ├── item_mymanitto.xml │ │ ├── item_mymanitto_expired.xml │ │ ├── item_mymanitto_removed.xml │ │ ├── item_result.xml │ │ ├── item_setting.xml │ │ ├── layout_finish.xml │ │ ├── layout_result.xml │ │ ├── santa_background.xml │ │ ├── santa_check_box.xml │ │ ├── santa_edit_text.xml │ │ ├── santa_image_round_button.xml │ │ ├── santa_indicator.xml │ │ ├── santa_loading.xml │ │ ├── santa_name_input.xml │ │ ├── santa_period_picker.xml │ │ └── santa_switch.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ ├── navigation_create_room.xml │ │ ├── navigation_main.xml │ │ ├── navigation_manittoroom.xml │ │ └── navigation_signin.xml │ │ ├── raw │ │ └── lottie_roulette.json │ │ ├── values-v28 │ │ └── themes.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── colors_common.xml │ │ ├── dimens.xml │ │ ├── font_style.xml │ │ ├── strings.xml │ │ └── themes.xml │ ├── mock │ ├── ic_launcher_dev-playstore.png │ ├── java │ │ └── org │ │ │ └── sopt │ │ │ └── santamanitto │ │ │ ├── room │ │ │ └── network │ │ │ │ ├── FakeRoomItems.kt │ │ │ │ ├── FakeRoomRequest.kt │ │ │ │ └── FakeRoomRequestModule.kt │ │ │ └── user │ │ │ ├── UserRemoteModule.kt │ │ │ └── data │ │ │ └── controller │ │ │ ├── FakeUserAuthController.kt │ │ │ └── FakeUserController.kt │ └── res │ │ ├── drawable │ │ └── ic_launcher_dev_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher_dev.xml │ │ └── ic_launcher_dev_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher_dev.webp │ │ ├── ic_launcher_dev_foreground.webp │ │ └── ic_launcher_dev_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher_dev.webp │ │ ├── ic_launcher_dev_foreground.webp │ │ └── ic_launcher_dev_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher_dev.webp │ │ ├── ic_launcher_dev_foreground.webp │ │ └── ic_launcher_dev_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher_dev.webp │ │ ├── ic_launcher_dev_foreground.webp │ │ └── ic_launcher_dev_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher_dev.webp │ │ ├── ic_launcher_dev_foreground.webp │ │ └── ic_launcher_dev_round.webp │ │ └── values │ │ └── strings.xml │ ├── prod │ ├── ic_launcher_dev-playstore.png │ ├── java │ │ └── org │ │ │ └── sopt │ │ │ └── santamanitto │ │ │ ├── room │ │ │ └── network │ │ │ │ ├── RoomRequestImpl.kt │ │ │ │ └── RoomRequestModule.kt │ │ │ └── user │ │ │ ├── UserRemoteModule.kt │ │ │ └── data │ │ │ └── controller │ │ │ ├── RetrofitUserAuthController.kt │ │ │ └── RetrofitUserController.kt │ └── res │ │ ├── drawable │ │ └── ic_launcher_dev_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher_dev.xml │ │ └── ic_launcher_dev_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher_dev.webp │ │ ├── ic_launcher_dev_foreground.webp │ │ └── ic_launcher_dev_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher_dev.webp │ │ ├── ic_launcher_dev_foreground.webp │ │ └── ic_launcher_dev_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher_dev.webp │ │ ├── ic_launcher_dev_foreground.webp │ │ └── ic_launcher_dev_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher_dev.webp │ │ ├── ic_launcher_dev_foreground.webp │ │ └── ic_launcher_dev_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher_dev.webp │ │ ├── ic_launcher_dev_foreground.webp │ │ └── ic_launcher_dev_round.webp │ │ └── values │ │ └── strings.xml │ ├── sharedTest │ └── java │ │ └── org │ │ └── sopt │ │ └── santamanitto │ │ └── util │ │ └── MockitoKotlinHelpers.kt │ └── test │ └── java │ └── org │ └── sopt │ └── santamanitto │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.github/ISSUE_TEMPLATE/santa-issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Santa ISSUE TEMPLATE 3 | about: default 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | *.aab 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle 19 | .gradle/ 20 | build/ 21 | 22 | # Signing files 23 | .signing/ 24 | 25 | # Local configuration file (sdk path, etc) 26 | local.properties 27 | keystore.properties 28 | 29 | # Proguard folder generated by Eclipse 30 | proguard/ 31 | 32 | # Log Files 33 | *.log 34 | 35 | # Android Studio 36 | /*/build/ 37 | /*/local.properties 38 | /*/keystore.properties 39 | /*/out 40 | /*/*/build 41 | /*/*/production 42 | captures/ 43 | .navigation/ 44 | *.ipr 45 | *~ 46 | *.swp 47 | 48 | # Keystore files 49 | *.jks 50 | *.pem 51 | *.keystore 52 | 53 | # Google Services (e.g. APIs or Firebase) 54 | google-services.json 55 | 56 | # Android Patch 57 | gen-external-apklibs 58 | 59 | # External native build folder generated in Android Studio 2.2 and later 60 | .externalNativeBuild 61 | 62 | # NDK 63 | obj/ 64 | 65 | # IntelliJ IDEA 66 | *.iml 67 | *.iws 68 | /out/ 69 | 70 | # User-specific configurations 71 | .idea 72 | 73 | # OS-specific files 74 | .DS_Store 75 | .DS_Store? 76 | ._* 77 | .Spotlight-V100 78 | .Trashes 79 | ehthumbs.db 80 | Thumbs.db 81 | 82 | # Legacy Eclipse project files 83 | .classpath 84 | .project 85 | .cproject 86 | .settings/ 87 | 88 | # Mobile Tools for Java (J2ME) 89 | .mtj.tmp/ 90 | 91 | # Package Files # 92 | *.war 93 | *.ear 94 | 95 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 96 | hs_err_pid* 97 | 98 | ## Plugin-specific files: 99 | 100 | # mpeltonen/sbt-idea plugin 101 | .idea_modules/ 102 | 103 | # JIRA plugin 104 | atlassian-ide-plugin.xml 105 | 106 | # Mongo Explorer plugin 107 | .idea/mongoSettings.xml 108 | 109 | # Crashlytics plugin (for Android Studio and IntelliJ) 110 | com_crashlytics_export_strings.xml 111 | crashlytics.properties 112 | crashlytics-build.properties 113 | fabric.properties 114 | 115 | ### AndroidStudio Patch ### 116 | 117 | !/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /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/org/sopt/santamanitto/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto 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("org.sopt.santamanitto", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 17 | 18 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 38 | 39 | 43 | 44 | 49 | 50 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/MetricsExt.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto 2 | 3 | import android.view.View 4 | import org.sopt.santamanitto.util.MetricsUtil 5 | 6 | fun Float.toDP(): Float { 7 | return MetricsUtil.convertPixelsToDp(this, null) 8 | } 9 | 10 | fun Float.toPixel() : Float { 11 | return MetricsUtil.convertDpToPixel(this, null) 12 | } 13 | 14 | fun View.getDpFromPx(px: Float): Float { 15 | return MetricsUtil.convertPixelsToDp(px, this.context) 16 | } 17 | 18 | fun View.getPxFromDp(dp: Float): Float { 19 | return MetricsUtil.convertDpToPixel(dp, this.context) 20 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/NetworkViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import javax.inject.Inject 8 | 9 | @HiltViewModel 10 | open class NetworkViewModel @Inject constructor(): ViewModel() { 11 | private var loadingCount = 0 12 | 13 | protected val _networkErrorOccur = MutableLiveData(false) 14 | val networkErrorOccur : LiveData 15 | get() = _networkErrorOccur 16 | 17 | protected val _isLoading = MutableLiveData(true) 18 | val isLoading: LiveData 19 | get() = _isLoading 20 | 21 | protected fun startLoading() { 22 | _isLoading.value = true 23 | loadingCount++ 24 | } 25 | 26 | protected fun stopLoading() { 27 | loadingCount-- 28 | if (loadingCount == 0) { 29 | _isLoading.value = false 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/SantaManittoApplication.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto 2 | 3 | import android.app.Application 4 | import androidx.appcompat.app.AppCompatDelegate 5 | import com.google.android.gms.ads.MobileAds 6 | import com.google.android.gms.ads.RequestConfiguration 7 | import dagger.hilt.android.HiltAndroidApp 8 | import org.sopt.santamanitto.BuildConfig.AMPLITUDE_KEY 9 | import org.sopt.santamanitto.analytics.AmplitudeManager 10 | import timber.log.Timber 11 | 12 | @HiltAndroidApp 13 | class SantaManittoApplication : Application() { 14 | 15 | override fun onCreate() { 16 | super.onCreate() 17 | 18 | initTimber() 19 | initAmplitude() 20 | setLightModeOnly() 21 | initializeAds() 22 | } 23 | 24 | private fun initTimber() { 25 | if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) 26 | } 27 | 28 | private fun initAmplitude() { 29 | AmplitudeManager.init(this, AMPLITUDE_KEY) 30 | } 31 | 32 | private fun setLightModeOnly() { 33 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) 34 | } 35 | 36 | private fun initializeAds() { 37 | MobileAds.setRequestConfiguration( 38 | RequestConfiguration.Builder() 39 | .setTestDeviceIds(listOf(BuildConfig.TH_TEST_ID)) 40 | .build() 41 | ) 42 | 43 | MobileAds.initialize(this) 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/admob/AdHelperModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.admob 2 | 3 | import dagger.Binds 4 | import dagger.Module 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | import javax.inject.Singleton 8 | 9 | @Module 10 | @InstallIn(SingletonComponent::class) 11 | abstract class AdHelperModule { 12 | @Binds 13 | @Singleton 14 | abstract fun bindAdHelper(adHelperImpl: AdmobInterstitialAdHelper): InterstitialAdHelper 15 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/admob/InterstitialAdHelper.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.admob 2 | 3 | import android.app.Activity 4 | 5 | interface InterstitialAdHelper { 6 | fun initialize(activity: Activity, adUnitId: String) 7 | fun loadAd() 8 | 9 | /** 10 | * 광고가 준비되어 있으면 즉시 보여주고, 광고가 준비되지 않았거나 실패했으면 바로 onFinished 콜백만 호출한다. 11 | * @param onFinished: 광고가 다 닫혔거나, 아예 광고가 없을 때 실행할 콜백 12 | */ 13 | fun showAdIfAvailable(onFinished: () -> Unit) 14 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/analytics/AmplitudeManager.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.analytics 2 | 3 | import android.content.Context 4 | import com.amplitude.android.Amplitude 5 | import com.amplitude.android.Configuration 6 | import com.amplitude.android.events.Identify 7 | 8 | object AmplitudeManager { 9 | private lateinit var amplitude: Amplitude 10 | 11 | fun init( 12 | context: Context, 13 | apiKey: String, 14 | ) { 15 | amplitude = Amplitude( 16 | Configuration( 17 | apiKey = apiKey, 18 | context = context.applicationContext, 19 | ), 20 | ) 21 | } 22 | 23 | fun trackEvent( 24 | eventName: String, 25 | eventType: EventType 26 | ) { 27 | amplitude.track(eventName, mapOf(TYPE to eventType.type)) 28 | } 29 | 30 | fun setUserId(userId: String) { 31 | amplitude.setUserId(userId) 32 | } 33 | 34 | fun updateStringProperty( 35 | propertyName: String, 36 | value: String, 37 | ) { 38 | amplitude.identify(Identify().set(propertyName, value)) 39 | } 40 | 41 | private const val TYPE = "Type" 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/analytics/EventType.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.analytics 2 | 3 | enum class EventType(val type: String) { 4 | PAGE("page"), BUTTON("button"), MODAL("modal") 5 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/auth/data/request/SignInRequestModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.auth.data.request 2 | 3 | data class SignInRequestModel( 4 | val serialNumber: String 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/auth/data/request/SignUpRequestModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.auth.data.request 2 | 3 | data class SignUpRequestModel( 4 | val serialNumber: String, 5 | val name: String 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/auth/data/response/SignInResponseModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.auth.data.response 2 | 3 | data class SignInResponseModel( 4 | val accessToken: String, 5 | val id: String 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/auth/data/response/SignUpResponseModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.auth.data.response 2 | 3 | data class SignUpResponseModel( 4 | val accessToken: String, 5 | val id: String 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/main/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.main 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.databinding.DataBindingUtil.setContentView 6 | import dagger.hilt.android.AndroidEntryPoint 7 | import org.sopt.santamanitto.R 8 | import org.sopt.santamanitto.databinding.ActivityMainBinding 9 | 10 | @AndroidEntryPoint 11 | class MainActivity : AppCompatActivity() { 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | 15 | setContentView(this, R.layout.activity_main) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/main/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.main 2 | 3 | import androidx.lifecycle.viewModelScope 4 | import dagger.hilt.android.lifecycle.HiltViewModel 5 | import kotlinx.coroutines.flow.MutableSharedFlow 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.flow.SharedFlow 8 | import kotlinx.coroutines.flow.StateFlow 9 | import kotlinx.coroutines.launch 10 | import org.sopt.santamanitto.NetworkViewModel 11 | import org.sopt.santamanitto.room.data.MyManittoModel 12 | import org.sopt.santamanitto.room.network.RoomRequest 13 | import org.sopt.santamanitto.util.RoomSortUtil 14 | import timber.log.Timber 15 | import javax.inject.Inject 16 | 17 | @HiltViewModel 18 | class MainViewModel @Inject constructor( 19 | private val roomRequest: RoomRequest 20 | ) : NetworkViewModel() { 21 | 22 | private var _myManittoModelList = MutableSharedFlow>(replay = 1) 23 | val myManittoModelList: SharedFlow> = _myManittoModelList 24 | 25 | private val _isRefreshing = MutableStateFlow(false) 26 | val isRefreshing: StateFlow = _isRefreshing 27 | 28 | fun fetchMyManittoList() { 29 | viewModelScope.launch { 30 | _isRefreshing.value = true 31 | _isLoading.value = true 32 | try { 33 | val rooms = roomRequest.getRooms() 34 | _myManittoModelList.emit(RoomSortUtil.getSortedRooms(rooms)) 35 | } catch (e: Exception) { 36 | Timber.e(e) 37 | _networkErrorOccur.value = true 38 | } finally { 39 | _isLoading.value = false 40 | _isRefreshing.value = false 41 | } 42 | } 43 | } 44 | 45 | fun exitRoom(roomId: String) { 46 | roomRequest.exitRoom(roomId) { 47 | if (it) { 48 | refresh() 49 | } else { 50 | _networkErrorOccur.value = true 51 | } 52 | } 53 | } 54 | 55 | fun deleteRoom(roomId: String) { 56 | viewModelScope.launch { 57 | roomRequest.deleteRoom(roomId).fold( 58 | onSuccess = { 59 | refresh() 60 | }, 61 | onFailure = { 62 | _networkErrorOccur.value = true 63 | } 64 | ) 65 | } 66 | } 67 | 68 | fun refresh() { 69 | fetchMyManittoList() 70 | } 71 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/main/list/AdapterModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.main.list 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.android.components.FragmentComponent 7 | import org.sopt.santamanitto.room.network.RoomRequest 8 | import org.sopt.santamanitto.user.data.source.UserMetadataSource 9 | 10 | @InstallIn(FragmentComponent::class) 11 | @Module 12 | class AdapterModule { 13 | @Provides 14 | fun provideJoinedRoomAdapter( 15 | userMetadataSource: UserMetadataSource, 16 | roomRequest: RoomRequest 17 | ): MyManittoListAdapter = MyManittoListAdapter(userMetadataSource, roomRequest) 18 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/main/list/ExpiredMyManittoViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.main.list 2 | 3 | import android.view.ViewGroup 4 | import org.sopt.santamanitto.R 5 | import org.sopt.santamanitto.databinding.ItemMymanittoExpiredBinding 6 | import org.sopt.santamanitto.room.data.MyManittoModel 7 | import org.sopt.santamanitto.view.recyclerview.BaseViewHolder 8 | 9 | class ExpiredMyManittoViewHolder( 10 | parent: ViewGroup, 11 | enterListener: ((roomId: String, isMatched: Boolean, isFinished: Boolean) -> Unit)?, 12 | removeListener: ((roomId: String) -> Unit)? 13 | ) : BaseViewHolder( 14 | R.layout.item_mymanitto_expired, 15 | parent 16 | ) { 17 | 18 | private val exitButton = binding.textviewMymanittoexpiredButton 19 | private val title = binding.textviewMymanittoexpriedTitle 20 | 21 | private var roomId = "" 22 | 23 | init { 24 | removeListener?.let { 25 | exitButton.setOnClickListener { 26 | it(roomId) 27 | } 28 | } 29 | enterListener?.let { 30 | binding.root.setOnClickListener { 31 | it(roomId, false, true) 32 | } 33 | } 34 | } 35 | 36 | override fun bind(data: MyManittoModel) { 37 | title.text = data.roomName 38 | roomId = data.roomId 39 | } 40 | 41 | override fun clear() { 42 | title.text = "" 43 | roomId = "" 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/main/list/MyManittoInfoModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.main.list 2 | 3 | data class MyManittoInfoModel( 4 | val manittoName: String, 5 | val mission: String? 6 | ) -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/main/list/RemovedMyManittoViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.main.list 2 | 3 | import android.view.ViewGroup 4 | import org.sopt.santamanitto.R 5 | import org.sopt.santamanitto.databinding.ItemMymanittoRemovedBinding 6 | import org.sopt.santamanitto.room.data.MyManittoModel 7 | import org.sopt.santamanitto.view.recyclerview.BaseViewHolder 8 | 9 | class RemovedMyManittoViewHolder( 10 | parent: ViewGroup, 11 | removeListener: ((roomId: String) -> Unit)? 12 | ) : BaseViewHolder( 13 | R.layout.item_mymanitto_removed, 14 | parent 15 | ) { 16 | 17 | private val removeButton = binding.textviewMymanittoremovedButton 18 | 19 | private var roomId = "" 20 | 21 | init { 22 | removeListener?.let { 23 | removeButton.setOnClickListener { 24 | it(roomId) 25 | } 26 | } 27 | } 28 | 29 | override fun bind(data: MyManittoModel) { 30 | roomId = data.roomId 31 | } 32 | 33 | override fun clear() { 34 | roomId = "" 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/main/list/RoomState.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.main.list 2 | 3 | enum class RoomState { 4 | IN_PROGRESS, 5 | WAITING, 6 | FINISHED, 7 | EXPIRED, 8 | DELETED, 9 | LEFT 10 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/network/NetworkModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.network 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | import okhttp3.OkHttpClient 8 | import okhttp3.logging.HttpLoggingInterceptor 9 | import org.sopt.santamanitto.BuildConfig 10 | import org.sopt.santamanitto.user.data.source.UserMetadataSource 11 | import retrofit2.Retrofit 12 | import retrofit2.converter.gson.GsonConverterFactory 13 | import javax.inject.Singleton 14 | 15 | @InstallIn(SingletonComponent::class) 16 | @Module 17 | object NetworkModule { 18 | 19 | @Provides 20 | fun provideBaseUrl(): String = BuildConfig.BASE_URL 21 | 22 | @Provides 23 | @Singleton 24 | @OtherInterceptorOkHttpClient 25 | fun provideOtherInterceptorOkHttpClient(): OkHttpClient { 26 | return OkHttpClient.Builder() 27 | .addInterceptor(provideLogger()) 28 | .build() 29 | } 30 | 31 | @Provides 32 | @Singleton 33 | @AuthInterceptorOkHttpClient 34 | fun provideAuthInterceptorOkHttpClient(userMetadataSource: UserMetadataSource): OkHttpClient { 35 | return OkHttpClient.Builder() 36 | .addInterceptor { 37 | val request = it.request().newBuilder() 38 | .addHeader("Authorization", "Bearer ${userMetadataSource.getAccessToken()}") 39 | .build() 40 | it.proceed(request) 41 | } 42 | .addInterceptor(provideLogger()) 43 | .build() 44 | } 45 | 46 | @Provides 47 | @Singleton 48 | @OtherRetrofitClient 49 | fun provideOtherRetrofitClient( 50 | @OtherInterceptorOkHttpClient okHttpClient: OkHttpClient, 51 | baseUrl: String 52 | ): Retrofit = 53 | provideRetrofit(okHttpClient, baseUrl) 54 | 55 | @Provides 56 | @Singleton 57 | @AuthRetrofitClient 58 | fun provideAuthRetrofitClient( 59 | @AuthInterceptorOkHttpClient okHttpClient: OkHttpClient, 60 | baseUrl: String 61 | ): Retrofit = 62 | provideRetrofit(okHttpClient, baseUrl) 63 | 64 | private fun provideLogger(): HttpLoggingInterceptor { 65 | return HttpLoggingInterceptor().apply { 66 | level = HttpLoggingInterceptor.Level.BODY 67 | } 68 | } 69 | 70 | fun provideRetrofit(okHttpClient: OkHttpClient, baseUrl: String): Retrofit = 71 | Retrofit.Builder() 72 | .addConverterFactory(GsonConverterFactory.create()) 73 | .baseUrl(baseUrl) 74 | .client(okHttpClient) 75 | .build() 76 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/network/Response.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.network 2 | 3 | data class Response( 4 | val statusCode: Int, 5 | val message: String, 6 | val data: T 7 | ) -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/network/ResponseCallback.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.network 2 | 3 | import retrofit2.Call 4 | import retrofit2.Callback 5 | import timber.log.Timber 6 | 7 | fun Call>.start(callback: RequestCallback) { 8 | val TAG = javaClass.simpleName + " response" 9 | enqueue(object : Callback> { 10 | override fun onResponse( 11 | call: Call>, 12 | response: retrofit2.Response> 13 | ) { 14 | if (response.isSuccessful) { 15 | if (response.body() != null) { 16 | callback.onSuccess(response.body()!!.data) 17 | } else { 18 | Timber.tag(TAG).e("response body is null. ${response.message()}") 19 | callback.onFail() 20 | } 21 | } else { 22 | Timber.tag(TAG).e("response is not successful. ${response.message()}") 23 | callback.onFail() 24 | } 25 | } 26 | 27 | override fun onFailure(call: Call>, t: Throwable) { 28 | Timber.tag(TAG).e("request is fail. ${t.message}") 29 | callback.onFail() 30 | } 31 | }) 32 | } 33 | 34 | interface RequestCallback { 35 | fun onSuccess(data: T) 36 | 37 | fun onFail() 38 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/network/RetrofitQualifier.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.network 2 | 3 | import javax.inject.Qualifier 4 | 5 | @Qualifier 6 | @Retention(AnnotationRetention.BINARY) 7 | annotation class AuthInterceptorOkHttpClient 8 | 9 | @Qualifier 10 | @Retention(AnnotationRetention.BINARY) 11 | annotation class OtherInterceptorOkHttpClient 12 | 13 | @Qualifier 14 | @Retention(AnnotationRetention.BINARY) 15 | annotation class AuthRetrofitClient 16 | 17 | @Qualifier 18 | @Retention(AnnotationRetention.BINARY) 19 | annotation class OtherRetrofitClient -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/network/SimpleResponse.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.network 2 | 3 | import retrofit2.Call 4 | import retrofit2.Callback 5 | import retrofit2.Response 6 | import timber.log.Timber 7 | 8 | data class SimpleResponse( 9 | val statusCode: Int, 10 | val message: String, 11 | val data: String 12 | ) 13 | 14 | fun Call.start(callback: (Boolean) -> Unit) { 15 | val TAG = javaClass.simpleName + " response" 16 | enqueue(object : Callback { 17 | override fun onResponse(call: Call, response: Response) { 18 | if (response.isSuccessful) { 19 | callback.invoke(true) 20 | } else { 21 | Timber.tag(TAG).e("response is not successful. ${response.message()}") 22 | callback.invoke(false) 23 | } 24 | } 25 | 26 | override fun onFailure(call: Call, t: Throwable) { 27 | Timber.tag(TAG).e("request is fail. ${t.message}") 28 | callback.invoke(false) 29 | } 30 | }) 31 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/preference/SharedPreferenceManager.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.preference 2 | 3 | interface SharedPreferenceManager { 4 | fun setString(key: String, value: String) 5 | 6 | fun getString(key: String): String? 7 | 8 | fun getString(key: String, defaultValue: String?): String? 9 | 10 | fun setInt(key: String, value: Int) 11 | 12 | fun getInt(key: String, defaultValue: Int): Int 13 | 14 | fun setBoolean(key: String, value: Boolean) 15 | 16 | fun getBoolean(key: String, defaultValue: Boolean): Boolean 17 | 18 | fun clear(): Boolean 19 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/preference/SharedPreferenceManagerImpl.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.preference 2 | 3 | import android.content.SharedPreferences 4 | 5 | 6 | class SharedPreferenceManagerImpl(private val sharedPreferences: SharedPreferences) : SharedPreferenceManager { 7 | companion object { 8 | const val SHARED_PREFERENCES_FILE_NAME = "manitto_preference" 9 | } 10 | 11 | private var editor: SharedPreferences.Editor = sharedPreferences.edit() 12 | 13 | override fun setString(key: String, value: String) { 14 | editor.putString(key, value).apply() 15 | } 16 | 17 | override fun getString(key: String): String? { 18 | return getString(key, null) 19 | } 20 | 21 | override fun getString(key: String, defaultValue: String?): String? { 22 | return sharedPreferences.getString(key, defaultValue) 23 | } 24 | 25 | override fun setInt(key: String, value: Int) { 26 | editor.putInt(key, value).apply() 27 | } 28 | 29 | override fun getInt(key: String, defaultValue: Int): Int { 30 | return sharedPreferences.getInt(key, defaultValue) 31 | } 32 | 33 | override fun setBoolean(key: String, value: Boolean) { 34 | editor.putBoolean(key, value).apply() 35 | } 36 | 37 | override fun getBoolean(key: String, defaultValue: Boolean): Boolean { 38 | return sharedPreferences.getBoolean(key, defaultValue) 39 | } 40 | 41 | override fun clear(): Boolean { 42 | return editor.clear().commit() 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/preference/SharedPreferenceModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.preference 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 org.sopt.santamanitto.preference.SharedPreferenceManagerImpl.Companion.SHARED_PREFERENCES_FILE_NAME 11 | import javax.inject.Singleton 12 | 13 | @InstallIn(SingletonComponent::class) 14 | @Module 15 | class SharedPreferenceModule { 16 | 17 | @Provides 18 | fun provideSharedPreference(@ApplicationContext context: Context): SharedPreferences = 19 | context.getSharedPreferences(SHARED_PREFERENCES_FILE_NAME, Context.MODE_PRIVATE) 20 | 21 | @Provides 22 | @Singleton 23 | fun provideSharedPreferenceManager(sharedPreferences: SharedPreferences): SharedPreferenceManager = 24 | SharedPreferenceManagerImpl(sharedPreferences) 25 | 26 | @Provides 27 | @Singleton 28 | fun provideUserPreferenceManager(sharedPreferenceManager: SharedPreferenceManager): UserPreferenceManager = 29 | UserPreferenceManager(sharedPreferenceManager) 30 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/preference/UserPreferenceManager.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.preference 2 | 3 | import org.sopt.santamanitto.user.data.source.UserMetadataSource 4 | 5 | class UserPreferenceManager(private val preferenceManager: SharedPreferenceManager) : 6 | UserMetadataSource { 7 | companion object { 8 | private const val PREF_KEY_USER_ID = "user_id" 9 | private const val PREF_KEY_SERIAL_NUMBER = "serial_number" 10 | private const val PREF_KEY_USER_NAME = "user_name" 11 | private const val PREF_KEY_USER_UPDATE_AT = "user_update_at" 12 | private const val PREF_KEY_USER_CREATE_AT = "user_update_at" 13 | private const val PREF_KEY_ACCESS_TOKEN = "access_token" 14 | } 15 | 16 | override fun setUserId(userId: String) { 17 | preferenceManager.setString(PREF_KEY_USER_ID, userId) 18 | } 19 | 20 | override fun getUserId(): String = preferenceManager.getString(PREF_KEY_USER_ID) ?: "" 21 | 22 | fun setSerialNumber(serialNumber: String) { 23 | preferenceManager.setString(PREF_KEY_SERIAL_NUMBER, serialNumber) 24 | } 25 | 26 | fun getSerialNumber(): String? { 27 | return preferenceManager.getString(PREF_KEY_SERIAL_NUMBER) 28 | } 29 | 30 | override fun setUserName(userName: String) { 31 | preferenceManager.setString(PREF_KEY_USER_NAME, userName) 32 | } 33 | 34 | override fun getUserName(): String = preferenceManager.getString(PREF_KEY_USER_NAME) ?: "" 35 | 36 | fun setUserUpdateTime(updateAt: String) { 37 | preferenceManager.setString(PREF_KEY_USER_UPDATE_AT, updateAt) 38 | } 39 | 40 | fun getUserUpdateTime(): String? { 41 | return preferenceManager.getString(PREF_KEY_USER_UPDATE_AT) 42 | } 43 | 44 | fun setUserCreateTime(createAt: String) { 45 | preferenceManager.setString(PREF_KEY_USER_CREATE_AT, createAt) 46 | } 47 | 48 | fun getUserCreateTime(): String? { 49 | return preferenceManager.getString(PREF_KEY_USER_CREATE_AT) 50 | } 51 | 52 | override fun setAccessToken(accessToken: String) { 53 | preferenceManager.setString(PREF_KEY_ACCESS_TOKEN, accessToken) 54 | } 55 | 56 | override fun getAccessToken(): String = preferenceManager.getString(PREF_KEY_ACCESS_TOKEN) ?: "" 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/create/CreateRoomActivity.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.create 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.databinding.DataBindingUtil.setContentView 6 | import dagger.hilt.android.AndroidEntryPoint 7 | import org.sopt.santamanitto.R 8 | import org.sopt.santamanitto.databinding.ActivityCreateRoomBinding 9 | 10 | @AndroidEntryPoint 11 | class CreateRoomActivity : AppCompatActivity() { 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(this, R.layout.activity_create_room) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/create/ExpirationExt.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.create 2 | 3 | import androidx.appcompat.widget.AppCompatTextView 4 | import androidx.databinding.BindingAdapter 5 | import org.sopt.santamanitto.R 6 | import org.sopt.santamanitto.room.create.data.ExpirationLiveData 7 | import org.sopt.santamanitto.view.SantaPeriodPicker 8 | import kotlin.math.exp 9 | 10 | @BindingAdapter("expirationDiff") 11 | fun setExpirationDiff(view: AppCompatTextView, expiration: ExpirationLiveData) { 12 | view.text = String.format(view.context.getText(R.string.createroom_preview_start).toString(), expiration.period) 13 | } 14 | 15 | @BindingAdapter("expirationPreview") 16 | fun setExpirationPreview(view: AppCompatTextView, expiration: ExpirationLiveData) { 17 | val context = view.context 18 | val amPmStr = if (expiration.isAm) { 19 | context.getString(R.string.am) 20 | } else { 21 | context.getString(R.string.pm) 22 | } 23 | view.text = 24 | String.format(context.getText(R.string.createroom_preview).toString(), 25 | expiration.year, expiration.month, expiration.day, 26 | amPmStr, expiration.hour, expiration.minute) 27 | } 28 | 29 | @BindingAdapter("expirationTime") 30 | fun setExpirationTime(view: AppCompatTextView, expiration: ExpirationLiveData) { 31 | view.text = String.format(view.context.getText(R.string.createroom_expiration_time).toString(), 32 | expiration.hour, expiration.minute) 33 | } 34 | 35 | @BindingAdapter("expirationPeriod") 36 | fun setExpirationPeriod(view: SantaPeriodPicker, expiration: ExpirationLiveData) { 37 | if (view.period != expiration.period) { 38 | view.period = expiration.period 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/create/adaptor/AddMissionViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.create.adaptor 2 | 3 | import android.content.Context 4 | import android.view.inputmethod.InputMethodManager 5 | import androidx.recyclerview.widget.RecyclerView 6 | import org.sopt.santamanitto.databinding.ItemAddMissionBinding 7 | 8 | class AddMissionViewHolder( 9 | private val callback: CreateMissionAdaptor.CreateMissionCallback, 10 | private val binding: ItemAddMissionBinding, 11 | ) : RecyclerView.ViewHolder(binding.root) { 12 | private var currentMission: String? = null 13 | 14 | fun bind() { 15 | binding.btnAddMission.setOnClickListener { 16 | if (!currentMission.isNullOrBlank()) { 17 | callback.onMissionInserted(currentMission!!) 18 | } 19 | } 20 | binding.root.setOnClickListener { 21 | hideKeyboard() 22 | } 23 | } 24 | 25 | fun updateText(mission: String?) { 26 | currentMission = mission 27 | } 28 | 29 | private fun hideKeyboard() { 30 | val imm = 31 | itemView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager 32 | imm.hideSoftInputFromWindow(itemView.windowToken, 0) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/create/adaptor/CreateConfirmAdaptor.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.create.adaptor 2 | 3 | class CreateConfirmAdaptor(createMissionCallback: CreateMissionCallback) : CreateMissionAdaptor(createMissionCallback) { 4 | 5 | override fun getItemCount(): Int { 6 | return missions.size 7 | } 8 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/create/adaptor/CreateMissionViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.create.adaptor 2 | 3 | import android.text.Editable 4 | import android.text.TextWatcher 5 | import android.view.KeyEvent 6 | import androidx.recyclerview.widget.RecyclerView 7 | import org.sopt.santamanitto.databinding.ItemCreateMissionBinding 8 | 9 | class CreateMissionViewHolder( 10 | private val callback: CreateMissionAdaptor.CreateMissionCallback, 11 | private val binding: ItemCreateMissionBinding, 12 | private val onTextChanged: (String?) -> Unit, 13 | ) : RecyclerView.ViewHolder(binding.root) { 14 | init { 15 | with(binding) { 16 | etCreateMission.setOnKeyListener { _, keyCode, _ -> 17 | return@setOnKeyListener keyCode == KeyEvent.KEYCODE_ENTER 18 | } 19 | etCreateMission.addTextChangeListener( 20 | object : TextWatcher { 21 | override fun beforeTextChanged( 22 | s: CharSequence?, 23 | start: Int, 24 | count: Int, 25 | after: Int, 26 | ) { 27 | } 28 | 29 | override fun onTextChanged( 30 | s: CharSequence?, 31 | start: Int, 32 | before: Int, 33 | count: Int, 34 | ) { 35 | } 36 | 37 | override fun afterTextChanged(editable: Editable?) { 38 | if (editable?.isNotBlank() == true) { 39 | etCreateMission.setDeleteImage() 40 | } else { 41 | etCreateMission.removeButton() 42 | } 43 | if (bindingAdapterPosition == bindingAdapter?.itemCount?.minus(2)) { 44 | onTextChanged(editable?.toString()) 45 | } 46 | } 47 | }, 48 | ) 49 | } 50 | } 51 | 52 | fun bind(mission: String?) { 53 | binding.etCreateMission.run { 54 | text = mission 55 | setButtonClickListener { text -> callback.onMissionDeleted(text) } 56 | if (mission == null) { 57 | compress(false) 58 | isEditable = true 59 | } else { 60 | compress(true) 61 | } 62 | if (bindingAdapterPosition == bindingAdapter?.itemCount?.minus(2)) { 63 | requestFocus() 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/create/data/CreateMissionLiveList.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.create.data 2 | 3 | import androidx.lifecycle.LiveData 4 | 5 | class CreateMissionLiveList : LiveData() { 6 | 7 | private val missions = mutableListOf() 8 | 9 | init { 10 | value = this 11 | } 12 | 13 | fun addMission(mission: String) { 14 | missions.add(mission) 15 | value = this 16 | } 17 | 18 | fun deleteMission(mission: String) { 19 | missions.remove(mission) 20 | value = this 21 | } 22 | 23 | fun getMissions(): List { 24 | return missions 25 | } 26 | 27 | fun isEmpty(): Boolean { 28 | return missions.isEmpty() 29 | } 30 | 31 | fun clear() { 32 | missions.clear() 33 | value = this 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/create/network/CreateRoomModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.create.network 2 | 3 | data class CreateRoomModel( 4 | val invitationCode: String, 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/create/network/CreateRoomRequestModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.create.network 2 | 3 | data class CreateRoomRequestModel( 4 | val roomName: String, 5 | val expirationDate: String, 6 | val missionContents: List = mutableListOf(), 7 | ) 8 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/create/network/ModifyRoomRequestModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.create.network 2 | 3 | data class ModifyRoomRequestModel( 4 | val roomName: String, 5 | val expirationDate: String 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/data/MissionContentModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.data 2 | 3 | data class MissionContentModel( 4 | val content: String 5 | ) -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/data/PersonalRoomModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.data 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import org.sopt.santamanitto.room.data.MyManittoModel.Member.Manitto 5 | import org.sopt.santamanitto.room.data.MyManittoModel.Mission 6 | 7 | data class PersonalRoomModel( 8 | @SerializedName("manitto") val manitto: Manitto, 9 | @SerializedName("mission") val mission: Mission 10 | ) 11 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/join/JoinRoomViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.join 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.map 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import org.sopt.santamanitto.NetworkViewModel 8 | import org.sopt.santamanitto.room.join.network.JoinRoomRequestModel 9 | import org.sopt.santamanitto.room.join.network.JoinRoomResponseModel 10 | import org.sopt.santamanitto.room.network.RoomRequest 11 | import org.sopt.santamanitto.room.network.RoomRequest.JoinRoomError 12 | import javax.inject.Inject 13 | 14 | @HiltViewModel 15 | class JoinRoomViewModel @Inject constructor( 16 | private val roomRequest: RoomRequest 17 | ) : NetworkViewModel() { 18 | 19 | val invitationCode = MutableLiveData(null) 20 | 21 | val isInvitationCodeEmpty = invitationCode.map { 22 | it.isNullOrEmpty() 23 | } 24 | 25 | private val _isWrongInvitationCode = MutableLiveData(false) 26 | val isWrongInvitationCode: LiveData 27 | get() = _isWrongInvitationCode 28 | 29 | private val _isAlreadyMatchedRoom = MutableLiveData(false) 30 | val isAlreadyMatchedRoom: LiveData 31 | get() = _isAlreadyMatchedRoom 32 | 33 | private val _isAlreadyEnteredRoom = MutableLiveData(false) 34 | val isAlreadyEnteredRoom: LiveData 35 | get() = _isAlreadyEnteredRoom 36 | 37 | fun joinRoom(callback: (JoinRoomResponseModel) -> Unit) { 38 | roomRequest.joinRoom(JoinRoomRequestModel(invitationCode.value!!), 39 | object : RoomRequest.JoinRoomCallback { 40 | 41 | override fun onSuccessJoinRoom(joinedRoom: JoinRoomResponseModel) { 42 | callback(joinedRoom) 43 | } 44 | 45 | override fun onFailed(joinRoomError: JoinRoomError) { 46 | when (joinRoomError) { 47 | JoinRoomError.WrongInvitationCode -> _isWrongInvitationCode.value = true 48 | JoinRoomError.AlreadyMatched -> _isAlreadyMatchedRoom.value = true 49 | JoinRoomError.AlreadyEntered -> _isAlreadyEnteredRoom.value = true 50 | else -> _networkErrorOccur.value = true 51 | } 52 | } 53 | }) 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/join/network/JoinRoomErrorModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.join.network 2 | 3 | data class JoinRoomErrorModel( 4 | val message: String 5 | ) -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/join/network/JoinRoomModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.join.network 2 | 3 | data class JoinRoomResponseModel( 4 | val roomId: String 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/join/network/JoinRoomRequestModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.join.network 2 | 3 | data class JoinRoomRequestModel( 4 | val invitationCode: String 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/manittoroom/ManittoRoomActivity.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.manittoroom 2 | 3 | import android.os.Bundle 4 | import androidx.activity.viewModels 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.databinding.DataBindingUtil.setContentView 7 | import dagger.hilt.android.AndroidEntryPoint 8 | import org.sopt.santamanitto.R 9 | import org.sopt.santamanitto.databinding.ActivityManittoRoomBinding 10 | import timber.log.Timber 11 | 12 | @AndroidEntryPoint 13 | class ManittoRoomActivity : AppCompatActivity() { 14 | 15 | companion object { 16 | private const val TAG = "ManittoRoomActivity" 17 | const val EXTRA_ROOM_ID = "roomId" 18 | const val EXTRA_IS_MATCHED = "isMatched" 19 | const val EXTRA_IS_FINISHED = "isFinished" 20 | } 21 | 22 | private val viewModel: ManittoRoomViewModel by viewModels() 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | setContentView(this, R.layout.activity_manitto_room) 27 | 28 | val roomId = intent.getStringExtra(EXTRA_ROOM_ID) ?: "" 29 | if (roomId.isBlank()) { 30 | Timber.tag(TAG).e("Wrong access.") 31 | finish() 32 | } else { 33 | viewModel.roomId = roomId 34 | } 35 | val isMatched = intent.getBooleanExtra(EXTRA_IS_MATCHED, false) 36 | viewModel.isMatched = isMatched 37 | val isFinished = intent.getBooleanExtra(EXTRA_IS_FINISHED, false) 38 | viewModel.isFinished = isFinished 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/manittoroom/MemberAdapter.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.manittoroom 2 | 3 | import android.view.ViewGroup 4 | import androidx.recyclerview.widget.ListAdapter 5 | import org.sopt.santamanitto.room.manittoroom.network.ManittoRoomMember 6 | import org.sopt.santamanitto.util.ItemDiffCallback 7 | 8 | class MemberAdapter : ListAdapter(DiffUtil) { 9 | 10 | override fun onCreateViewHolder( 11 | parent: ViewGroup, 12 | viewType: Int 13 | ): MemberViewHolder = MemberViewHolder(parent) 14 | 15 | override fun onBindViewHolder(holder: MemberViewHolder, position: Int) { 16 | holder.bind(getItem(position)) 17 | } 18 | 19 | companion object { 20 | private val DiffUtil = ItemDiffCallback( 21 | onContentsTheSame = { oldItem, newItem -> oldItem.santa.userId == newItem.santa.userId }, 22 | onItemsTheSame = { oldItem, newItem -> oldItem == newItem } 23 | ) 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/manittoroom/MemberViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.manittoroom 2 | 3 | import android.view.ViewGroup 4 | import org.sopt.santamanitto.R 5 | import org.sopt.santamanitto.databinding.ItemMemberBinding 6 | import org.sopt.santamanitto.room.manittoroom.network.ManittoRoomMember 7 | import org.sopt.santamanitto.util.RandomUtil 8 | import org.sopt.santamanitto.view.recyclerview.BaseViewHolder 9 | 10 | class MemberViewHolder(parent: ViewGroup) : 11 | BaseViewHolder(R.layout.item_member, parent) { 12 | 13 | companion object { 14 | private val imageSource = arrayOf(R.drawable.ic_santa_ic, R.drawable.ic_rudolf_ic) 15 | } 16 | 17 | override fun bind(data: ManittoRoomMember) { 18 | binding.run { 19 | imageviewMemberitemImage.setImageResource( 20 | imageSource[RandomUtil.getRandomValue(imageSource.size)] 21 | ) 22 | textviewMemberitemName.text = data.santa.userName 23 | } 24 | } 25 | 26 | override fun clear() { 27 | binding.run { 28 | imageviewMemberitemImage.setImageResource(0) 29 | textviewMemberitemName.text = "" 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/manittoroom/ResultAdapter.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.manittoroom 2 | 3 | import android.view.ViewGroup 4 | import androidx.recyclerview.widget.ListAdapter 5 | import org.sopt.santamanitto.room.manittoroom.network.ManittoRoomMember 6 | import org.sopt.santamanitto.util.ItemDiffCallback 7 | 8 | class ResultAdapter() : ListAdapter(DiffUtil) { 9 | 10 | override fun onCreateViewHolder( 11 | parent: ViewGroup, 12 | viewType: Int 13 | ): ResultViewHolder = ResultViewHolder(parent) 14 | 15 | override fun onBindViewHolder(holder: ResultViewHolder, position: Int) { 16 | holder.bind(getItem(position)) 17 | } 18 | 19 | companion object { 20 | private val DiffUtil = ItemDiffCallback( 21 | onContentsTheSame = { oldItem, newItem -> oldItem.manitto.userId == newItem.manitto.userId }, 22 | onItemsTheSame = { oldItem, newItem -> oldItem == newItem } 23 | ) 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/manittoroom/ResultViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.manittoroom 2 | 3 | import android.view.ViewGroup 4 | import org.sopt.santamanitto.R 5 | import org.sopt.santamanitto.databinding.ItemResultBinding 6 | import org.sopt.santamanitto.room.manittoroom.network.ManittoRoomMember 7 | import org.sopt.santamanitto.view.recyclerview.BaseViewHolder 8 | 9 | class ResultViewHolder( 10 | parent: ViewGroup, 11 | ) : BaseViewHolder(R.layout.item_result, parent) { 12 | 13 | override fun bind(data: ManittoRoomMember) { 14 | binding.run { 15 | textviewItemresultSanta.text = data.santa.userName 16 | textviewItemresultMinitto.text = data.manitto.userName 17 | } 18 | } 19 | 20 | override fun clear() { 21 | binding.run { 22 | textviewItemresultSanta.text = "" 23 | textviewItemresultMinitto.text = "" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/manittoroom/fragment/MatchingFragment.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.manittoroom.fragment 2 | 3 | import android.os.Bundle 4 | import android.os.Handler 5 | import android.os.Looper 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.fragment.app.Fragment 10 | import androidx.fragment.app.activityViewModels 11 | import androidx.lifecycle.viewModelScope 12 | import androidx.navigation.fragment.findNavController 13 | import dagger.hilt.android.AndroidEntryPoint 14 | import kotlinx.coroutines.Dispatchers 15 | import kotlinx.coroutines.launch 16 | import kotlinx.coroutines.withContext 17 | import org.sopt.santamanitto.analytics.AmplitudeManager 18 | import org.sopt.santamanitto.analytics.EventType 19 | import org.sopt.santamanitto.databinding.FragmentMatchingBinding 20 | import org.sopt.santamanitto.room.manittoroom.ManittoRoomViewModel 21 | import org.sopt.santamanitto.room.manittoroom.fragment.MatchingFragmentDirections.Companion.actionMatchingFragmentToMatchedFragment 22 | 23 | @AndroidEntryPoint 24 | class MatchingFragment : Fragment() { 25 | 26 | companion object { 27 | private const val DELAY_MILLIS = 2000L 28 | } 29 | 30 | private lateinit var binding: FragmentMatchingBinding 31 | 32 | private val viewModel: ManittoRoomViewModel by activityViewModels() 33 | 34 | private var isDelayDone = false 35 | 36 | private var isInBackground = false 37 | 38 | override fun onCreateView( 39 | inflater: LayoutInflater, 40 | container: ViewGroup?, 41 | savedInstanceState: Bundle? 42 | ): View { 43 | binding = FragmentMatchingBinding.inflate(inflater, container, false) 44 | 45 | AmplitudeManager.trackEvent("manitto_matching_lottie", EventType.PAGE) 46 | 47 | Handler(Looper.getMainLooper()).postDelayed({ 48 | isDelayDone = true 49 | }, DELAY_MILLIS) 50 | 51 | return binding.root 52 | } 53 | 54 | override fun onResume() { 55 | super.onResume() 56 | isInBackground = false 57 | navigateMissionFragment() 58 | } 59 | 60 | override fun onPause() { 61 | super.onPause() 62 | isInBackground = true 63 | } 64 | 65 | private fun navigateMissionFragment() { 66 | viewModel.viewModelScope.launch(Dispatchers.Default) { 67 | while ((!viewModel.isMatched || !isDelayDone) && !isInBackground) { 68 | } 69 | if (isInBackground) { 70 | return@launch 71 | } 72 | 73 | withContext(Dispatchers.Main) { 74 | findNavController().navigate(actionMatchingFragmentToMatchedFragment()) 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/manittoroom/network/ManittoRoomMember.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.manittoroom.network 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class ManittoRoomMember( 6 | val santa: SantaRoomInfo, 7 | val manitto: ManittoRoomInfo 8 | ) { 9 | data class SantaRoomInfo( 10 | @SerializedName("id") val userId: String, 11 | @SerializedName("username") val userName: String, 12 | @SerializedName("missionId") val missionId: String, 13 | ) 14 | 15 | data class ManittoRoomInfo( 16 | @SerializedName("id") val userId: String, 17 | @SerializedName("username") val userName: String, 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/manittoroom/network/ManittoRoomModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.manittoroom.network 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class ManittoRoomModel( 6 | @SerializedName("id") val roomId: String, 7 | val roomName: String, 8 | val invitationCode: String, 9 | val createdAt: String, 10 | val expirationDate: String, 11 | val matchingDate: String?, 12 | val deletedByCreatorDate: String?, 13 | @SerializedName("Creator") val creator: ManittoRoomCreator, 14 | @SerializedName("Missions") val missions: List, 15 | @SerializedName("Members") val members: List, 16 | ) { 17 | data class ManittoRoomCreator( 18 | @SerializedName("id") val userId: String, 19 | @SerializedName("username") val userName: String, 20 | val manittoUserId: String?, 21 | ) 22 | 23 | data class ManittoRoomMission( 24 | @SerializedName("id") val missionId: String, 25 | val content: String, 26 | ) 27 | 28 | val isMatched: Boolean 29 | get() = matchingDate != null 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/network/ExitRoomRequestModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.network 2 | 3 | data class ExitRoomRequestModel( 4 | val roomId: String 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/network/RoomNetworkModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.network 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | import org.sopt.santamanitto.network.AuthRetrofitClient 8 | import retrofit2.Retrofit 9 | import javax.inject.Singleton 10 | 11 | @InstallIn(SingletonComponent::class) 12 | @Module 13 | class RoomNetworkModule { 14 | 15 | @Provides 16 | @Singleton 17 | fun provideRoomService(@AuthRetrofitClient retrofitClient: Retrofit): RoomService = 18 | retrofitClient.create(RoomService::class.java) 19 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/network/RoomRequest.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.network 2 | 3 | import org.sopt.santamanitto.room.create.network.CreateRoomModel 4 | import org.sopt.santamanitto.room.create.network.CreateRoomRequestModel 5 | import org.sopt.santamanitto.room.create.network.ModifyRoomRequestModel 6 | import org.sopt.santamanitto.room.data.MyManittoModel 7 | import org.sopt.santamanitto.room.data.PersonalRoomModel 8 | import org.sopt.santamanitto.room.join.network.JoinRoomRequestModel 9 | import org.sopt.santamanitto.room.join.network.JoinRoomResponseModel 10 | import org.sopt.santamanitto.room.manittoroom.network.ManittoRoomModel 11 | 12 | interface RoomRequest { 13 | 14 | interface CreateRoomCallback { 15 | fun onRoomCreated(createdRoom: CreateRoomModel) 16 | 17 | fun onFailed() 18 | } 19 | 20 | interface JoinRoomCallback { 21 | fun onSuccessJoinRoom(joinedRoom: JoinRoomResponseModel) 22 | 23 | fun onFailed(joinRoomError: JoinRoomError) 24 | } 25 | 26 | interface GetManittoRoomCallback { 27 | fun onLoadManittoRoomData(manittoRoom: ManittoRoomModel) 28 | 29 | fun onFailed() 30 | } 31 | 32 | interface GetPersonalRoomInfoCallback { 33 | fun onLoadPersonalRoomInfo(personalRoom: PersonalRoomModel) 34 | 35 | fun onDataNotAvailable() 36 | } 37 | 38 | enum class JoinRoomError { 39 | WrongInvitationCode, AlreadyMatched, AlreadyEntered, Els 40 | } 41 | 42 | suspend fun getRooms(): List 43 | 44 | fun createRoom(request: CreateRoomRequestModel, callback: CreateRoomCallback) 45 | 46 | fun modifyRoom( 47 | roomId: String, 48 | request: ModifyRoomRequestModel, 49 | callback: (onSuccess: Boolean) -> Unit 50 | ) 51 | 52 | fun joinRoom(request: JoinRoomRequestModel, callback: JoinRoomCallback) 53 | 54 | fun getManittoRoomData(roomId: String, callback: GetManittoRoomCallback) 55 | 56 | fun matchManitto(roomId: String, callback: (onSuccess: Boolean) -> Unit) 57 | 58 | fun getPersonalRoomInfo(roomId: String, callback: GetPersonalRoomInfoCallback) 59 | 60 | fun exitRoom(roomId: String, callback: (onSuccess: Boolean) -> Unit) 61 | 62 | fun removeHistory(roomId: String, callback: (onSuccess: Boolean) -> Unit) 63 | 64 | suspend fun deleteRoom(roomId: String): Result 65 | 66 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/room/network/RoomService.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.network 2 | 3 | import org.sopt.santamanitto.network.Response 4 | import org.sopt.santamanitto.network.SimpleResponse 5 | import org.sopt.santamanitto.room.create.network.CreateRoomModel 6 | import org.sopt.santamanitto.room.create.network.CreateRoomRequestModel 7 | import org.sopt.santamanitto.room.create.network.ModifyRoomRequestModel 8 | import org.sopt.santamanitto.room.data.MyManittoModel 9 | import org.sopt.santamanitto.room.data.PersonalRoomModel 10 | import org.sopt.santamanitto.room.join.network.JoinRoomRequestModel 11 | import org.sopt.santamanitto.room.join.network.JoinRoomResponseModel 12 | import org.sopt.santamanitto.room.manittoroom.network.ManittoRoomModel 13 | import retrofit2.Call 14 | import retrofit2.http.Body 15 | import retrofit2.http.DELETE 16 | import retrofit2.http.GET 17 | import retrofit2.http.PATCH 18 | import retrofit2.http.POST 19 | import retrofit2.http.Path 20 | 21 | interface RoomService { 22 | @GET("rooms") 23 | suspend fun getRooms(): Response> 24 | 25 | @GET("rooms/{roomId}/my") 26 | fun getRoomPersonalInfo( 27 | @Path("roomId") roomId: String 28 | ): Call> 29 | 30 | @POST("rooms") 31 | fun createRoom( 32 | @Body request: CreateRoomRequestModel 33 | ): Call> 34 | 35 | @PATCH("rooms/{roomId}") 36 | fun modifyRoom( 37 | @Path("roomId") roomId: String, 38 | @Body request: ModifyRoomRequestModel 39 | ): Call 40 | 41 | @POST("rooms/enter") 42 | fun joinRoom( 43 | @Body request: JoinRoomRequestModel 44 | ): Call> 45 | 46 | @GET("rooms/{roomId}") 47 | fun getManittoRoomData( 48 | @Path("roomId") roomId: String 49 | ): Call> 50 | 51 | @POST("rooms/{roomId}/match") 52 | fun matchManitto( 53 | @Path("roomId") roomId: String 54 | ): Call 55 | 56 | @DELETE("rooms/{roomId}/exit") 57 | fun exitRoom( 58 | @Path("roomId") roomId: String 59 | ): Call 60 | 61 | @DELETE("rooms/{roomId}/history") 62 | fun removeHistory( 63 | @Path("roomId") roomId: String 64 | ): Call 65 | 66 | @DELETE("rooms/{roomId}") 67 | suspend fun deleteRoom( 68 | @Path("roomId") roomId: String 69 | ): SimpleResponse 70 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/setting/SettingFragment.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.setting 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.navigation.fragment.findNavController 6 | import dagger.hilt.android.AndroidEntryPoint 7 | import org.sopt.santamanitto.BuildConfig 8 | import org.sopt.santamanitto.R 9 | import org.sopt.santamanitto.analytics.AmplitudeManager 10 | import org.sopt.santamanitto.analytics.EventType 11 | import org.sopt.santamanitto.databinding.FragmentSettingBinding 12 | import org.sopt.santamanitto.util.base.BaseFragment 13 | 14 | @AndroidEntryPoint 15 | class SettingFragment : BaseFragment(R.layout.fragment_setting, false) { 16 | 17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 18 | super.onViewCreated(view, savedInstanceState) 19 | 20 | AmplitudeManager.trackEvent("setting", EventType.PAGE) 21 | binding.run { 22 | buttonSettingBack.setOnClickListener { findNavController().navigateUp() } 23 | settinglistviewSetting 24 | .addSetting(getString(R.string.setting_1_edit_name)) { 25 | AmplitudeManager.trackEvent("setting_name_edit_btn", EventType.BUTTON) 26 | findNavController().navigate( 27 | SettingFragmentDirections.actionSettingFragmentToEditNameFragment(), 28 | ) 29 | } 30 | .addSetting(getString(R.string.setting_2_inquiry)) { 31 | goToWebViewFragment(BuildConfig.INQUIRY_URL) 32 | } 33 | .addSetting(getString(R.string.setting_3_tos)) { 34 | goToWebViewFragment(BuildConfig.TOS_URL) 35 | } 36 | .addSetting(getString(R.string.setting_4_privacy_policy)) { 37 | goToWebViewFragment(BuildConfig.PRIVACY_POLICY_RUL) 38 | } 39 | .commit() 40 | } 41 | } 42 | 43 | private fun goToWebViewFragment(url: String) { 44 | findNavController().navigate( 45 | SettingFragmentDirections.actionSettingFragmentToWebViewFragmentFromSetting( 46 | url, 47 | ), 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/setting/SettingListView.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.setting 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import android.view.ViewGroup 7 | import androidx.recyclerview.widget.LinearLayoutManager 8 | import androidx.recyclerview.widget.RecyclerView 9 | import org.sopt.santamanitto.R 10 | import org.sopt.santamanitto.databinding.ItemSettingBinding 11 | 12 | class SettingListView @JvmOverloads constructor( 13 | context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 14 | ) : RecyclerView(context, attrs, defStyleAttr) { 15 | 16 | companion object { 17 | private const val VIEW_HOLDER_RES = R.layout.item_setting 18 | } 19 | 20 | private val adapter = SettingListViewAdapter() 21 | 22 | init { 23 | itemAnimator = null 24 | setHasFixedSize(true) 25 | overScrollMode = OVER_SCROLL_NEVER 26 | layoutManager = LinearLayoutManager(context) 27 | setAdapter(adapter) 28 | } 29 | 30 | fun addSetting(title: String, listener: () -> Unit): SettingListView { 31 | adapter.addSetting(Setting(title, listener)) 32 | return this 33 | } 34 | 35 | fun commit() { 36 | adapter.notifyDataSetChanged() 37 | } 38 | 39 | private class SettingListViewAdapter : Adapter() { 40 | 41 | private val settingList = mutableListOf() 42 | 43 | fun addSetting(setting: Setting) { 44 | settingList.add(setting) 45 | } 46 | 47 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingListViewHolder { 48 | return SettingListViewHolder(parent) 49 | } 50 | 51 | override fun onBindViewHolder(holder: SettingListViewHolder, position: Int) { 52 | holder.onBind(settingList[position].title, settingList[position].listener) 53 | } 54 | 55 | override fun getItemCount(): Int { 56 | return settingList.size 57 | } 58 | } 59 | 60 | private class SettingListViewHolder(parent: ViewGroup) : ViewHolder( 61 | LayoutInflater.from(parent.context) 62 | .inflate(VIEW_HOLDER_RES, parent, false) 63 | ) { 64 | 65 | private val binding = ItemSettingBinding.bind(itemView) 66 | 67 | fun onBind(title: String, listener: () -> Unit) { 68 | binding.run { 69 | root.setOnClickListener { listener.invoke() } 70 | textviewSettingitemTitle.text = title 71 | } 72 | } 73 | } 74 | 75 | private data class Setting( 76 | val title: String, 77 | val listener: () -> Unit 78 | ) 79 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/update/version/Version.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.update.version 2 | 3 | class Version private constructor(private val version: String) : Comparable { 4 | 5 | companion object { 6 | const val MAJOR = 0 7 | const val MINOR = 1 8 | const val FETCH = 2 9 | 10 | private val versionRegex = Regex("\\d+.\\d+.\\d+") 11 | 12 | fun create(version: String): Version { 13 | if (!version.matches(versionRegex)) { 14 | throw IllegalArgumentException("Version must be $versionRegex. Received version is \"$version\"") 15 | } 16 | return Version(version) 17 | } 18 | } 19 | 20 | private val parsedVersion: IntArray 21 | 22 | val major: Int 23 | get() = parsedVersion[MAJOR] 24 | 25 | val minor: Int 26 | get() = parsedVersion[MINOR] 27 | 28 | val fetch: Int 29 | get() = parsedVersion[FETCH] 30 | 31 | override fun compareTo(other: Version): Int { 32 | return version.compareTo(other.version) 33 | } 34 | 35 | fun compare(version: String, what: Int): Int { 36 | val other = create(version) 37 | return parsedVersion[what].compareTo(other.get(what)) 38 | } 39 | 40 | fun get(what: Int): Int { 41 | if (what !in 0 until 2) { 42 | throw IllegalArgumentException("what is in 0 ~ 2") 43 | } 44 | return parsedVersion[what] 45 | } 46 | 47 | init { 48 | val parsed = version.split(".") 49 | val temp = IntArray(2) 50 | for (i in 0 until 2) { 51 | temp[i] = parsed[i].toInt() 52 | } 53 | parsedVersion = temp 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/UserModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.provider.Settings 6 | import dagger.Module 7 | import dagger.Provides 8 | import dagger.hilt.InstallIn 9 | import dagger.hilt.android.qualifiers.ApplicationContext 10 | import dagger.hilt.components.SingletonComponent 11 | import org.sopt.santamanitto.preference.UserPreferenceManager 12 | import org.sopt.santamanitto.user.data.source.CachedUserMetadataSource 13 | import org.sopt.santamanitto.user.data.source.UserMetadataSource 14 | import javax.inject.Named 15 | import javax.inject.Singleton 16 | 17 | @InstallIn(SingletonComponent::class) 18 | @Module 19 | class UserModule { 20 | @SuppressLint("HardwareIds") 21 | @Provides 22 | @Named("serialNumber") 23 | fun provideSerialNumber( 24 | @ApplicationContext context: Context, 25 | ): String = Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) 26 | 27 | @Provides 28 | @Singleton 29 | fun provideCachedUserMetadataSource(userPreferenceManager: UserPreferenceManager): CachedUserMetadataSource = 30 | CachedUserMetadataSource(userPreferenceManager) 31 | 32 | @Provides 33 | @Singleton 34 | fun provideUserMetadataSource(userPreferenceManager: UserPreferenceManager): UserMetadataSource = 35 | CachedUserMetadataSource(userPreferenceManager) 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/data/UserInfoModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.data 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class UserInfoModel( 6 | @SerializedName("id") val userId: Int, 7 | @SerializedName("username") val userName: String, 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/data/controller/UserAuthController.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.data.controller 2 | 3 | import org.sopt.santamanitto.user.data.UserInfoModel 4 | 5 | interface UserAuthController { 6 | 7 | interface GetUserInfoCallback { 8 | fun onUserInfoLoaded(userInfoModel: UserInfoModel) 9 | 10 | fun onDataNotAvailable() 11 | } 12 | 13 | suspend fun changeUserName(newName: String): Result 14 | 15 | fun getUserInfo(userId: String, callback: GetUserInfoCallback) 16 | 17 | suspend fun withdraw(): Result 18 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/data/controller/UserController.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.data.controller 2 | 3 | import org.sopt.santamanitto.auth.data.response.SignInResponseModel 4 | import org.sopt.santamanitto.auth.data.response.SignUpResponseModel 5 | 6 | interface UserController { 7 | suspend fun login(serialNumber: String): Result 8 | 9 | suspend fun createAccount( 10 | userName: String, 11 | serialNumber: String, 12 | ): Result 13 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/data/source/CachedMainUserDataSource.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.data.source 2 | 3 | //class CachedMainUserDataSource @Inject constructor( 4 | // private val userMetadataSource: UserMetadataSource, 5 | // private val userAuthController: UserAuthController 6 | //) : MainUserDataSource { 7 | // 8 | // var isMyManittoDirty = true 9 | // 10 | // private var myManittoModels: List? = null 11 | // 12 | // override suspend fun getMyManittoList(): List { 13 | // if (isMyManittoDirty || myManittoModels == null) { 14 | // val rooms = 15 | // 16 | // userAuthController.getUserInfo( 17 | // userMetadataSource.getUserId(), 18 | // object : UserAuthController.GetUserInfoCallback { 19 | // override fun onUserInfoLoaded(userInfoModel: UserInfoModel) { 20 | // isMyManittoDirty = false 21 | //// myManittoModels = userInfoModel.myManittoModels.reversed() 22 | // callback.onMyManittoListLoaded(myManittoModels!!) 23 | // } 24 | // 25 | // override fun onDataNotAvailable() { 26 | // callback.onDataNotAvailable() 27 | // } 28 | // }) 29 | // } else { 30 | // callback.onMyManittoListLoaded(myManittoModels!!) 31 | // } 32 | // } 33 | //} -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/data/source/CachedUserMetadataSource.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.data.source 2 | 3 | class CachedUserMetadataSource(private val userPreferenceManager: UserMetadataSource) : 4 | UserMetadataSource { 5 | private var cachedUserId: String? = null 6 | private var cachedAccessToken: String? = null 7 | private var cachedUserName: String? = null 8 | 9 | private var userNameIsDirty = false 10 | 11 | override fun getUserId(): String { 12 | if (cachedUserId == null) { 13 | cachedUserId = userPreferenceManager.getUserId() 14 | } 15 | return cachedUserId!! 16 | } 17 | 18 | override fun getAccessToken(): String { 19 | if (cachedAccessToken == null) { 20 | cachedAccessToken = userPreferenceManager.getAccessToken() 21 | } 22 | return cachedAccessToken!! 23 | } 24 | 25 | override fun getUserName(): String { 26 | if (cachedUserName == null || userNameIsDirty) { 27 | userNameIsDirty = true 28 | cachedUserName = userPreferenceManager.getUserName() 29 | } 30 | return cachedUserName!! 31 | } 32 | 33 | override fun setUserId(userId: String) { 34 | cachedUserId = userId 35 | userPreferenceManager.setUserId(userId) 36 | } 37 | 38 | override fun setAccessToken(accessToken: String) { 39 | cachedAccessToken = accessToken 40 | userPreferenceManager.setAccessToken(accessToken) 41 | } 42 | 43 | override fun setUserName(userName: String) { 44 | cachedUserName = userName 45 | userPreferenceManager.setUserName(userName) 46 | } 47 | 48 | fun setUserNameDirty() { 49 | userNameIsDirty = true 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/data/source/UserMetadataSource.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.data.source 2 | 3 | interface UserMetadataSource { 4 | fun getUserId(): String 5 | 6 | fun getAccessToken(): String 7 | 8 | fun getUserName(): String 9 | 10 | fun setUserId(userId: String) 11 | 12 | fun setAccessToken(accessToken: String) 13 | 14 | fun setUserName(userName: String) 15 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/mypage/UserNameModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.mypage 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class UserNameModel( 6 | val id: Int, 7 | @SerializedName("username") 8 | val userName: String, 9 | val serialNumber: String, 10 | val isDeleted: Boolean, 11 | val createdAt: Boolean, 12 | val updatedAt: Boolean 13 | ) -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/mypage/UserNameRequestModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.mypage 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class UserNameRequestModel( 6 | @SerializedName("username") val userName: String 7 | ) 8 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/network/UserAuthService.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.network 2 | 3 | import org.sopt.santamanitto.network.Response 4 | import org.sopt.santamanitto.network.SimpleResponse 5 | import org.sopt.santamanitto.user.data.UserInfoModel 6 | import org.sopt.santamanitto.user.mypage.UserNameRequestModel 7 | import retrofit2.Call 8 | import retrofit2.http.Body 9 | import retrofit2.http.DELETE 10 | import retrofit2.http.GET 11 | import retrofit2.http.PUT 12 | import retrofit2.http.Path 13 | 14 | interface UserAuthService { 15 | @GET("users/{userId}") 16 | fun getUserInfo( 17 | @Path("userId") userId: String 18 | ): Call> 19 | 20 | @PUT("users/my/nickname") 21 | suspend fun changeUserName( 22 | @Body request: UserNameRequestModel 23 | ): Response 24 | 25 | @DELETE("users/my") 26 | suspend fun withdraw(): SimpleResponse 27 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/network/UserNetworkModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.network 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | import org.sopt.santamanitto.network.AuthRetrofitClient 8 | import org.sopt.santamanitto.network.OtherRetrofitClient 9 | import retrofit2.Retrofit 10 | 11 | @InstallIn(SingletonComponent::class) 12 | @Module 13 | class UserNetworkModule { 14 | 15 | @Provides 16 | fun provideUserService(@OtherRetrofitClient retrofitClient: Retrofit): UserService = 17 | retrofitClient.create(UserService::class.java) 18 | 19 | @Provides 20 | fun provideUserAuthService(@AuthRetrofitClient retrofitClient: Retrofit): UserAuthService = 21 | retrofitClient.create(UserAuthService::class.java) 22 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/network/UserService.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.network 2 | 3 | import org.sopt.santamanitto.auth.data.request.SignInRequestModel 4 | import org.sopt.santamanitto.auth.data.request.SignUpRequestModel 5 | import org.sopt.santamanitto.auth.data.response.SignInResponseModel 6 | import org.sopt.santamanitto.auth.data.response.SignUpResponseModel 7 | import org.sopt.santamanitto.network.Response 8 | import org.sopt.santamanitto.user.data.UserInfoModel 9 | import org.sopt.santamanitto.user.mypage.UserNameModel 10 | import org.sopt.santamanitto.user.mypage.UserNameRequestModel 11 | import retrofit2.Call 12 | import retrofit2.http.Body 13 | import retrofit2.http.GET 14 | import retrofit2.http.POST 15 | import retrofit2.http.PUT 16 | import retrofit2.http.Path 17 | 18 | 19 | interface UserService { 20 | @POST("auth/sign-up") 21 | suspend fun createAccount( 22 | @Body body: SignUpRequestModel 23 | ): Response 24 | 25 | @POST("auth/sign-in") 26 | suspend fun login( 27 | @Body body: SignInRequestModel 28 | ): Response 29 | 30 | @Deprecated("deprecated", ReplaceWith("UserAuthService#getUserInfo")) 31 | @GET("users/{userId}") 32 | fun getUserInfo( 33 | @Path("userId") userId: Int 34 | ): Call> 35 | 36 | @Deprecated("deprecated", ReplaceWith("UserAuthService#changeUserName")) 37 | @PUT("users/{userId}") 38 | fun changeUserName( 39 | @Path("userId") userId: Int, 40 | @Body request: UserNameRequestModel 41 | ): Call> 42 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/signin/SignInActivity.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.signin 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import androidx.databinding.DataBindingUtil.* 6 | import dagger.hilt.android.AndroidEntryPoint 7 | import org.sopt.santamanitto.R 8 | import org.sopt.santamanitto.databinding.ActivitySignInBinding 9 | 10 | @AndroidEntryPoint 11 | class SignInActivity : AppCompatActivity() { 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(this, R.layout.activity_sign_in) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/signin/fragment/WebLinkFragment.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.signin.fragment 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.os.Bundle 6 | import androidx.fragment.app.Fragment 7 | import androidx.navigation.fragment.findNavController 8 | import androidx.navigation.fragment.navArgs 9 | 10 | 11 | class WebLinkFragment : Fragment() { 12 | 13 | private val args: WebLinkFragmentArgs by navArgs() 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | val browserIntent = 18 | Intent(Intent.ACTION_VIEW, Uri.parse(args.url)) 19 | startActivity(browserIntent) 20 | findNavController().navigateUp() 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/signin/viewmodel/ConditionViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.signin.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import kotlinx.coroutines.launch 8 | import org.sopt.santamanitto.analytics.AmplitudeManager 9 | import org.sopt.santamanitto.user.data.controller.UserController 10 | import org.sopt.santamanitto.user.data.source.UserMetadataSource 11 | import javax.inject.Inject 12 | import javax.inject.Named 13 | 14 | @HiltViewModel 15 | class ConditionViewModel @Inject constructor( 16 | private val userController: UserController, 17 | private val userMetadataSource: UserMetadataSource, 18 | @Named("serialNumber") private val serialNumber: String 19 | ) : ViewModel() { 20 | 21 | val isReady = MutableLiveData() 22 | val userSaveSuccess = MutableLiveData(false) 23 | val userSaveFail = MutableLiveData(false) 24 | val isUserExist = MutableLiveData(false) 25 | 26 | private var _isWaitingForResponse = false 27 | val isWaitingForResponse: Boolean 28 | get() = _isWaitingForResponse 29 | 30 | fun signIn(userName: String) { 31 | if (_isWaitingForResponse) { 32 | return 33 | } 34 | _isWaitingForResponse = true 35 | 36 | viewModelScope.launch { 37 | val result = userController.createAccount(userName, serialNumber) 38 | _isWaitingForResponse = false 39 | 40 | result.onSuccess { signUpResponseModel -> 41 | userMetadataSource.run { 42 | setUserName(userName) 43 | setAccessToken(signUpResponseModel.accessToken) 44 | setUserId(signUpResponseModel.id) 45 | } 46 | AmplitudeManager.run { 47 | setUserId(signUpResponseModel.id) 48 | updateStringProperty("user_name", userName) 49 | } 50 | userSaveSuccess.value = true 51 | _isWaitingForResponse = false 52 | }.onFailure { exception -> 53 | when { 54 | exception.message?.contains("409") == true -> { 55 | isUserExist.value = true 56 | _isWaitingForResponse = false 57 | } 58 | 59 | else -> { 60 | userSaveFail.value = true 61 | _isWaitingForResponse = false 62 | } 63 | } 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/user/signin/viewmodel/EnterNameViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.signin.viewmodel 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.SharingStarted 7 | import kotlinx.coroutines.flow.StateFlow 8 | import kotlinx.coroutines.flow.map 9 | import kotlinx.coroutines.flow.stateIn 10 | 11 | class EnterNameViewModel : ViewModel() { 12 | 13 | private val _userName = MutableStateFlow("") 14 | val userName: StateFlow = _userName 15 | 16 | val isUserNameValid: StateFlow = userName.map { it.isNotBlank() } 17 | .stateIn(viewModelScope, SharingStarted.Eagerly, false) 18 | 19 | fun setUserName(newName: String) { 20 | _userName.value = newName 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/util/BindingAdapters.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.util 2 | 3 | import android.view.View 4 | import androidx.databinding.BindingAdapter 5 | 6 | object BindingAdapters { 7 | @BindingAdapter("layout_height") 8 | fun setLayoutHeight(view: View, height: Int) { 9 | val layoutParams = view.layoutParams 10 | layoutParams.height = height 11 | view.layoutParams = layoutParams 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/util/ClipBoardUtil.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.util 2 | 3 | import android.content.ClipData 4 | import android.content.ClipboardManager 5 | import android.content.Context 6 | 7 | object ClipBoardUtil { 8 | 9 | fun copy(context: Context, label: String, content: String) { 10 | val manager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager 11 | val clip = ClipData.newPlainText(label, content) 12 | manager.setPrimaryClip(clip) 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/util/FragmentUtil.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.util 2 | 3 | import android.content.Context 4 | import android.view.MotionEvent 5 | import android.view.inputmethod.InputMethodManager 6 | import android.widget.EditText 7 | import androidx.fragment.app.Fragment 8 | 9 | object FragmentUtil { 10 | 11 | fun Fragment.hideKeyboardOnOutsideEditText() { 12 | view?.setOnTouchListener { v, event -> 13 | if (event.action == MotionEvent.ACTION_DOWN) { 14 | val currentFocusView = activity?.currentFocus 15 | if (currentFocusView is EditText) { 16 | val imm = 17 | activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager 18 | imm?.hideSoftInputFromWindow(currentFocusView.windowToken, 0) 19 | currentFocusView.clearFocus() 20 | } 21 | v.performClick() 22 | } 23 | false 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/util/ItemDiffCallback.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.util 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | 5 | class ItemDiffCallback( 6 | val onItemsTheSame: (T, T) -> Boolean, 7 | val onContentsTheSame: (T, T) -> Boolean 8 | ) : DiffUtil.ItemCallback() { 9 | override fun areItemsTheSame( 10 | oldItem: T, 11 | newItem: T 12 | ): Boolean = onItemsTheSame(oldItem, newItem) 13 | 14 | override fun areContentsTheSame( 15 | oldItem: T, 16 | newItem: T 17 | ): Boolean = onContentsTheSame(oldItem, newItem) 18 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/util/MetricsUtil.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.util 2 | 3 | import android.content.Context 4 | import android.content.res.Resources 5 | import android.util.DisplayMetrics 6 | 7 | object MetricsUtil { 8 | @JvmStatic 9 | fun convertDpToPixel(dp: Float, context: Context?): Float { 10 | return if (context != null) { 11 | val resources = context.resources 12 | val metrics = resources.displayMetrics 13 | (dp * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)) 14 | } else { 15 | val metrics = Resources.getSystem().displayMetrics 16 | (dp * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)) 17 | } 18 | } 19 | 20 | fun convertPixelsToDp(px: Float, context: Context?): Float { 21 | return if (context != null) { 22 | val resources = context.resources 23 | val metrics = resources.displayMetrics 24 | (px / (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)) 25 | } else { 26 | val metrics = Resources.getSystem().displayMetrics 27 | (px / (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)) 28 | } 29 | } 30 | } 31 | 32 | internal fun Int.toDP(): Int { 33 | return MetricsUtil.convertPixelsToDp(this.toFloat(), null).toInt() 34 | } 35 | 36 | internal fun Int.toPixel() : Int { 37 | return MetricsUtil.convertDpToPixel(this.toFloat(), null).toInt() 38 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/util/RandomUtil.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.util 2 | 3 | import java.util.* 4 | 5 | 6 | object RandomUtil { 7 | 8 | fun getRandomValue(maxValue: Int): Int { 9 | return Random().nextInt(maxValue) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/util/RoomSortUtil.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.util 2 | 3 | import org.sopt.santamanitto.main.list.RoomState 4 | import org.sopt.santamanitto.room.data.MyManittoModel 5 | import org.sopt.santamanitto.room.data.getRoomState 6 | 7 | object RoomSortUtil { 8 | fun getSortedRooms(rooms: List): List { 9 | return rooms.sortedWith(compareBy { room -> 10 | // 1차 정렬: 방 상태 우선순위 11 | when (room.getRoomState()) { 12 | RoomState.IN_PROGRESS -> 0 13 | RoomState.WAITING -> 1 14 | RoomState.FINISHED -> 2 15 | RoomState.EXPIRED -> 3 16 | RoomState.DELETED -> 4 17 | RoomState.LEFT -> 5 18 | } 19 | }.thenByDescending { room -> 20 | // 2차 정렬: 상태별 세부 정렬 21 | when (room.getRoomState()) { 22 | RoomState.IN_PROGRESS -> { 23 | // 결과 공개 예정일 기준 오름차순 24 | room.expirationDate?.let { 25 | TimeUtil.convertKstToCalendar(it).timeInMillis 26 | } ?: 0 27 | } 28 | 29 | RoomState.WAITING -> { 30 | // 방 생성 일자 내림차순 31 | -(TimeUtil.convertKstToCalendar(room.createdAt).timeInMillis) 32 | } 33 | 34 | RoomState.FINISHED -> { 35 | // 방 종료 일자 내림차순 36 | room.expirationDate?.let { 37 | -(TimeUtil.convertKstToCalendar(it).timeInMillis) 38 | } ?: 0 39 | } 40 | 41 | RoomState.DELETED -> { 42 | // 방 생성 일자 내림차순 43 | -(TimeUtil.convertKstToCalendar(room.createdAt).timeInMillis) 44 | } 45 | 46 | else -> 0 47 | } 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/util/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.util.base 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.view.WindowManager 8 | import androidx.annotation.LayoutRes 9 | import androidx.databinding.DataBindingUtil 10 | import androidx.databinding.ViewDataBinding 11 | import androidx.fragment.app.Fragment 12 | 13 | open class BaseFragment( 14 | @LayoutRes private val layoutRes: Int, 15 | private val adjustPan: Boolean, 16 | ) : Fragment() { 17 | protected lateinit var binding: B 18 | 19 | override fun onCreateView( 20 | inflater: LayoutInflater, 21 | container: ViewGroup?, 22 | savedInstanceState: Bundle?, 23 | ): View { 24 | binding = DataBindingUtil.inflate(inflater, layoutRes, container, false) 25 | binding.lifecycleOwner = viewLifecycleOwner 26 | return binding.root 27 | } 28 | 29 | override fun onStart() { 30 | super.onStart() 31 | 32 | if (adjustPan) { 33 | requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) 34 | } else { 35 | requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/LiveCheckbox.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.appcompat.widget.AppCompatCheckBox 6 | import androidx.lifecycle.LiveData 7 | import androidx.lifecycle.MutableLiveData 8 | 9 | class LiveCheckbox : AppCompatCheckBox { 10 | //@JvmOverloads 로는 터치가 안먹음 11 | constructor(context: Context) : super(context) 12 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) 13 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) 14 | 15 | private val _isChecked: MutableLiveData? = MutableLiveData() 16 | val isCheckedLiveData: LiveData 17 | get() = _isChecked!! 18 | 19 | init { 20 | //라이브데이터가 늦게 생성되기 때문에 여기서 값 초기화 21 | _isChecked?.value = isChecked 22 | } 23 | 24 | //체크될 때마다 라이브데이터 값도 갱신 25 | override fun setChecked(checked: Boolean) { 26 | super.setChecked(checked) 27 | //attrs로 값 초기화 시 아직 라이브데이터가 생성되지 않을 수 있음 28 | _isChecked?.value = checked 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/SantaCardView.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.cardview.widget.CardView 6 | import org.sopt.santamanitto.R 7 | 8 | open class SantaCardView @JvmOverloads constructor( 9 | context: Context, 10 | attrs: AttributeSet? = null, 11 | defStyleAttr: Int = 0 12 | ) : CardView(context, attrs, defStyleAttr) { 13 | 14 | init { 15 | radius = getDimen(R.dimen.radius_cardview) 16 | cardElevation = getDimen(R.dimen.elevation_shadow) 17 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { 18 | outlineSpotShadowColor = getColor(R.color.shadow_gray) 19 | } 20 | setRippleEffect(true) 21 | 22 | val typeArray = context.theme.obtainStyledAttributes( 23 | attrs, 24 | R.styleable.SantaCardView, 25 | 0, 0 26 | ) 27 | 28 | if (typeArray.hasValue(R.styleable.SantaCardView_moreRound)) { 29 | val moreRound = typeArray.getBoolean(R.styleable.SantaCardView_moreRound, false) 30 | if (moreRound) { 31 | radius = getDimen(R.dimen.radius_cardview_more) 32 | } 33 | } 34 | 35 | if (typeArray.hasValue(R.styleable.SantaCardView_cardBackgroundColor)) { 36 | val color = typeArray.getColor(R.styleable.SantaCardView_cardBackgroundColor, getColor(R.color.white)) 37 | setCardBackgroundColor(color) 38 | } else { 39 | setCardBackgroundColor(getColor(R.color.white)) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/SantaCheckBoxBindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view 2 | 3 | import android.widget.CheckBox 4 | import androidx.databinding.BindingAdapter 5 | import androidx.databinding.InverseBindingAdapter 6 | import androidx.databinding.InverseBindingListener 7 | import org.sopt.santamanitto.R 8 | 9 | object SantaCheckBoxBindingAdapter { 10 | @BindingAdapter("setChecked") 11 | @JvmStatic 12 | fun setChecked(view: SantaCheckBox, isChecked: Boolean) { 13 | if (view.isChecked != isChecked) { 14 | view.isChecked = isChecked 15 | } 16 | } 17 | 18 | @InverseBindingAdapter(attribute = "setChecked", event = "checkedAttrChanged") 19 | @JvmStatic 20 | fun getChecked(view: SantaCheckBox): Boolean { 21 | return view.findViewById(R.id.checkbox_santacheckbox).isChecked 22 | } 23 | 24 | @BindingAdapter("checkedAttrChanged") 25 | @JvmStatic 26 | fun setListener(view: SantaCheckBox, listener: InverseBindingListener) { 27 | val checkBox = view.findViewById(R.id.checkbox_santacheckbox) 28 | checkBox.setOnCheckedChangeListener { _, _ -> 29 | listener.onChange() 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/SantaEditTextBindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view 2 | 3 | import android.text.Editable 4 | import android.text.TextWatcher 5 | import androidx.appcompat.widget.AppCompatEditText 6 | import androidx.databinding.BindingAdapter 7 | import androidx.databinding.InverseBindingAdapter 8 | import androidx.databinding.InverseBindingListener 9 | import org.sopt.santamanitto.R 10 | import org.sopt.santamanitto.view.SantaEditText 11 | 12 | object SantaEditTextBindingAdapter { 13 | @BindingAdapter("setText") 14 | @JvmStatic 15 | fun setText(view: SantaEditText, text: String?) { 16 | if (view.text != text) { 17 | view.text = text 18 | } 19 | } 20 | 21 | @InverseBindingAdapter(attribute = "setText", event = "textAttrChanged") 22 | @JvmStatic 23 | fun getText(view: SantaEditText) : String? { 24 | return view.text 25 | } 26 | 27 | @BindingAdapter("textAttrChanged") 28 | @JvmStatic 29 | fun setListener(view: SantaEditText, listener: InverseBindingListener) { 30 | view.addTextChangeListener(object: TextWatcher { 31 | override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { } 32 | 33 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { 34 | listener.onChange() 35 | } 36 | 37 | override fun afterTextChanged(s: Editable?) { } 38 | }) 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/SantaIndicator.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import android.widget.FrameLayout 7 | import androidx.core.content.ContextCompat 8 | import org.sopt.santamanitto.R 9 | import org.sopt.santamanitto.databinding.SantaIndicatorBinding 10 | 11 | class SantaIndicator @JvmOverloads constructor( 12 | context: Context, 13 | attrs: AttributeSet? = null, 14 | defStyleAttr: Int = 0 15 | ) : FrameLayout(context, attrs, defStyleAttr) { 16 | 17 | private val binding = SantaIndicatorBinding.inflate( 18 | LayoutInflater.from(context), 19 | this, true 20 | ) 21 | 22 | private val circle = binding.imageviewSantaindicator 23 | 24 | private val number = binding.textviewSantaindicator 25 | 26 | var isCurrentIndicator: Boolean 27 | get() = circle.imageTintList == ContextCompat.getColorStateList(context, R.color.navy) 28 | set(value) { 29 | if (value) { 30 | circle.imageTintList = ContextCompat.getColorStateList(context, R.color.navy) 31 | } else { 32 | circle.imageTintList = ContextCompat.getColorStateList(context, R.color.gray_1) 33 | } 34 | } 35 | 36 | init { 37 | val typeArray = context.theme.obtainStyledAttributes( 38 | attrs, 39 | R.styleable.SantaIndicator, 40 | 0, 0) 41 | 42 | //선택 여부 43 | if (typeArray.hasValue(R.styleable.SantaIndicator_isSelected)) { 44 | isCurrentIndicator = typeArray.getBoolean(R.styleable.SantaIndicator_isSelected, true) 45 | } 46 | 47 | //넘버 48 | if (typeArray.hasValue(R.styleable.SantaIndicator_number)) { 49 | number.text = typeArray.getInt(R.styleable.SantaIndicator_number, 1).toString() 50 | } 51 | 52 | typeArray.recycle() 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/SantaLoading.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.widget.FrameLayout 8 | import androidx.databinding.DataBindingUtil 9 | import org.sopt.santamanitto.R 10 | import org.sopt.santamanitto.databinding.SantaLoadingBinding 11 | 12 | class SantaLoading @JvmOverloads constructor( 13 | context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 14 | ) : FrameLayout(context, attrs, defStyleAttr) { 15 | 16 | private val binding = DataBindingUtil.inflate( 17 | LayoutInflater.from(context), 18 | R.layout.santa_loading, 19 | this, true 20 | ) 21 | 22 | private val progressbar = binding.progressbarSantaloading 23 | 24 | private val textView = binding.textviewSantaloading 25 | 26 | fun setError(isError: Boolean) { 27 | if (isError) { 28 | progressbar.visibility = View.GONE 29 | textView.visibility = View.VISIBLE 30 | } else { 31 | textView.visibility = View.GONE 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/SantaNameInput.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view 2 | 3 | import android.content.Context 4 | import android.text.Editable 5 | import android.text.TextWatcher 6 | import android.util.AttributeSet 7 | import android.view.LayoutInflater 8 | import org.sopt.santamanitto.databinding.SantaNameInputBinding 9 | 10 | class SantaNameInput @JvmOverloads constructor( 11 | context: Context, 12 | attrs: AttributeSet? = null, 13 | defStyleAttr: Int = 0 14 | ) : SantaCardView(context, attrs, defStyleAttr) { 15 | 16 | private val binding = SantaNameInputBinding.inflate( 17 | LayoutInflater.from(context), 18 | this 19 | ) 20 | 21 | private val nameInput = binding.edittextNameinputName 22 | // private val alertMessage = binding.textviewNameinputAlert 23 | 24 | var hint: String? 25 | get() = nameInput.hint 26 | set(value) { 27 | nameInput.hint = value 28 | } 29 | 30 | fun observeTextChanges(onTextChanged: (String) -> Unit) { 31 | nameInput.addTextChangeListener(object : TextWatcher { 32 | override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} 33 | 34 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { 35 | onTextChanged(s.toString()) 36 | } 37 | 38 | override fun afterTextChanged(s: Editable?) {} 39 | }) 40 | } 41 | 42 | fun setText(text: String) { 43 | if (nameInput.text.toString() != text) { 44 | nameInput.text = text 45 | } 46 | } 47 | 48 | fun getText(): String { 49 | return nameInput.text.toString() 50 | } 51 | 52 | // init { 53 | // alertMessage.setTextByIdWithArgs(R.string.santanameinput_alert, MAX_LENGTH) 54 | // 55 | // addTextChangeListener(SantaEditText.SantaEditLimitLengthWatcher(nameInput, MAX_LENGTH) { isWarning -> 56 | // if (isWarning) { 57 | // alertMessage.setTextColorById(R.color.red) 58 | // } else { 59 | // alertMessage.setTextColorById(R.color.gray_3) 60 | // } 61 | // }) 62 | // 63 | // } 64 | 65 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/TextObservable.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view 2 | 3 | import android.text.Editable 4 | import android.text.TextWatcher 5 | import androidx.databinding.InverseBindingListener 6 | 7 | interface TextObservable { 8 | 9 | var text: String? 10 | 11 | fun addTextChangeListener(textWatcher: TextWatcher) 12 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/ViewExt.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view 2 | 3 | import android.graphics.Typeface 4 | import android.graphics.drawable.Drawable 5 | import android.util.TypedValue 6 | 7 | import android.view.View 8 | import android.widget.TextView 9 | import androidx.annotation.ColorRes 10 | import androidx.annotation.DimenRes 11 | import androidx.annotation.DrawableRes 12 | import androidx.annotation.StringRes 13 | import androidx.core.content.ContextCompat 14 | 15 | fun View.setPaddingVerticalById(@DimenRes id: Int) { 16 | val dimen = resources.getDimension(id).toInt() 17 | setPadding(paddingLeft, dimen, paddingRight, dimen) 18 | } 19 | 20 | fun View.setBackgroundTint(@ColorRes id: Int) { 21 | backgroundTintList = ContextCompat.getColorStateList(context, id) 22 | } 23 | 24 | fun View.setRippleEffect(isEnabled: Boolean) { 25 | foreground = if (isEnabled) { 26 | val outValue = TypedValue() 27 | context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true) 28 | ContextCompat.getDrawable(context, outValue.resourceId) 29 | } else { 30 | null 31 | } 32 | } 33 | 34 | fun View.getDimen(@DimenRes id: Int): Float { 35 | return resources.getDimension(id) 36 | } 37 | 38 | fun View.getColor(@ColorRes id: Int): Int { 39 | return ContextCompat.getColor(context, id) 40 | } 41 | 42 | fun View.getDrawable(@DrawableRes id: Int): Drawable? { 43 | return ContextCompat.getDrawable(context, id) 44 | } 45 | 46 | fun View.setPadding(padding: Int) { 47 | setPadding(padding, padding, padding, padding) 48 | } 49 | 50 | fun TextView.setTextColorById(@ColorRes id: Int) { 51 | setTextColor(getColor(id)) 52 | } 53 | 54 | fun TextView.setTextSize(@DimenRes id: Int) { 55 | setTextSize(TypedValue.COMPLEX_UNIT_PX, getDimen(id)) 56 | } 57 | 58 | fun TextView.setBold(isEnabled: Boolean) { 59 | if (isEnabled) { 60 | setTypeface(typeface, Typeface.BOLD) 61 | } else { 62 | typeface = Typeface.DEFAULT 63 | } 64 | } 65 | 66 | fun TextView.setTextById(@StringRes id: Int) { 67 | text = resources.getString(id) 68 | } 69 | 70 | fun TextView.setTextByIdWithArgs(@StringRes id: Int, vararg args: Any) { 71 | text = resources.getString(id, *args) 72 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/dialog/RoundDialogButton.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view.dialog 2 | 3 | import android.view.View 4 | 5 | internal data class RoundDialogButton( 6 | val text: String, 7 | val canDismiss: Boolean, 8 | val listener: ((content: View?) -> Unit)? 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/dialog/exit/ExitDialogCreator.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view.dialog.exit 2 | 3 | import android.content.Context 4 | import androidx.fragment.app.DialogFragment 5 | import org.sopt.santamanitto.R 6 | import org.sopt.santamanitto.view.dialog.RoundDialogBuilder 7 | 8 | object ExitDialogCreator { 9 | fun create( 10 | context: Context, roomName: String, isHost: Boolean, listener: (() -> Unit) 11 | ): DialogFragment { 12 | val message = if (isHost) { 13 | context.getString(R.string.exit_dialog_host) 14 | } else { 15 | String.format(context.getString(R.string.exit_dialog_guest), roomName) 16 | } 17 | return RoundDialogBuilder() 18 | .setContentText(message) 19 | .addHorizontalButton(context.getString(R.string.exit_dialog_host_cancel)) 20 | .addHorizontalButton(context.getString(R.string.exit_dialog_host_confirm)) { 21 | listener() 22 | } 23 | .build() 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/dialog/withdraw/WithdrawDialogCreator.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view.dialog.withdraw 2 | 3 | import android.content.Context 4 | import androidx.fragment.app.DialogFragment 5 | import org.sopt.santamanitto.R 6 | import org.sopt.santamanitto.view.dialog.RoundDialogBuilder 7 | 8 | object WithdrawDialogCreator { 9 | fun create(context: Context, listener: () -> Unit): DialogFragment { 10 | val message = context.getString(R.string.withdraw_dialog_title) 11 | 12 | return RoundDialogBuilder() 13 | .setContentText(message) 14 | .addHorizontalButton(context.getString(R.string.withdraw_dialog_cancel)) { 15 | listener() 16 | } 17 | .addHorizontalButton(context.getString(R.string.withdraw_dialog_confirm), true) 18 | .build() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/recyclerview/BaseAdapter.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view.recyclerview 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | 5 | abstract class BaseAdapter : RecyclerView.Adapter>() { 6 | 7 | protected var items: MutableList = mutableListOf() 8 | 9 | override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { 10 | holder.bind(items[position]) 11 | } 12 | 13 | override fun getItemCount(): Int = items.size 14 | 15 | fun setList(data: List) { 16 | if (data == items) { 17 | return 18 | } 19 | clear() 20 | addAll(data) 21 | } 22 | 23 | 24 | fun add(data: T) { 25 | insert(data, items.size) 26 | } 27 | 28 | 29 | fun addAll(data: List?) { 30 | if (data == null) { 31 | return 32 | } 33 | 34 | val startIndex = items.size 35 | items.addAll(startIndex, data) 36 | notifyItemRangeInserted(startIndex, data.size) 37 | } 38 | 39 | fun getItem(position: Int): T { 40 | return items[position] 41 | } 42 | 43 | 44 | fun insert(data: T, position: Int) { 45 | items.add(position, data) 46 | notifyItemInserted(position) 47 | } 48 | 49 | 50 | fun remove(position: Int) { 51 | items.removeAt(position) 52 | notifyItemRemoved(position) 53 | } 54 | 55 | 56 | fun change(position: Int, data: T) { 57 | items[position] = data 58 | notifyItemChanged(position) 59 | } 60 | 61 | open fun clear() { 62 | val size = items.size 63 | items.clear() 64 | notifyItemRangeRemoved(0, size) 65 | } 66 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/recyclerview/BaseViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view.recyclerview 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.annotation.LayoutRes 6 | import androidx.databinding.DataBindingUtil 7 | import androidx.databinding.ViewDataBinding 8 | import androidx.recyclerview.widget.RecyclerView 9 | 10 | abstract class BaseViewHolder( 11 | @LayoutRes layoutRes: Int, 12 | parent: ViewGroup 13 | ) : RecyclerView 14 | .ViewHolder(LayoutInflater.from(parent.context) 15 | .inflate(layoutRes, parent, false)) { 16 | 17 | protected var binding: B = DataBindingUtil.bind(itemView)!! 18 | 19 | abstract fun bind(data: T) 20 | abstract fun clear() 21 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/recyclerview/MarginDecoration.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view.recyclerview 2 | 3 | import android.graphics.Rect 4 | import android.view.View 5 | import androidx.recyclerview.widget.RecyclerView 6 | 7 | class MarginDecoration(private val margin: Float, private val orientation: Int) : RecyclerView.ItemDecoration() { 8 | 9 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { 10 | super.getItemOffsets(outRect, view, parent, state) 11 | 12 | val position = parent.getChildAdapterPosition(view) 13 | val itemCount = state.itemCount 14 | 15 | orientation.let { 16 | if (position != itemCount - 1) { 17 | when (orientation) { 18 | RecyclerView.HORIZONTAL -> outRect.right = margin.toInt() 19 | RecyclerView.VERTICAL -> outRect.bottom = margin.toInt() 20 | } 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/recyclerview/RecyclerViewExt.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view.recyclerview 2 | 3 | import android.view.View 4 | import androidx.databinding.BindingAdapter 5 | import androidx.recyclerview.widget.LinearLayoutManager 6 | import androidx.recyclerview.widget.ListAdapter 7 | import androidx.recyclerview.widget.RecyclerView 8 | 9 | @BindingAdapter("replaceAll") 10 | fun RecyclerView.replaceAll(list: List?) { 11 | (this.adapter as? ListAdapter)?.let { adapter -> 12 | adapter.submitList(list?.toList()) 13 | this.visibility = if (list.isNullOrEmpty()) View.INVISIBLE else View.VISIBLE 14 | } ?: (this.adapter as? BaseAdapter)?.run { 15 | if (list != null) { 16 | setList(list) 17 | // 모든 마진이 15dp로 설정되어있어서 일단 하드코딩, 추후 바인딩 어댑터 해제시 참고 18 | this@replaceAll.setItemMargin(15f) 19 | this@replaceAll.visibility = if (list.isEmpty()) { 20 | View.INVISIBLE 21 | } else { 22 | View.VISIBLE 23 | } 24 | } else { 25 | clear() 26 | this@replaceAll.visibility = View.INVISIBLE 27 | } 28 | } 29 | } 30 | 31 | @BindingAdapter("setItemMargin") 32 | fun RecyclerView.setItemMargin(dimen: Float) { 33 | val orientation = (layoutManager as LinearLayoutManager?)?.orientation ?: return 34 | removeItemDecoration(MarginDecoration(dimen, orientation)) 35 | addItemDecoration(MarginDecoration(dimen, orientation)) 36 | } -------------------------------------------------------------------------------- /app/src/main/java/org/sopt/santamanitto/view/santanumberpicker/SantaNumberPickerSnapScrollListener.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.view.santanumberpicker 2 | 3 | import androidx.recyclerview.widget.LinearSnapHelper 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | class SantaNumberPickerSnapScrollListener( 7 | private val recyclerView: RecyclerView, 8 | private val onSnapPositionChangedListener : (position: Int) -> Unit 9 | ): RecyclerView.OnScrollListener() { 10 | 11 | private val snapHelper = LinearSnapHelper().apply { attachToRecyclerView(recyclerView) } 12 | private var previousSnapPosition = RecyclerView.NO_POSITION 13 | 14 | override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { 15 | val currentSnapPosition = getSnapPosition(recyclerView) 16 | if (previousSnapPosition != currentSnapPosition) { 17 | onSnapPositionChangedListener(currentSnapPosition) 18 | previousSnapPosition = currentSnapPosition 19 | } 20 | } 21 | 22 | private fun getSnapPosition(recyclerView: RecyclerView): Int { 23 | val layoutManager = recyclerView.layoutManager ?: return RecyclerView.NO_POSITION 24 | val snapView = snapHelper.findSnapView(layoutManager) ?: return RecyclerView.NO_POSITION 25 | return layoutManager.getPosition(snapView) 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_invitation_code_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_round_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_santaedittext_edittext.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cursor_edittext.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_alert.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 13 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_right.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_btn_add.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_btn_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_btn_cancel.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_circle_30.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_minus_dark_gray.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_minus_light_gray.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_my_page.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_plus_dark_gray.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_plus_light_gray.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_refresh.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_setting.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_snow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_snow_matching.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 26 | 31 | 36 | 41 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_ractangle_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_ractangle_background_30.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_ractangle_background_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_ractangle_background_4_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_ractangle_bottom_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/scrollbar_round_30.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_santa_check_box.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_dark_gray_fill_10_rect.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_light_gray_fill_4_rect.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/font/pretendard_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/font/pretendard_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/pretendard_font_family.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 21 | 22 | 29 | 30 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/font/pretendard_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/font/pretendard_medium.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/pretendard_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/font/pretendard_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/pretendard_semi_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/font/pretendard_semi_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_create_room.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_manitto_room.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_sign_in.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_create_room_time_picker.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 30 | 31 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_invitation_code.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 21 | 22 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_text_content_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_matching.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 10 | 19 | 20 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 19 | 20 | 30 | 31 | 40 | 41 | 42 | 43 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_webview.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_add_mission.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 21 | 22 | 37 | 38 | 48 | 49 | 50 | 51 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_create_mission.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_member.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 10 | 21 | 22 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 30 | 31 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/santa_check_box.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 15 | 16 | 26 | 27 | 40 | 41 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/res/layout/santa_indicator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 19 | 20 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/santa_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 21 | 22 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /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.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/navigation/navigation_create_room.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 16 | 19 | 23 | 24 | 25 | 30 | 33 | 34 | 35 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/navigation/navigation_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 16 | 19 | 20 | 21 | 26 | 27 | 32 | 33 | 38 | 39 | 42 | 43 | 46 | 47 | 48 | 49 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/res/navigation/navigation_signin.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 15 | 16 | 21 | 24 | 27 | 28 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/values-v28/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #EFEFEF 5 | #C4C4C4 6 | #ECECEC 7 | #F7F7F7 8 | #2F5846 9 | #E3E3E3 10 | 11 | #666666 12 | #DADADA 13 | #F4F4F4 14 | 15 | #F31523 16 | #00154B 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors_common.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF000000 4 | #FFFFFFFF 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/font_style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 16 | 17 | 22 | 23 | 28 | 29 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/mock/ic_launcher_dev-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/ic_launcher_dev-playstore.png -------------------------------------------------------------------------------- /app/src/mock/java/org/sopt/santamanitto/room/network/FakeRoomRequestModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.network 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | 8 | @InstallIn(SingletonComponent::class) 9 | @Module 10 | class FakeRoomRequestModule { 11 | 12 | @Provides 13 | fun provideFakeCreateRoomRequest(): RoomRequest = FakeRoomRequest() 14 | } -------------------------------------------------------------------------------- /app/src/mock/java/org/sopt/santamanitto/user/UserRemoteModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | import org.sopt.santamanitto.user.data.controller.FakeUserAuthController 8 | import org.sopt.santamanitto.user.data.controller.FakeUserController 9 | import org.sopt.santamanitto.user.data.controller.UserAuthController 10 | import org.sopt.santamanitto.user.data.controller.UserController 11 | import javax.inject.Singleton 12 | 13 | @InstallIn(SingletonComponent::class) 14 | @Module 15 | class UserRemoteModule { 16 | 17 | @Provides 18 | @Singleton 19 | fun provideUserAuthController(): UserAuthController = FakeUserAuthController() 20 | 21 | @Provides 22 | @Singleton 23 | fun provideUserController(): UserController = FakeUserController() 24 | } -------------------------------------------------------------------------------- /app/src/mock/java/org/sopt/santamanitto/user/data/controller/FakeUserAuthController.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.data.controller 2 | 3 | import org.sopt.santamanitto.user.data.UserInfoModel 4 | 5 | class FakeUserAuthController : UserAuthController { 6 | 7 | private val fakeUsers = HashMap().apply { 8 | put("1", UserInfoModel(1, "fakeUser1")) 9 | put("2", UserInfoModel(2, "fakeUser2")) 10 | put("3", UserInfoModel(3, "fakeUser3")) 11 | put("4", UserInfoModel(4, "fakeUser4")) 12 | put("5", UserInfoModel(5, "fakeUser5")) 13 | put("6", UserInfoModel(6, "fakeUser6")) 14 | } 15 | 16 | override suspend fun changeUserName( 17 | newName: String, 18 | ): Result { 19 | return Result.success(true) 20 | } 21 | 22 | override fun getUserInfo(userId: String, callback: UserAuthController.GetUserInfoCallback) { 23 | if (fakeUsers.containsKey(userId)) { 24 | callback.onUserInfoLoaded(fakeUsers[userId]!!) 25 | } else { 26 | callback.onDataNotAvailable() 27 | } 28 | } 29 | 30 | override suspend fun withdraw(): Result { 31 | return Result.success(Unit) 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/mock/java/org/sopt/santamanitto/user/data/controller/FakeUserController.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user.data.controller 2 | 3 | import kotlinx.coroutines.delay 4 | import org.sopt.santamanitto.auth.data.response.SignInResponseModel 5 | import org.sopt.santamanitto.auth.data.response.SignUpResponseModel 6 | 7 | class FakeUserController : UserController { 8 | 9 | private val fakeLoginUser = SignInResponseModel( 10 | accessToken = "f0a1k2e3u4e5s6e7r8", 11 | id = "1" 12 | ) 13 | 14 | private val fakeSignUpUser = SignUpResponseModel( 15 | accessToken = "fake1access2token3", 16 | id = "1" 17 | ) 18 | 19 | override suspend fun login(serialNumber: String): Result { 20 | return try { 21 | Result.success(fakeLoginUser) 22 | } catch (e: Exception) { 23 | Result.failure(e) 24 | } 25 | } 26 | 27 | override suspend fun createAccount( 28 | userName: String, 29 | serialNumber: String 30 | ): Result { 31 | return try { 32 | delay(2000) 33 | Result.success(fakeSignUpUser) 34 | } catch (e: Exception) { 35 | Result.failure(e) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-anydpi-v26/ic_launcher_dev.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-anydpi-v26/ic_launcher_dev_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-hdpi/ic_launcher_dev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-hdpi/ic_launcher_dev.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-hdpi/ic_launcher_dev_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-hdpi/ic_launcher_dev_foreground.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-hdpi/ic_launcher_dev_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-hdpi/ic_launcher_dev_round.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-mdpi/ic_launcher_dev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-mdpi/ic_launcher_dev.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-mdpi/ic_launcher_dev_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-mdpi/ic_launcher_dev_foreground.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-mdpi/ic_launcher_dev_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-mdpi/ic_launcher_dev_round.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-xhdpi/ic_launcher_dev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-xhdpi/ic_launcher_dev.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-xhdpi/ic_launcher_dev_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-xhdpi/ic_launcher_dev_foreground.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-xhdpi/ic_launcher_dev_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-xhdpi/ic_launcher_dev_round.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-xxhdpi/ic_launcher_dev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-xxhdpi/ic_launcher_dev.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-xxhdpi/ic_launcher_dev_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-xxhdpi/ic_launcher_dev_foreground.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-xxhdpi/ic_launcher_dev_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-xxhdpi/ic_launcher_dev_round.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-xxxhdpi/ic_launcher_dev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-xxxhdpi/ic_launcher_dev.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-xxxhdpi/ic_launcher_dev_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-xxxhdpi/ic_launcher_dev_foreground.webp -------------------------------------------------------------------------------- /app/src/mock/res/mipmap-xxxhdpi/ic_launcher_dev_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/mock/res/mipmap-xxxhdpi/ic_launcher_dev_round.webp -------------------------------------------------------------------------------- /app/src/mock/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | mock manitto 3 | -------------------------------------------------------------------------------- /app/src/prod/ic_launcher_dev-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/ic_launcher_dev-playstore.png -------------------------------------------------------------------------------- /app/src/prod/java/org/sopt/santamanitto/room/network/RoomRequestModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.room.network 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | 8 | @InstallIn(SingletonComponent::class) 9 | @Module 10 | class RoomRequestModule { 11 | 12 | @Provides 13 | fun provideCreateRoomRequest(roomService: RoomService): RoomRequest = 14 | RoomRequestImpl(roomService) 15 | } -------------------------------------------------------------------------------- /app/src/prod/java/org/sopt/santamanitto/user/UserRemoteModule.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto.user 2 | 3 | import RetrofitUserAuthController 4 | import RetrofitUserController 5 | import dagger.Module 6 | import dagger.Provides 7 | import dagger.hilt.InstallIn 8 | import dagger.hilt.components.SingletonComponent 9 | import org.sopt.santamanitto.user.data.controller.UserAuthController 10 | import org.sopt.santamanitto.user.data.controller.UserController 11 | import org.sopt.santamanitto.user.network.UserAuthService 12 | import org.sopt.santamanitto.user.network.UserService 13 | import javax.inject.Singleton 14 | 15 | @InstallIn(SingletonComponent::class) 16 | @Module 17 | class UserRemoteModule { 18 | 19 | @Provides 20 | @Singleton 21 | fun provideUserAuthController(userAuthService: UserAuthService): UserAuthController = 22 | RetrofitUserAuthController(userAuthService) 23 | 24 | @Provides 25 | @Singleton 26 | fun provideUserController(userService: UserService): UserController = 27 | RetrofitUserController(userService) 28 | } -------------------------------------------------------------------------------- /app/src/prod/java/org/sopt/santamanitto/user/data/controller/RetrofitUserAuthController.kt: -------------------------------------------------------------------------------- 1 | import org.sopt.santamanitto.network.RequestCallback 2 | import org.sopt.santamanitto.network.start 3 | import org.sopt.santamanitto.user.data.UserInfoModel 4 | import org.sopt.santamanitto.user.data.controller.UserAuthController 5 | import org.sopt.santamanitto.user.mypage.UserNameRequestModel 6 | import org.sopt.santamanitto.user.network.UserAuthService 7 | import timber.log.Timber 8 | 9 | class RetrofitUserAuthController(private val userAuthService: UserAuthService) : 10 | UserAuthController { 11 | override suspend fun changeUserName( 12 | newName: String, 13 | ): Result { 14 | return try { 15 | val response = userAuthService.changeUserName(UserNameRequestModel(newName)) 16 | if (response.statusCode == 200) { 17 | Result.success(true) 18 | } else { 19 | Result.failure(Exception("Failed to change user name: ${response.message}")) 20 | } 21 | } catch (e: Exception) { 22 | Result.failure(e) 23 | } 24 | } 25 | 26 | override fun getUserInfo( 27 | userId: String, 28 | callback: UserAuthController.GetUserInfoCallback, 29 | ) { 30 | userAuthService.getUserInfo(userId).start( 31 | object : RequestCallback { 32 | override fun onSuccess(data: UserInfoModel) { 33 | callback.onUserInfoLoaded(data) 34 | } 35 | 36 | override fun onFail() { 37 | callback.onDataNotAvailable() 38 | } 39 | }, 40 | ) 41 | } 42 | 43 | override suspend fun withdraw(): Result { 44 | return try { 45 | val response = userAuthService.withdraw() 46 | if (response.statusCode == 200) { 47 | Result.success(Unit) 48 | } else { 49 | Result.failure(Exception("Failed to withdraw: ${response.message}")) 50 | } 51 | } catch (e: Exception) { 52 | Timber.e("${e.message}") 53 | Result.failure(e) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/prod/java/org/sopt/santamanitto/user/data/controller/RetrofitUserController.kt: -------------------------------------------------------------------------------- 1 | import org.sopt.santamanitto.auth.data.request.SignInRequestModel 2 | import org.sopt.santamanitto.auth.data.request.SignUpRequestModel 3 | import org.sopt.santamanitto.auth.data.response.SignInResponseModel 4 | import org.sopt.santamanitto.auth.data.response.SignUpResponseModel 5 | import org.sopt.santamanitto.user.data.controller.UserController 6 | import org.sopt.santamanitto.user.network.UserService 7 | 8 | class RetrofitUserController(private val userService: UserService) : UserController { 9 | override suspend fun login(serialNumber: String): Result { 10 | return try { 11 | val response = userService.login(SignInRequestModel(serialNumber)) 12 | if (response.statusCode == 200) { 13 | val result = response.data 14 | Result.success(result) 15 | } else { 16 | Result.failure(Exception("${response.statusCode}")) 17 | } 18 | } catch (e: Exception) { 19 | Result.failure(e) 20 | } 21 | } 22 | 23 | override suspend fun createAccount( 24 | userName: String, 25 | serialNumber: String, 26 | ): Result { 27 | return try { 28 | val response = userService.createAccount(SignUpRequestModel(serialNumber, userName)) 29 | if (response.statusCode == 201) { 30 | val result = response.data 31 | Result.success(result) 32 | } else { 33 | Result.failure(Exception("${response.statusCode}")) 34 | } 35 | } catch (e: Exception) { 36 | Result.failure(e) 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-anydpi-v26/ic_launcher_dev.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-anydpi-v26/ic_launcher_dev_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-hdpi/ic_launcher_dev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-hdpi/ic_launcher_dev.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-hdpi/ic_launcher_dev_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-hdpi/ic_launcher_dev_foreground.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-hdpi/ic_launcher_dev_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-hdpi/ic_launcher_dev_round.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-mdpi/ic_launcher_dev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-mdpi/ic_launcher_dev.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-mdpi/ic_launcher_dev_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-mdpi/ic_launcher_dev_foreground.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-mdpi/ic_launcher_dev_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-mdpi/ic_launcher_dev_round.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-xhdpi/ic_launcher_dev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-xhdpi/ic_launcher_dev.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-xhdpi/ic_launcher_dev_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-xhdpi/ic_launcher_dev_foreground.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-xhdpi/ic_launcher_dev_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-xhdpi/ic_launcher_dev_round.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-xxhdpi/ic_launcher_dev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-xxhdpi/ic_launcher_dev.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-xxhdpi/ic_launcher_dev_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-xxhdpi/ic_launcher_dev_foreground.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-xxhdpi/ic_launcher_dev_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-xxhdpi/ic_launcher_dev_round.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-xxxhdpi/ic_launcher_dev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-xxxhdpi/ic_launcher_dev.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-xxxhdpi/ic_launcher_dev_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-xxxhdpi/ic_launcher_dev_foreground.webp -------------------------------------------------------------------------------- /app/src/prod/res/mipmap-xxxhdpi/ic_launcher_dev_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/app/src/prod/res/mipmap-xxxhdpi/ic_launcher_dev_round.webp -------------------------------------------------------------------------------- /app/src/prod/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | rudolf manitto 3 | -------------------------------------------------------------------------------- /app/src/sharedTest/java/org/sopt/santamanitto/util/MockitoKotlinHelpers.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.sopt.santamanitto.util 17 | 18 | //import org.mockito.ArgumentCaptor 19 | //import org.mockito.Mockito 20 | // 21 | //fun eq(obj: T): T = Mockito.eq(obj) 22 | // 23 | //fun any(): T = Mockito.any() 24 | // 25 | //fun capture(argumentCaptor: ArgumentCaptor): T = argumentCaptor.capture() 26 | // 27 | //inline fun argumentCaptor(): ArgumentCaptor = 28 | // ArgumentCaptor.forClass(T::class.java) 29 | // 30 | //inline fun mock(): T = Mockito.mock(T::class.java)!! -------------------------------------------------------------------------------- /app/src/test/java/org/sopt/santamanitto/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package org.sopt.santamanitto 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.android.application) apply false 3 | alias(libs.plugins.kotlin.android) apply false 4 | alias(libs.plugins.kotlin.kapt) apply false 5 | alias(libs.plugins.hilt) apply false 6 | alias(libs.plugins.androidx.navigation.safeargs) apply false 7 | alias(libs.plugins.google.services) apply false 8 | alias(libs.plugins.firebase.crashlytics) apply false 9 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | android.nonTransitiveRClass=false 23 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manito-project/manitto-android/8f3d6dc0860fdb52020c49eeb76e886c86538a2e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google { 4 | content { 5 | includeGroupByRegex("com\\.android.*") 6 | includeGroupByRegex("com\\.google.*") 7 | includeGroupByRegex("androidx.*") 8 | } 9 | } 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | dependencyResolutionManagement { 15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.name = "santa manito" 23 | include(":app") --------------------------------------------------------------------------------