├── .github └── ISSUE_TEMPLATE │ └── feature_request.md ├── .gitignore ├── Android ├── FunBox │ ├── .gitignore │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle.kts │ │ ├── google-services.json │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── rpg │ │ │ │ └── funbox │ │ │ │ └── ExampleInstrumentedTest.kt │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── ic_funbox-playstore.png │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── rpg │ │ │ │ │ └── funbox │ │ │ │ │ ├── app │ │ │ │ │ ├── MainApplication.kt │ │ │ │ │ └── MySharedPreferences.kt │ │ │ │ │ ├── data │ │ │ │ │ ├── JWTInterceptor.kt │ │ │ │ │ ├── JwtDecoder.kt │ │ │ │ │ ├── OkHttpClientInstance.kt │ │ │ │ │ ├── RetrofitInstance.kt │ │ │ │ │ ├── dto │ │ │ │ │ │ ├── NaverAccessTokenRequest.kt │ │ │ │ │ │ ├── NaverAccessTokenResponse.kt │ │ │ │ │ │ ├── NaverLoginRequest.kt │ │ │ │ │ │ ├── User.kt │ │ │ │ │ │ ├── UserAuthDto.kt │ │ │ │ │ │ ├── UserDetail.kt │ │ │ │ │ │ ├── UserInfoRequest.kt │ │ │ │ │ │ ├── UserInfoResponse.kt │ │ │ │ │ │ ├── UserLocation.kt │ │ │ │ │ │ ├── UserLocationResponse.kt │ │ │ │ │ │ └── UsersLocationRequest.kt │ │ │ │ │ ├── network │ │ │ │ │ │ └── service │ │ │ │ │ │ │ ├── NaverLoginApi.kt │ │ │ │ │ │ │ ├── SignUpApi.kt │ │ │ │ │ │ │ ├── UserInfoApi.kt │ │ │ │ │ │ │ └── UsersLocationApi.kt │ │ │ │ │ └── repository │ │ │ │ │ │ ├── NaverLoginRepository.kt │ │ │ │ │ │ ├── NaverLoginRepositoryImpl.kt │ │ │ │ │ │ ├── UserRepository.kt │ │ │ │ │ │ ├── UserRepositoryImpl.kt │ │ │ │ │ │ ├── UsersLocationRepository.kt │ │ │ │ │ │ └── UsersLocationRepositoryImpl.kt │ │ │ │ │ └── presentation │ │ │ │ │ ├── ActivityExtension.kt │ │ │ │ │ ├── BaseDialogFragment.kt │ │ │ │ │ ├── BaseFragment.kt │ │ │ │ │ ├── CustomNaverMap.kt │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ ├── MapSocket.kt │ │ │ │ │ ├── game │ │ │ │ │ ├── GameActivity.kt │ │ │ │ │ ├── gameselect │ │ │ │ │ │ ├── GameListAdapter.kt │ │ │ │ │ │ ├── GameSelectBindingAdapters.kt │ │ │ │ │ │ ├── GameSelectFragment.kt │ │ │ │ │ │ ├── GameSelectUiEvent.kt │ │ │ │ │ │ ├── GameSelectUiState.kt │ │ │ │ │ │ ├── GameSelectViewModel.kt │ │ │ │ │ │ ├── OnGameClickListener.kt │ │ │ │ │ │ └── QuestionGameViewModel.kt │ │ │ │ │ ├── quiz │ │ │ │ │ │ ├── AnswerCheckFragment.kt │ │ │ │ │ │ ├── ChatAdapter.kt │ │ │ │ │ │ ├── LoadingDialog.kt │ │ │ │ │ │ ├── MessageItem.kt │ │ │ │ │ │ ├── NetworkAlertFragment.kt │ │ │ │ │ │ ├── QuizBindingAdapters.kt │ │ │ │ │ │ ├── QuizFragment.kt │ │ │ │ │ │ ├── QuizUiEvent.kt │ │ │ │ │ │ ├── QuizUiState.kt │ │ │ │ │ │ ├── QuizViewModel.kt │ │ │ │ │ │ └── ScoreBoardFragment.kt │ │ │ │ │ └── wait │ │ │ │ │ │ └── WaitFragment.kt │ │ │ │ │ ├── login │ │ │ │ │ ├── AccessPermission.kt │ │ │ │ │ ├── TitleActivity.kt │ │ │ │ │ ├── nickname │ │ │ │ │ │ ├── NicknameFragment.kt │ │ │ │ │ │ ├── NicknameUiEvent.kt │ │ │ │ │ │ └── NicknameUiState.kt │ │ │ │ │ ├── profile │ │ │ │ │ │ ├── ProfileBindingAdapters.kt │ │ │ │ │ │ ├── ProfileFragment.kt │ │ │ │ │ │ ├── ProfileUiEvent.kt │ │ │ │ │ │ └── ProfileUiState.kt │ │ │ │ │ ├── splash │ │ │ │ │ │ ├── SplashActivity.kt │ │ │ │ │ │ ├── SplashUiEvent.kt │ │ │ │ │ │ └── SplashViewModel.kt │ │ │ │ │ └── title │ │ │ │ │ │ ├── TitleBindingAdapters.kt │ │ │ │ │ │ ├── TitleFragment.kt │ │ │ │ │ │ ├── TitleUiEvent.kt │ │ │ │ │ │ ├── TitleUiState.kt │ │ │ │ │ │ └── TitleViewModel.kt │ │ │ │ │ ├── map │ │ │ │ │ ├── GameData.kt │ │ │ │ │ ├── GetGameDialog.kt │ │ │ │ │ ├── MapBindingAdapters.kt │ │ │ │ │ ├── MapFragment.kt │ │ │ │ │ ├── MapProfile.kt │ │ │ │ │ ├── MapProfileAdapter.kt │ │ │ │ │ ├── MapUiEvent.kt │ │ │ │ │ ├── MapViewModel.kt │ │ │ │ │ ├── MessageDialog.kt │ │ │ │ │ ├── OtherUserState.kt │ │ │ │ │ └── SocketApplication.kt │ │ │ │ │ └── setting │ │ │ │ │ ├── SetNameDialog.kt │ │ │ │ │ ├── SetProfileDialog.kt │ │ │ │ │ ├── SettingBindingAdapter.kt │ │ │ │ │ ├── SettingFragment.kt │ │ │ │ │ ├── SettingUiEvent.kt │ │ │ │ │ ├── SettingUiState.kt │ │ │ │ │ ├── SettingViewModel.kt │ │ │ │ │ └── WithdrawalDialog.kt │ │ │ └── res │ │ │ │ ├── anim │ │ │ │ ├── fade_in.xml │ │ │ │ ├── fade_out.xml │ │ │ │ ├── slide_left_enter.xml │ │ │ │ └── slide_left_exit.xml │ │ │ │ ├── drawable │ │ │ │ ├── add_24.xml │ │ │ │ ├── answer_edittext.xml │ │ │ │ ├── autorenew_24.xml │ │ │ │ ├── baseline_arrow_back_24.xml │ │ │ │ ├── bg_dialog_rectangle.xml │ │ │ │ ├── bg_green_rectangle.xml │ │ │ │ ├── bg_input_text.xml │ │ │ │ ├── bg_red_rectangle.xml │ │ │ │ ├── close_24.xml │ │ │ │ ├── fail_button.xml │ │ │ │ ├── funbox_logo.png │ │ │ │ ├── group_1940.xml │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ ├── ic_launcher_foreground.xml │ │ │ │ ├── map_24.xml │ │ │ │ ├── menu_24.xml │ │ │ │ ├── my_location.png │ │ │ │ ├── naver_login.png │ │ │ │ ├── navi_icon.png │ │ │ │ ├── navigate_next_24.xml │ │ │ │ ├── nickname_edittext.xml │ │ │ │ ├── other_location.png │ │ │ │ ├── profile_add.png │ │ │ │ ├── profile_game.xml │ │ │ │ ├── profile_none.png │ │ │ │ ├── round_radius.xml │ │ │ │ ├── rounded_corner_rect_shadow.xml │ │ │ │ ├── settings_24.xml │ │ │ │ ├── splash_background.xml │ │ │ │ ├── success_button.xml │ │ │ │ ├── wait_background.xml │ │ │ │ └── write_24.xml │ │ │ │ ├── layout │ │ │ │ ├── activity_game.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── activity_title.xml │ │ │ │ ├── dialog_get_game.xml │ │ │ │ ├── dialog_loading.xml │ │ │ │ ├── dialog_message.xml │ │ │ │ ├── dialog_positive.xml │ │ │ │ ├── dialog_profile_change.xml │ │ │ │ ├── dialog_red.xml │ │ │ │ ├── dialog_red_with_text.xml │ │ │ │ ├── fragment_answer_check.xml │ │ │ │ ├── fragment_game_select.xml │ │ │ │ ├── fragment_map.xml │ │ │ │ ├── fragment_network_alert.xml │ │ │ │ ├── fragment_nickname.xml │ │ │ │ ├── fragment_profile.xml │ │ │ │ ├── fragment_quiz.xml │ │ │ │ ├── fragment_score_board.xml │ │ │ │ ├── fragment_setting.xml │ │ │ │ ├── fragment_title.xml │ │ │ │ ├── fragment_wait.xml │ │ │ │ ├── item_chat.xml │ │ │ │ ├── item_chat_other.xml │ │ │ │ ├── item_game_list.xml │ │ │ │ ├── map_profile.xml │ │ │ │ ├── menu_header.xml │ │ │ │ ├── quiz_question_count_spinner.xml │ │ │ │ └── to_be_determined_list.xml │ │ │ │ ├── menu │ │ │ │ └── side_menu.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_funbox.xml │ │ │ │ └── ic_funbox_round.xml │ │ │ │ ├── mipmap-anydpi │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_funbox.webp │ │ │ │ ├── ic_funbox_foreground.webp │ │ │ │ ├── ic_funbox_round.webp │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_funbox.webp │ │ │ │ ├── ic_funbox_foreground.webp │ │ │ │ ├── ic_funbox_round.webp │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_funbox.webp │ │ │ │ ├── ic_funbox_foreground.webp │ │ │ │ ├── ic_funbox_round.webp │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_funbox.webp │ │ │ │ ├── ic_funbox_foreground.webp │ │ │ │ ├── ic_funbox_round.webp │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_funbox.webp │ │ │ │ ├── ic_funbox_foreground.webp │ │ │ │ ├── ic_funbox_round.webp │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── navigation │ │ │ │ ├── game_nav_graph.xml │ │ │ │ ├── main_navi_graph.xml │ │ │ │ └── title_nav_graph.xml │ │ │ │ ├── values-night │ │ │ │ └── themes.xml │ │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── ic_funbox_background.xml │ │ │ │ ├── strings.xml │ │ │ │ ├── styles.xml │ │ │ │ └── themes.xml │ │ │ │ └── xml │ │ │ │ ├── backup_rules.xml │ │ │ │ └── data_extraction_rules.xml │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── rpg │ │ │ └── funbox │ │ │ └── ExampleUnitTest.kt │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle.kts └── README.md ├── BackEnd ├── README.md └── funbox │ ├── .eslintrc.js │ ├── .gitignore │ ├── .husky │ └── pre-commit │ ├── .prettierrc │ ├── README.md │ ├── nest-cli.json │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app.module.ts │ ├── auth │ │ ├── auth.controller.ts │ │ ├── auth.module.ts │ │ ├── auth.service.ts │ │ ├── dto │ │ │ ├── auth-request.dto.ts │ │ │ ├── auth-response.dto.ts │ │ │ └── user-auth.dto.ts │ │ └── jwt.strategy.ts │ ├── configs │ │ ├── swagger.config.ts │ │ └── typeorm.config.ts │ ├── main.ts │ ├── socket │ │ ├── dto │ │ │ └── game-room.dto.ts │ │ ├── enum │ │ │ └── game-apply-answer.enum.ts │ │ ├── filters │ │ │ └── ws-exception.filter.ts │ │ ├── game.service.ts │ │ ├── pipes │ │ │ └── answer-validation.pipe.ts │ │ ├── socket.gateway.ts │ │ ├── socket.module.ts │ │ └── socket.service.ts │ └── users │ │ ├── dto │ │ ├── near-users.dto.ts │ │ ├── user-location.dto.ts │ │ ├── user-request.dto.ts │ │ └── user-response.dto.ts │ │ ├── user.entity.ts │ │ ├── users.controller.ts │ │ ├── users.module.ts │ │ └── users.service.ts │ ├── test │ ├── test.png │ ├── test_apis.js │ ├── test_auth.js │ ├── test_date.js │ └── test_profile.js │ ├── tsconfig.build.json │ └── tsconfig.json ├── README.md └── docs └── pull_request_template.md /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Issue 타입(하나 이상의 Issue 타입을 선택해주세요) 11 | - [ ] 기능 추가 12 | - [ ] 기능 삭제 13 | - [ ] 버그 수정 14 | - [ ] 의존성, 환경 변수, 빌드 관련 코드 업데이트 15 | 16 | ### 상세 내용 17 | ex) Github 소셜 로그인 기능이 필요합니다. 18 | 19 | ### 예상 소요 시간 20 | - [ ] `0.5h` 21 | - [ ] `1h` 22 | - [ ] `1.5h` 23 | - [ ] `2h` 24 | - [ ] `2.5h` 25 | - [ ] `3h` 26 | 27 | ### 라벨 28 | - 예상 소요 시간: `E: 1h` 29 | - 그룹: `client`, `server` 30 | - 긴급도: `High`, `Middle`, `Low` 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Android/FunBox/.idea/ 2 | -------------------------------------------------------------------------------- /Android/FunBox/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /key.jks 4 | /key.properties 5 | /local.properties 6 | /.idea/caches 7 | /.idea/libraries 8 | /.idea/modules.xml 9 | /.idea/workspace.xml 10 | /.idea/navEditor.xml 11 | /.idea/assetWizardSettings.xml 12 | /.idea/misc.xml 13 | .DS_Store 14 | /build 15 | /release 16 | /captures 17 | .externalNativeBuild 18 | .cxx 19 | local.properties 20 | -------------------------------------------------------------------------------- /Android/FunBox/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /release -------------------------------------------------------------------------------- /Android/FunBox/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "897486577056", 4 | "project_id": "funbox-f4840", 5 | "storage_bucket": "funbox-f4840.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:897486577056:android:d693940bacaab95d2a98c5", 11 | "android_client_info": { 12 | "package_name": "com.rpg.funbox" 13 | } 14 | }, 15 | "oauth_client": [], 16 | "api_key": [ 17 | { 18 | "current_key": "AIzaSyAelvQzX6nCyGANdZlNXxuVimknV7FAmRY" 19 | } 20 | ], 21 | "services": { 22 | "appinvite_service": { 23 | "other_platform_oauth_client": [] 24 | } 25 | } 26 | } 27 | ], 28 | "configuration_version": "1" 29 | } -------------------------------------------------------------------------------- /Android/FunBox/app/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Android/FunBox/app/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /Android/FunBox/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | -keep class com.google.android.gms.location.** { *; } -------------------------------------------------------------------------------- /Android/FunBox/app/src/androidTest/java/com/rpg/funbox/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.funbox", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 32 | 33 | 37 | 38 | 41 | 42 | 45 | 46 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/ic_funbox-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/ic_funbox-playstore.png -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/app/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.app 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import timber.log.Timber 6 | 7 | class MainApplication : Application() { 8 | 9 | init { 10 | instance = this 11 | } 12 | 13 | override fun onCreate() { 14 | mySharedPreferences = MySharedPreferences() 15 | super.onCreate() 16 | 17 | Timber.plant(Timber.DebugTree()) 18 | } 19 | 20 | companion object { 21 | lateinit var mySharedPreferences: MySharedPreferences 22 | var instance: MainApplication? = null 23 | 24 | fun myContext(): Context { 25 | return instance!!.applicationContext 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/app/MySharedPreferences.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.app 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | 6 | class MySharedPreferences { 7 | 8 | private val preferences: SharedPreferences = 9 | MainApplication.myContext().getSharedPreferences("prefs_name", Context.MODE_PRIVATE) 10 | 11 | fun getJWT(key: String, defValue: String): String { 12 | return preferences.getString(key, defValue).toString() 13 | } 14 | 15 | fun setJWT(key: String, defValue: String) { 16 | preferences.edit().putString(key, defValue).apply() 17 | } 18 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/JWTInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data 2 | 3 | import com.rpg.funbox.app.MainApplication 4 | import okhttp3.Interceptor 5 | import okhttp3.Response 6 | import timber.log.Timber 7 | import java.io.IOException 8 | 9 | object JWTInterceptor : Interceptor { 10 | 11 | @Throws(IOException::class) 12 | override fun intercept(chain: Interceptor.Chain): Response = with(chain) { 13 | val accessToken: String = MainApplication.mySharedPreferences.getJWT("jwt", "") 14 | Timber.d("JWTInterceptor: $accessToken") 15 | val newRequest = request().newBuilder() 16 | .addHeader("Authorization", "Bearer $accessToken") 17 | .build() 18 | proceed(newRequest) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/JwtDecoder.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data 2 | 3 | import android.util.Base64 4 | import com.google.gson.JsonParser 5 | import com.rpg.funbox.data.dto.UserAuthDto 6 | import java.nio.charset.StandardCharsets 7 | 8 | object JwtDecoder { 9 | 10 | //private val gson = Gson() 11 | //private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() 12 | 13 | fun getUser(accessToken: String): UserAuthDto { 14 | val payloadJwt = accessToken.split(".")[1] 15 | val decodedPayload = 16 | String(Base64.decode(payloadJwt, Base64.DEFAULT), StandardCharsets.UTF_8) 17 | val jsonObject = JsonParser.parseString(decodedPayload).asJsonObject 18 | 19 | return UserAuthDto( 20 | jsonObject.get("id").asInt, 21 | jsonObject.get("iat").asInt, 22 | jsonObject.get("exp").asInt 23 | ) 24 | 25 | /* 26 | val json = gson.toJson(jsonObject, JwtDecoder::class.java) 27 | 28 | val moshiAdapter: JsonAdapter = moshi.adapter(UserAuthDto::class.java) 29 | return moshiAdapter.fromJsonValue(decodedPayload) 30 | */ 31 | } 32 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/OkHttpClientInstance.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data 2 | 3 | import okhttp3.OkHttpClient 4 | import java.util.concurrent.TimeUnit 5 | 6 | object OkHttpClientInstance { 7 | 8 | val okHttpClient: OkHttpClient by lazy { 9 | OkHttpClient.Builder().run { 10 | this.addInterceptor(JWTInterceptor) 11 | this.build() 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/RetrofitInstance.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data 2 | 3 | import com.squareup.moshi.Moshi 4 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory 5 | import retrofit2.Retrofit 6 | import retrofit2.converter.moshi.MoshiConverterFactory 7 | 8 | object RetrofitInstance { 9 | 10 | private const val FUNBOX_BASE_URL = "http://175.45.193.191:3000" 11 | private val moshi = Moshi.Builder() 12 | .add(KotlinJsonAdapterFactory()) 13 | .build() 14 | 15 | val retrofit: Retrofit by lazy { 16 | Retrofit.Builder() 17 | .baseUrl(FUNBOX_BASE_URL) 18 | .client(OkHttpClientInstance.okHttpClient) 19 | .addConverterFactory(MoshiConverterFactory.create(moshi)) 20 | .build() 21 | } 22 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/NaverAccessTokenRequest.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | data class NaverAccessTokenRequest( 4 | val naverAccessToken: String 5 | ) -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/NaverAccessTokenResponse.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | data class NaverAccessTokenResponse( 4 | val accessToken: String 5 | ) 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/NaverLoginRequest.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | data class NaverLoginRequest( 4 | val userId: String 5 | ) -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/User.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | import com.naver.maps.geometry.LatLng 4 | import com.naver.maps.map.overlay.InfoWindow 5 | import com.naver.maps.map.overlay.Marker 6 | 7 | data class User( 8 | val status: Int, 9 | val id: Int, 10 | var loc: LatLng, 11 | var name: String?, 12 | var isMsg: Boolean, 13 | var mapPin: Marker? = null 14 | ) 15 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/UserAuthDto.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | import com.squareup.moshi.Json 4 | 5 | data class UserAuthDto( 6 | @Json(name = "id") 7 | val id: Int, 8 | 9 | @Json(name = "iat") 10 | val iat: Int, 11 | 12 | @Json(name = "exp") 13 | val exp: Int 14 | ) 15 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/UserDetail.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | data class UserDetail ( 4 | val id: Int, 5 | var msg: String, 6 | val profile: String, 7 | var name: String 8 | ) -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/UserInfoRequest.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | import com.squareup.moshi.Json 4 | 5 | data class UserInfoRequest( 6 | @Json(name = "username") 7 | val userName: String?, 8 | 9 | @Json(name = "profileUrl") 10 | val profileUrl: String?, 11 | 12 | @Json(name = "message") 13 | val message: String? 14 | ) -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/UserInfoResponse.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | import com.squareup.moshi.Json 4 | 5 | data class UserInfoResponse( 6 | @Json(name = "username") 7 | val userName: String?, 8 | 9 | @Json(name = "profileUrl") 10 | val profileUrl: String?, 11 | 12 | @Json(name = "message") 13 | val message: String? 14 | ) 15 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/UserLocation.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | data class UserLocation( 4 | val id: Int, 5 | val username: String?, 6 | val locX: Double?, 7 | val locY: Double?, 8 | val isMsgInAnHour: Boolean 9 | ) -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/UserLocationResponse.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | data class UserLocationResponse( 4 | val resultMessage: String, 5 | val userLocations: List? 6 | ) -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/dto/UsersLocationRequest.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.dto 2 | 3 | data class UsersLocationRequest( 4 | val locX: Double, 5 | val locY: Double 6 | ) 7 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/network/service/NaverLoginApi.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.network.service 2 | 3 | import com.rpg.funbox.data.dto.NaverAccessTokenRequest 4 | import com.rpg.funbox.data.dto.NaverAccessTokenResponse 5 | import retrofit2.Response 6 | import retrofit2.http.Body 7 | import retrofit2.http.POST 8 | 9 | interface NaverLoginApi { 10 | 11 | @POST("/auth/navertoken") 12 | suspend fun submitNaverAccessToken( 13 | @Body body: NaverAccessTokenRequest 14 | ): Response 15 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/network/service/SignUpApi.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.network.service 2 | 3 | import com.rpg.funbox.data.dto.UserInfoRequest 4 | import com.rpg.funbox.data.dto.UserInfoResponse 5 | import okhttp3.MultipartBody 6 | import retrofit2.Response 7 | import retrofit2.http.Body 8 | import retrofit2.http.DELETE 9 | import retrofit2.http.GET 10 | import retrofit2.http.Multipart 11 | import retrofit2.http.PATCH 12 | import retrofit2.http.POST 13 | import retrofit2.http.Part 14 | 15 | interface SignUpApi { 16 | 17 | @GET("/users") 18 | suspend fun fetchUserInfo(): Response 19 | 20 | @PATCH("/users/username") 21 | suspend fun submitUserName( 22 | @Body body: UserInfoRequest 23 | ): Response 24 | 25 | @Multipart 26 | @POST("/users/profile") 27 | suspend fun submitUserProfile( 28 | @Part file: MultipartBody.Part 29 | ): Response 30 | 31 | @DELETE("/users") 32 | suspend fun deleteUserInfo(): Response 33 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/network/service/UserInfoApi.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.network.service 2 | 3 | import com.rpg.funbox.data.dto.UserInfoRequest 4 | import com.rpg.funbox.data.dto.UserInfoResponse 5 | import retrofit2.Response 6 | import retrofit2.http.Body 7 | import retrofit2.http.GET 8 | import retrofit2.http.PATCH 9 | import retrofit2.http.Path 10 | 11 | interface UserInfoApi { 12 | 13 | @GET("/users/{id}") 14 | suspend fun fetchSpecificUser( 15 | @Path(value = "id") userId: Int 16 | ): Response 17 | 18 | @PATCH("/users/message") 19 | suspend fun submitUserMessage( 20 | @Body body: UserInfoRequest 21 | ): Response 22 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/network/service/UsersLocationApi.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.network.service 2 | 3 | import com.rpg.funbox.data.dto.UserLocation 4 | import com.rpg.funbox.data.dto.UsersLocationRequest 5 | import retrofit2.Response 6 | import retrofit2.http.Body 7 | import retrofit2.http.POST 8 | 9 | interface UsersLocationApi { 10 | @POST("/users/location") 11 | suspend fun fetchUsersLocation( 12 | @Body body: UsersLocationRequest 13 | ) : Response> 14 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/repository/NaverLoginRepository.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.repository 2 | 3 | import com.rpg.funbox.data.dto.UserAuthDto 4 | 5 | interface NaverLoginRepository { 6 | 7 | suspend fun postNaverAccessToken(token: String): UserAuthDto? 8 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/repository/NaverLoginRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.repository 2 | 3 | import com.rpg.funbox.app.MainApplication 4 | import com.rpg.funbox.data.JwtDecoder 5 | import com.rpg.funbox.data.RetrofitInstance 6 | import com.rpg.funbox.data.dto.NaverAccessTokenRequest 7 | import com.rpg.funbox.data.dto.UserAuthDto 8 | import com.rpg.funbox.data.network.service.NaverLoginApi 9 | import timber.log.Timber 10 | 11 | class NaverLoginRepositoryImpl : NaverLoginRepository { 12 | 13 | private val naverLoginApi: NaverLoginApi by lazy { 14 | RetrofitInstance.retrofit.create(NaverLoginApi::class.java) 15 | } 16 | 17 | override suspend fun postNaverAccessToken(token: String): UserAuthDto? { 18 | val response = naverLoginApi.submitNaverAccessToken(NaverAccessTokenRequest(token)) 19 | Timber.d("${response.code()}") 20 | val body = response.body() 21 | Timber.d("$body") 22 | when (response.code()) { 23 | in successStatusCodeRange -> { 24 | return body?.let { 25 | Timber.d("JWT: ${it.accessToken}") 26 | MainApplication.mySharedPreferences.setJWT("jwt", it.accessToken) 27 | JwtDecoder.getUser(it.accessToken) 28 | } 29 | } 30 | 31 | UNAUTHORIZED_STATUS -> { 32 | if (body != null) { 33 | Timber.d("JWT: ${body.accessToken}") 34 | } 35 | return UserAuthDto(0, 0, 0) 36 | } 37 | 38 | in serverErrorStatusCodeRange -> {} 39 | 40 | else -> {} 41 | } 42 | return null 43 | } 44 | 45 | companion object { 46 | private const val UNAUTHORIZED_STATUS: Int = 401 47 | 48 | private val successStatusCodeRange: IntRange = 200..299 49 | private val serverErrorStatusCodeRange: IntRange = 500..599 50 | } 51 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/repository/UserRepository.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.repository 2 | 3 | import com.rpg.funbox.data.dto.UserInfoResponse 4 | import okhttp3.MultipartBody 5 | 6 | interface UserRepository { 7 | 8 | suspend fun getUserInfo(): UserInfoResponse? 9 | 10 | suspend fun getSpecificUserInfo(userId: Int): UserInfoResponse? 11 | 12 | suspend fun patchUserMessage(message: String): Boolean 13 | 14 | suspend fun patchUserName(userName: String): Boolean 15 | 16 | suspend fun postUserProfile(imageFile: MultipartBody.Part): Boolean 17 | 18 | suspend fun withdraw(): Boolean 19 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/repository/UserRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.repository 2 | 3 | import com.rpg.funbox.data.RetrofitInstance 4 | import com.rpg.funbox.data.dto.UserInfoRequest 5 | import com.rpg.funbox.data.dto.UserInfoResponse 6 | import com.rpg.funbox.data.network.service.SignUpApi 7 | import com.rpg.funbox.data.network.service.UserInfoApi 8 | import okhttp3.MultipartBody 9 | import timber.log.Timber 10 | 11 | class UserRepositoryImpl : UserRepository { 12 | 13 | private val signUpApi: SignUpApi by lazy { 14 | RetrofitInstance.retrofit.create(SignUpApi::class.java) 15 | } 16 | 17 | private val userInfoApi: UserInfoApi by lazy { 18 | RetrofitInstance.retrofit.create(UserInfoApi::class.java) 19 | } 20 | 21 | override suspend fun getUserInfo(): UserInfoResponse? { 22 | val response = signUpApi.fetchUserInfo() 23 | when (response.code()) { 24 | in successStatusCodeRange -> { 25 | return response.body() 26 | } 27 | 28 | else -> {} 29 | } 30 | 31 | return null 32 | } 33 | 34 | override suspend fun getSpecificUserInfo(userId: Int): UserInfoResponse? { 35 | val response = userInfoApi.fetchSpecificUser(userId = userId) 36 | when (response.code()) { 37 | in successStatusCodeRange -> { 38 | return response.body() 39 | } 40 | 41 | else -> {} 42 | } 43 | 44 | return null 45 | } 46 | 47 | override suspend fun patchUserMessage(message: String): Boolean { 48 | Timber.d("Message: $message") 49 | val response = userInfoApi.submitUserMessage(UserInfoRequest(null, null, message = message)) 50 | Timber.d("Response: ${response.body()}") 51 | when (response.code()) { 52 | in successStatusCodeRange -> { 53 | return true 54 | } 55 | 56 | else -> {} 57 | } 58 | 59 | return false 60 | } 61 | 62 | override suspend fun patchUserName(userName: String): Boolean { 63 | val response = signUpApi.submitUserName(UserInfoRequest(userName = userName, null, null)) 64 | when (response.code()) { 65 | in successStatusCodeRange -> { 66 | return true 67 | } 68 | 69 | else -> {} 70 | } 71 | 72 | return false 73 | } 74 | 75 | override suspend fun postUserProfile(imageFile: MultipartBody.Part): Boolean { 76 | val response = signUpApi.submitUserProfile(file = imageFile) 77 | when (response.code()) { 78 | in successStatusCodeRange -> { 79 | return true 80 | } 81 | 82 | else -> {} 83 | } 84 | return false 85 | } 86 | 87 | override suspend fun withdraw(): Boolean { 88 | val response = signUpApi.deleteUserInfo() 89 | when (response.code()) { 90 | in successStatusCodeRange -> { 91 | return true 92 | } 93 | 94 | else -> {} 95 | } 96 | 97 | return false 98 | } 99 | 100 | companion object { 101 | private const val UNAUTHORIZED_STATUS: Int = 401 102 | 103 | private val successStatusCodeRange: IntRange = 200..299 104 | private val serverErrorStatusCodeRange: IntRange = 500..599 105 | } 106 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/repository/UsersLocationRepository.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.repository 2 | 3 | import com.rpg.funbox.data.dto.UserLocationResponse 4 | 5 | interface UsersLocationRepository { 6 | 7 | suspend fun getUsersLocation(locX: Double, locY: Double): UserLocationResponse 8 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/data/repository/UsersLocationRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.data.repository 2 | 3 | import com.rpg.funbox.data.RetrofitInstance 4 | import com.rpg.funbox.data.dto.UserLocationResponse 5 | import com.rpg.funbox.data.dto.UsersLocationRequest 6 | import com.rpg.funbox.data.network.service.UsersLocationApi 7 | import timber.log.Timber 8 | 9 | class UsersLocationRepositoryImpl : UsersLocationRepository { 10 | 11 | private val usersLocationApi: UsersLocationApi by lazy { 12 | RetrofitInstance.retrofit.create(UsersLocationApi::class.java) 13 | } 14 | 15 | override suspend fun getUsersLocation(locX: Double, locY: Double): UserLocationResponse { 16 | val response = usersLocationApi.fetchUsersLocation(UsersLocationRequest(locX = locX, locY = locY)) 17 | Timber.d("Response Code: ${response.code()}") 18 | when (response.code()) { 19 | in successStatusCodeRange -> { 20 | Timber.d("Success") 21 | return UserLocationResponse("OK", response.body()) 22 | } 23 | 24 | UNAUTHORIZED_STATUS -> { 25 | Timber.d("Unauthorized") 26 | return UserLocationResponse("Unauthorized", null) 27 | } 28 | 29 | in serverErrorStatusCodeRange -> { 30 | Timber.d("Server Error") 31 | return UserLocationResponse("Server Error", null) 32 | } 33 | 34 | else -> {} 35 | } 36 | 37 | Timber.d("Else") 38 | return UserLocationResponse("Timeout", null) 39 | } 40 | 41 | companion object { 42 | private const val UNAUTHORIZED_STATUS: Int = 401 43 | 44 | private val successStatusCodeRange: IntRange = 200..299 45 | private val serverErrorStatusCodeRange: IntRange = 500..599 46 | } 47 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/ActivityExtension.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation 2 | 3 | import android.app.Activity 4 | import android.app.ActivityOptions 5 | import android.content.pm.PackageManager 6 | import android.os.Bundle 7 | import androidx.navigation.NavController 8 | import androidx.navigation.NavDirections 9 | import com.rpg.funbox.R 10 | import timber.log.Timber 11 | 12 | fun Activity.checkPermission(permissions: Array): Boolean { 13 | return permissions.all { permission -> 14 | checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED 15 | } 16 | } 17 | 18 | fun Activity.slideLeft(): Bundle { 19 | return ActivityOptions.makeCustomAnimation(this, R.anim.slide_left_enter, R.anim.slide_left_exit).toBundle() 20 | } 21 | 22 | fun Activity.fadeInOut(): Bundle { 23 | return ActivityOptions.makeCustomAnimation(this, R.anim.fade_in, R.anim.fade_out).toBundle() 24 | } 25 | 26 | fun NavController.safeNavigate(action: NavDirections) { 27 | Timber.d("Current Destination: ${currentDestination}") 28 | currentDestination?.getAction(action.actionId)?.run { 29 | navigate(action) 30 | } 31 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/BaseDialogFragment.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation 2 | 3 | import android.graphics.Color 4 | import android.graphics.drawable.ColorDrawable 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.view.Window 10 | import androidx.databinding.DataBindingUtil 11 | import androidx.databinding.ViewDataBinding 12 | import androidx.fragment.app.DialogFragment 13 | 14 | abstract class BaseDialogFragment(private val layoutId: Int) : DialogFragment() { 15 | 16 | private var _binding: T? = null 17 | protected val binding 18 | get() = requireNotNull(_binding) 19 | 20 | override fun onCreateView( 21 | inflater: LayoutInflater, 22 | container: ViewGroup?, 23 | savedInstanceState: Bundle? 24 | ): View? { 25 | dialog?.window?.run { 26 | setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) 27 | requestFeature(Window.FEATURE_NO_TITLE) 28 | } 29 | 30 | _binding = DataBindingUtil.inflate(inflater, layoutId, container, false) 31 | binding.lifecycleOwner = viewLifecycleOwner 32 | return binding.root 33 | } 34 | 35 | override fun onDestroyView() { 36 | super.onDestroyView() 37 | _binding = null 38 | } 39 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.activity.OnBackPressedCallback 8 | import androidx.databinding.DataBindingUtil 9 | import androidx.databinding.ViewDataBinding 10 | import androidx.fragment.app.Fragment 11 | import androidx.lifecycle.Lifecycle 12 | import androidx.lifecycle.lifecycleScope 13 | import androidx.lifecycle.repeatOnLifecycle 14 | import com.google.android.material.snackbar.Snackbar 15 | import kotlinx.coroutines.flow.Flow 16 | import kotlinx.coroutines.flow.collectLatest 17 | import kotlinx.coroutines.launch 18 | 19 | abstract class BaseFragment(private val layoutId: Int) : Fragment() { 20 | 21 | private var _binding: T? = null 22 | protected val binding 23 | get() = requireNotNull(_binding) 24 | 25 | override fun onCreateView( 26 | inflater: LayoutInflater, 27 | container: ViewGroup?, 28 | savedInstanceState: Bundle? 29 | ): View? { 30 | _binding = DataBindingUtil.inflate(inflater, layoutId, container, false) 31 | binding.lifecycleOwner = viewLifecycleOwner 32 | return binding.root 33 | } 34 | 35 | 36 | override fun onDestroyView() { 37 | super.onDestroyView() 38 | _binding = null 39 | } 40 | 41 | fun collectLatestFlow(flow: Flow, action: suspend (T) -> Unit) { 42 | viewLifecycleOwner.lifecycleScope.launch { 43 | viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { 44 | flow.collectLatest(action) 45 | } 46 | } 47 | } 48 | 49 | fun showSnackBar(messageId: Int) { 50 | Snackbar.make(this.requireView(), messageId, Snackbar.LENGTH_LONG).show() 51 | } 52 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/CustomNaverMap.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation 2 | 3 | import com.naver.maps.geometry.LatLng 4 | import com.naver.maps.geometry.LatLngBounds 5 | import com.naver.maps.map.LocationTrackingMode 6 | import com.naver.maps.map.NaverMap 7 | import com.naver.maps.map.overlay.OverlayImage 8 | import com.naver.maps.map.util.FusedLocationSource 9 | import com.rpg.funbox.R 10 | 11 | object CustomNaverMap { 12 | 13 | private const val MIN_ZOOM = 5.0 14 | private const val MAX_ZOOM = 20.0 15 | private const val SOUTH_WEST_LATITUDE = 31.43 16 | private const val SOUTH_WEST_LONGITUDE = 122.37 17 | private const val NORTH_EAST_LATITUDE = 44.35 18 | private const val NORTH_EAST_LONGITUDE = 132.0 19 | 20 | fun setNaverMap(naverMap: NaverMap, fusedLocationSource: FusedLocationSource): NaverMap { 21 | naverMap.locationSource = fusedLocationSource 22 | naverMap.locationTrackingMode = LocationTrackingMode.Face 23 | naverMap.uiSettings.isLocationButtonEnabled = true 24 | 25 | naverMap.minZoom = MIN_ZOOM 26 | naverMap.maxZoom = MAX_ZOOM 27 | naverMap.uiSettings.isZoomControlEnabled = true 28 | 29 | val locationOverlay = naverMap.locationOverlay 30 | locationOverlay.isVisible = true 31 | locationOverlay.icon = OverlayImage.fromResource(R.drawable.my_location) 32 | 33 | naverMap.extent = LatLngBounds( 34 | LatLng(SOUTH_WEST_LATITUDE, SOUTH_WEST_LONGITUDE), LatLng( 35 | NORTH_EAST_LATITUDE, NORTH_EAST_LONGITUDE 36 | ) 37 | ) 38 | 39 | return naverMap 40 | } 41 | 42 | fun setNaverMapLocationOverlay(naverMap: NaverMap): NaverMap { 43 | naverMap.locationOverlay.apply { 44 | isVisible = true 45 | iconHeight = 120 46 | iconWidth = 120 47 | icon = OverlayImage.fromResource(R.drawable.navi_icon) 48 | } 49 | 50 | return naverMap 51 | } 52 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/MapSocket.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation 2 | 3 | import com.google.gson.Gson 4 | import com.rpg.funbox.presentation.map.ApplyGameToServerData 5 | import com.rpg.funbox.presentation.map.Chat 6 | import com.rpg.funbox.presentation.map.GameApplyAnswerToServerData 7 | import com.rpg.funbox.presentation.map.QuizAnswerToServer 8 | import com.rpg.funbox.presentation.map.SocketApplication 9 | import com.rpg.funbox.presentation.map.VerifyAnswerToServer 10 | import org.json.JSONObject 11 | import timber.log.Timber 12 | 13 | object MapSocket { 14 | val mSocket = SocketApplication.get() 15 | 16 | fun verifyAnswer(roomId: String, isCorrect: Boolean) { 17 | val json = Gson().toJson(VerifyAnswerToServer(isCorrect, roomId)) 18 | mSocket.emit("verifyAnswer", JSONObject(json)) 19 | Timber.d(roomId) 20 | } 21 | 22 | fun sendQuizAnswer(roomId: String, answer: String) { 23 | val json = Gson().toJson(QuizAnswerToServer(answer, roomId)) 24 | mSocket.emit("quizAnswer", JSONObject(json)) 25 | Timber.d("Room: $roomId, Answer: $answer") 26 | } 27 | 28 | fun acceptGame(roomId: String) { 29 | val json = Gson().toJson(GameApplyAnswerToServerData(roomId, "ACCEPT")) 30 | mSocket.emit("gameApplyAnswer", JSONObject(json)) 31 | } 32 | 33 | fun rejectGame(roomId: String) { 34 | val json = Gson().toJson(GameApplyAnswerToServerData(roomId, "REJECT")) 35 | mSocket.emit("gameApplyAnswer", JSONObject(json)) 36 | } 37 | 38 | fun applyGame(id: Int) { 39 | val json = Gson().toJson(ApplyGameToServerData(id)) 40 | mSocket.emit("gameApply", JSONObject(json)) 41 | Timber.d("Other Id: $id") 42 | } 43 | 44 | fun quitGame() { 45 | mSocket.emit("quitGame") 46 | } 47 | 48 | fun send(otherId:Int, message:String){ 49 | val json = Gson().toJson(Chat(otherId,message)) 50 | Timber.d("Other Id: $otherId $message") 51 | mSocket.emit("directMessage", JSONObject(json)) 52 | } 53 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/GameActivity.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import androidx.activity.viewModels 6 | import com.rpg.funbox.app.MainApplication 7 | import com.rpg.funbox.data.JwtDecoder 8 | import com.rpg.funbox.databinding.ActivityGameBinding 9 | import com.rpg.funbox.presentation.MapSocket 10 | import com.rpg.funbox.presentation.game.quiz.QuizViewModel 11 | 12 | class GameActivity : AppCompatActivity() { 13 | 14 | private lateinit var binding: ActivityGameBinding 15 | private val viewModel: QuizViewModel by viewModels() 16 | 17 | private val myUserId = 18 | JwtDecoder.getUser(MainApplication.mySharedPreferences.getJWT("jwt", "")).id 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | binding = ActivityGameBinding.inflate(layoutInflater) 22 | setContentView(binding.root) 23 | binding.vm = viewModel 24 | binding.lifecycleOwner = this 25 | 26 | viewModel.connectSocket(myUserId = myUserId) 27 | 28 | initUsersState() 29 | } 30 | 31 | private fun initUsersState() { 32 | viewModel.setRoomId(intent.getStringExtra("RoomId")) 33 | viewModel.setUserState(intent.getBooleanExtra("StartGame",false)) 34 | viewModel.setUsersInfo(intent.getIntExtra("OtherUserId", -1)) 35 | 36 | if (!viewModel.userState.value) { 37 | viewModel.roomId.value?.let { MapSocket.acceptGame(it) } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/gameselect/GameSelectBindingAdapters.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.gameselect 2 | 3 | import android.view.View 4 | import android.widget.AdapterView 5 | import android.widget.ArrayAdapter 6 | import android.widget.Spinner 7 | import android.widget.TextView 8 | import androidx.databinding.BindingAdapter 9 | import androidx.databinding.InverseBindingAdapter 10 | import androidx.databinding.InverseBindingListener 11 | import androidx.recyclerview.widget.ListAdapter 12 | import androidx.recyclerview.widget.RecyclerView 13 | import com.rpg.funbox.R 14 | import com.rpg.funbox.presentation.game.gameselect.GameSelectUiState.Companion.QUESTION_CARD_TYPE 15 | import timber.log.Timber 16 | 17 | @BindingAdapter("app:game_title") 18 | fun TextView.bindGameTitle(gameCard: GameSelectUiState.GameCard) { 19 | when (gameCard.gameType) { 20 | QUESTION_CARD_TYPE -> { 21 | text = resources.getString(R.string.game_question_card_title) 22 | } 23 | } 24 | } 25 | 26 | @BindingAdapter("app:entries") 27 | fun Spinner.setEntries(entries: List?) { 28 | entries?.run { 29 | val arrayAdapter = ArrayAdapter(context, R.layout.quiz_question_count_spinner, entries) 30 | arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) 31 | adapter = arrayAdapter 32 | } 33 | } 34 | 35 | @InverseBindingAdapter(attribute = "selectedValue", event = "selectedValueAttrChanged") 36 | fun Spinner.getSelectedValue(): Any? { 37 | return selectedItem 38 | } 39 | 40 | @BindingAdapter("selectedValueAttrChanged") 41 | fun Spinner.setInverseBindingAdapter(inverseBindingListener: InverseBindingListener?) { 42 | inverseBindingListener?.run { 43 | onItemSelectedListener = object : AdapterView.OnItemSelectedListener { 44 | override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { 45 | if (tag != position) { 46 | inverseBindingListener.onChange() 47 | } 48 | } 49 | 50 | override fun onNothingSelected(parent: AdapterView<*>?) {} 51 | } 52 | } 53 | } 54 | 55 | @BindingAdapter("selectedValue") 56 | fun Spinner.setSelectedValue(selectedValue: Int) { 57 | adapter?.run { 58 | Timber.d("Select $selectedValue") 59 | val position = (adapter as ArrayAdapter).getPosition(selectedValue) 60 | setSelection(position, false) 61 | tag = position 62 | } 63 | } 64 | 65 | @BindingAdapter("app:submitGames") 66 | fun RecyclerView.bindItems(items: List) { 67 | val adapter = this.adapter ?: return 68 | val listAdapter: ListAdapter = adapter as ListAdapter 69 | listAdapter.submitList(items) 70 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/gameselect/GameSelectFragment.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.gameselect 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.viewModels 6 | import androidx.lifecycle.lifecycleScope 7 | import androidx.navigation.fragment.findNavController 8 | import com.rpg.funbox.R 9 | import com.rpg.funbox.databinding.FragmentGameSelectBinding 10 | import com.rpg.funbox.presentation.BaseFragment 11 | import kotlinx.coroutines.launch 12 | import timber.log.Timber 13 | 14 | class GameSelectFragment : BaseFragment(R.layout.fragment_game_select) { 15 | 16 | private val viewModel: GameSelectViewModel by viewModels() 17 | private val viewModel2: QuestionGameViewModel by viewModels() 18 | private val entry = listOf(1, 5, 10, 20) 19 | 20 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 21 | super.onViewCreated(view, savedInstanceState) 22 | 23 | binding.vm = viewModel 24 | binding.gameListAdapter = GameListAdapter(viewModel) 25 | 26 | lifecycleScope.launch { 27 | viewModel2.quizQuestionCount.collect { 28 | Timber.d("Count: $it") 29 | } 30 | } 31 | 32 | collectLatestFlow(viewModel.gameSelectUiEvent) { handleUiEvent(it) } 33 | } 34 | 35 | private fun handleUiEvent(event: GameSelectUiEvent) = when (event) { 36 | is GameSelectUiEvent.NetworkErrorEvent -> { 37 | showSnackBar(R.string.network_error_message) 38 | } 39 | 40 | is GameSelectUiEvent.GameSelectSuccess -> { 41 | Timber.d("Question Count: ${viewModel2.quizQuestionCount.value}") 42 | // findNavController().navigate(R.id.action_GameSelectFragment_to_WaitFragment) 43 | } 44 | 45 | is GameSelectUiEvent.GameListSubmit -> { 46 | viewModel2.setSpinnerEntry(entry) 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/gameselect/GameSelectUiEvent.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.gameselect 2 | 3 | sealed class GameSelectUiEvent { 4 | 5 | data class NetworkErrorEvent(val message: String = "Network Error") : GameSelectUiEvent() 6 | data object GameSelectSuccess : GameSelectUiEvent() 7 | data object GameListSubmit : GameSelectUiEvent() 8 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/gameselect/GameSelectUiState.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.gameselect 2 | 3 | import java.util.UUID 4 | 5 | sealed class GameSelectUiState(val id: String = UUID.randomUUID().toString()) { 6 | 7 | data class GameCard( 8 | val gameType: Int, 9 | val questionCountList: List = emptyList() 10 | ) : GameSelectUiState() 11 | 12 | data class ToBeDetermined( 13 | val gameType: Int = TO_BE_DETERMINED 14 | ) : GameSelectUiState() 15 | 16 | companion object { 17 | const val TO_BE_DETERMINED = 0 18 | const val QUESTION_CARD_TYPE = 1 19 | const val OTHER_GAMES = 2 20 | } 21 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/gameselect/GameSelectViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.gameselect 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.rpg.funbox.presentation.game.gameselect.GameSelectUiState.Companion.QUESTION_CARD_TYPE 6 | import kotlinx.coroutines.flow.MutableSharedFlow 7 | import kotlinx.coroutines.flow.MutableStateFlow 8 | import kotlinx.coroutines.flow.asSharedFlow 9 | import kotlinx.coroutines.flow.asStateFlow 10 | import kotlinx.coroutines.launch 11 | 12 | class GameSelectViewModel : ViewModel(), OnGameClickListener { 13 | 14 | private val _gameSelectUiEvent = MutableSharedFlow() 15 | val gameSelectUiEvent = _gameSelectUiEvent.asSharedFlow() 16 | 17 | private val _gameList = MutableStateFlow>(listOf()) 18 | val gameList = _gameList.asStateFlow() 19 | 20 | init { 21 | addGameContent() 22 | } 23 | 24 | override fun onClick(game: GameSelectUiState.GameCard) { 25 | viewModelScope.launch { 26 | when (game.gameType) { 27 | QUESTION_CARD_TYPE -> { 28 | _gameSelectUiEvent.emit(GameSelectUiEvent.GameSelectSuccess) 29 | } 30 | } 31 | } 32 | } 33 | 34 | private fun addGameContent() { 35 | _gameList.value += getQuestionCard() 36 | _gameList.value += getToBeDetermined() 37 | viewModelScope.launch { 38 | _gameSelectUiEvent.emit(GameSelectUiEvent.GameListSubmit) 39 | } 40 | } 41 | 42 | private fun getQuestionCard(): GameSelectUiState.GameCard { 43 | return GameSelectUiState.GameCard( 44 | gameType = QUESTION_CARD_TYPE, 45 | questionCountList = listOf(1, 5, 10, 20) 46 | ) 47 | } 48 | 49 | private fun getToBeDetermined(): GameSelectUiState.ToBeDetermined { 50 | return GameSelectUiState.ToBeDetermined() 51 | } 52 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/gameselect/OnGameClickListener.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.gameselect 2 | 3 | interface OnGameClickListener { 4 | fun onClick(game: GameSelectUiState.GameCard) 5 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/gameselect/QuestionGameViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.gameselect 2 | 3 | import androidx.lifecycle.ViewModel 4 | import kotlinx.coroutines.flow.MutableStateFlow 5 | import kotlinx.coroutines.flow.StateFlow 6 | 7 | class QuestionGameViewModel : ViewModel() { 8 | 9 | private val _spinnerEntry = MutableStateFlow(emptyList()) 10 | val spinnerEntry : StateFlow?> = _spinnerEntry 11 | 12 | val quizQuestionCount = MutableStateFlow(0) 13 | 14 | fun setSpinnerEntry(entry: List) { 15 | _spinnerEntry.value = entry 16 | } 17 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/quiz/AnswerCheckFragment.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.quiz 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.activityViewModels 6 | import androidx.lifecycle.lifecycleScope 7 | import com.rpg.funbox.R 8 | import com.rpg.funbox.databinding.FragmentAnswerCheckBinding 9 | import com.rpg.funbox.presentation.BaseDialogFragment 10 | import kotlinx.coroutines.flow.collectLatest 11 | import kotlinx.coroutines.launch 12 | 13 | class AnswerCheckFragment : BaseDialogFragment(R.layout.fragment_answer_check) { 14 | 15 | private val viewModel: QuizViewModel by activityViewModels() 16 | 17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 18 | super.onViewCreated(view, savedInstanceState) 19 | binding.vm = viewModel 20 | 21 | isCancelable = false 22 | 23 | lifecycleScope.launch { 24 | viewModel.quizUiEvent.collectLatest { uiEvent -> 25 | when (uiEvent) { 26 | QuizUiEvent.QuizAnswerCheckRight -> dismiss() 27 | QuizUiEvent.QuizAnswerCheckWrong -> dismiss() 28 | else -> {} 29 | } 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/quiz/ChatAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.quiz 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.DiffUtil 6 | import androidx.recyclerview.widget.ListAdapter 7 | import androidx.recyclerview.widget.RecyclerView 8 | import androidx.recyclerview.widget.RecyclerView.ViewHolder 9 | import androidx.viewbinding.ViewBinding 10 | import com.rpg.funbox.databinding.ItemChatBinding 11 | import com.rpg.funbox.databinding.ItemChatOtherBinding 12 | 13 | class ChatAdapter : ListAdapter(diffUtil) { 14 | class ChatViewHolder(private val binding: ItemChatBinding) : 15 | ViewHolder(binding.root) { 16 | fun bind(item: MessageItem) { 17 | binding.tvChatText.text = item.message 18 | } 19 | } 20 | 21 | class OtherChatViewHolder(private val binding: ItemChatOtherBinding) : 22 | ViewHolder(binding.root) { 23 | fun bind(item: MessageItem) { 24 | binding.tvChatText.text = item.message 25 | } 26 | } 27 | 28 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 29 | return if (viewType == MY_CHAT) { 30 | ChatViewHolder( 31 | ItemChatBinding.inflate( 32 | LayoutInflater.from(parent.context), 33 | parent, 34 | false 35 | ) 36 | ) 37 | } else { 38 | OtherChatViewHolder( 39 | ItemChatOtherBinding.inflate( 40 | LayoutInflater.from(parent.context), 41 | parent, 42 | false 43 | ) 44 | ) 45 | } 46 | } 47 | 48 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 49 | if (getItemViewType(position) == MY_CHAT) { 50 | (holder as ChatViewHolder).bind(currentList[position]) 51 | } else { 52 | (holder as OtherChatViewHolder).bind(currentList[position]) 53 | } 54 | } 55 | 56 | override fun getItemViewType(position: Int): Int { 57 | return if (currentList[position].type == 0) { 58 | MY_CHAT 59 | } else { 60 | OTHER_CHAT 61 | } 62 | } 63 | 64 | companion object { 65 | val diffUtil = object : DiffUtil.ItemCallback() { 66 | override fun areItemsTheSame(oldItem: MessageItem, newItem: MessageItem): Boolean { 67 | return oldItem == newItem 68 | } 69 | 70 | override fun areContentsTheSame(oldItem: MessageItem, newItem: MessageItem): Boolean { 71 | return false 72 | } 73 | } 74 | 75 | 76 | private const val MY_CHAT = 0 77 | private const val OTHER_CHAT = 1 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/quiz/LoadingDialog.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.quiz 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.activityViewModels 6 | import androidx.lifecycle.lifecycleScope 7 | import androidx.navigation.fragment.findNavController 8 | import com.rpg.funbox.R 9 | import com.rpg.funbox.databinding.DialogLoadingBinding 10 | import com.rpg.funbox.presentation.BaseDialogFragment 11 | import kotlinx.coroutines.flow.collect 12 | import kotlinx.coroutines.flow.collectLatest 13 | import kotlinx.coroutines.launch 14 | import timber.log.Timber 15 | 16 | class LoadingDialog : BaseDialogFragment(R.layout.dialog_loading) { 17 | 18 | private val viewModel: QuizViewModel by activityViewModels() 19 | 20 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 21 | super.onViewCreated(view, savedInstanceState) 22 | binding.vm = viewModel 23 | 24 | isCancelable = false 25 | 26 | lifecycleScope.launch { 27 | viewModel.quizUiState.collect { 28 | Timber.d(viewModel.quizUiState.value.isUserQuizState.toString()) 29 | if (it.isUserQuizState) { 30 | dismiss() 31 | } 32 | } 33 | } 34 | 35 | lifecycleScope.launch { 36 | viewModel.quizUiEvent.collectLatest { uiEvent -> 37 | if (uiEvent == QuizUiEvent.QuizScoreBoard) { 38 | findNavController().navigate(R.id.action_loadingDialog_to_scoreBoardFragment) 39 | } 40 | } 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/quiz/MessageItem.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.quiz 2 | 3 | data class MessageItem( 4 | val type:Int, 5 | val message:String 6 | ) 7 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/quiz/NetworkAlertFragment.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.quiz 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.activityViewModels 6 | import androidx.lifecycle.lifecycleScope 7 | import com.rpg.funbox.R 8 | import com.rpg.funbox.databinding.FragmentNetworkAlertBinding 9 | import com.rpg.funbox.presentation.BaseDialogFragment 10 | import kotlinx.coroutines.flow.collectLatest 11 | import kotlinx.coroutines.launch 12 | 13 | class NetworkAlertFragment : BaseDialogFragment(R.layout.fragment_network_alert) { 14 | 15 | private val viewModel: QuizViewModel by activityViewModels() 16 | 17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 18 | super.onViewCreated(view, savedInstanceState) 19 | binding.vm = viewModel 20 | 21 | isCancelable = false 22 | 23 | lifecycleScope.launch { 24 | viewModel.quizUiEvent.collectLatest { 25 | if (it == QuizUiEvent.QuizFinish) { 26 | requireActivity().finish() 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/quiz/QuizBindingAdapters.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.quiz 2 | 3 | import android.widget.EditText 4 | import android.widget.ImageView 5 | import android.widget.TextView 6 | import androidx.core.net.toUri 7 | import androidx.databinding.BindingAdapter 8 | import coil.load 9 | import com.rpg.funbox.R 10 | 11 | @BindingAdapter("app:setImage") 12 | fun ImageView.bindImage(url: String?) { 13 | if (url == null) { 14 | load(R.drawable.profile_none) 15 | } else { 16 | load("https://kr.object.ncloudstorage.com/funbox-profiles/${url}".toUri()) 17 | } 18 | } 19 | 20 | @BindingAdapter("app:question_marquee") 21 | fun TextView.bindMarquee(quizUiState: QuizUiState) { 22 | isSelected = true 23 | } 24 | 25 | @BindingAdapter("app:delete_text") 26 | fun EditText.deleteText(quizUiState: QuizUiState) { 27 | if (!quizUiState.isEtEnable) { 28 | text = null 29 | } 30 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/quiz/QuizUiEvent.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.quiz 2 | 3 | sealed class QuizUiEvent { 4 | 5 | data class NetworkErrorEvent(val message: String = "네트워크에 문제가 있습니다.") : QuizUiEvent() 6 | 7 | data object WaitSuccess : QuizUiEvent() 8 | 9 | data object QuizAnswerSubmit : QuizUiEvent() 10 | 11 | data object QuizNetworkDisconnected : QuizUiEvent() 12 | 13 | data object QuizAnswerCheckStart : QuizUiEvent() 14 | 15 | data object QuizAnswerCheckRight : QuizUiEvent() 16 | 17 | data object QuizAnswerCheckWrong : QuizUiEvent() 18 | 19 | data object QuizScoreBoard : QuizUiEvent() 20 | 21 | data object QuizFinish : QuizUiEvent() 22 | 23 | data object SendMessage : QuizUiEvent() 24 | 25 | data object ReceiveMessage : QuizUiEvent() 26 | 27 | data object OtherPlaying : QuizUiEvent() 28 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/quiz/QuizUiState.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.quiz 2 | 3 | data class QuizUiState( 4 | val waiting: Boolean = true, 5 | val finishWaiting: Boolean = false, 6 | val answerValidState: Boolean = false, 7 | val answerWriteState: Boolean = true, 8 | val userQuizState: UserQuizState = UserQuizState.Answer 9 | ) { 10 | val isAnswerSubmitBtnEnable: Boolean = (answerValidState) 11 | val isEtEnable: Boolean = (answerWriteState) 12 | val isUserQuizState: Boolean = (userQuizState == UserQuizState.Quiz) 13 | } 14 | 15 | enum class UserQuizState { 16 | Quiz, Answer 17 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/quiz/ScoreBoardFragment.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.quiz 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.activityViewModels 6 | import androidx.lifecycle.lifecycleScope 7 | import com.rpg.funbox.R 8 | import com.rpg.funbox.databinding.FragmentScoreBoardBinding 9 | import com.rpg.funbox.presentation.BaseDialogFragment 10 | import kotlinx.coroutines.flow.collectLatest 11 | import kotlinx.coroutines.launch 12 | 13 | class ScoreBoardFragment : BaseDialogFragment(R.layout.fragment_score_board) { 14 | 15 | private val viewModel: QuizViewModel by activityViewModels() 16 | 17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 18 | super.onViewCreated(view, savedInstanceState) 19 | binding.vm = viewModel 20 | 21 | isCancelable = false 22 | 23 | lifecycleScope.launch { 24 | viewModel.quizUiEvent.collectLatest { 25 | if (it == QuizUiEvent.QuizFinish) { 26 | requireActivity().finish() 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/game/wait/WaitFragment.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.game.wait 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.view.View 6 | import android.widget.Toast 7 | import androidx.activity.OnBackPressedCallback 8 | import androidx.fragment.app.activityViewModels 9 | import androidx.navigation.fragment.findNavController 10 | import com.rpg.funbox.R 11 | import com.rpg.funbox.databinding.FragmentWaitBinding 12 | import com.rpg.funbox.presentation.BaseFragment 13 | import com.rpg.funbox.presentation.MapSocket 14 | import com.rpg.funbox.presentation.game.quiz.QuizUiEvent 15 | import com.rpg.funbox.presentation.game.quiz.QuizViewModel 16 | 17 | class WaitFragment : BaseFragment(R.layout.fragment_wait) { 18 | 19 | private val viewModel: QuizViewModel by activityViewModels() 20 | private lateinit var backPressedCallback: OnBackPressedCallback 21 | private var backPressTime: Long = 0 22 | 23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | super.onViewCreated(view, savedInstanceState) 25 | binding.vm = viewModel 26 | 27 | collectLatestFlow(viewModel.quizUiEvent) { handleUiEvent(it) } 28 | 29 | if (!viewModel.userState.value) { 30 | findNavController().navigate(R.id.action_WaitFragment_to_QuizFragment) 31 | } 32 | } 33 | 34 | override fun onAttach(context: Context) { 35 | super.onAttach(context) 36 | 37 | setBackPressedCallback() 38 | } 39 | 40 | private fun handleUiEvent(event: QuizUiEvent) = when (event) { 41 | is QuizUiEvent.NetworkErrorEvent -> { 42 | showSnackBar(R.string.network_error_message) 43 | requireActivity().finish() 44 | } 45 | 46 | is QuizUiEvent.WaitSuccess -> { 47 | findNavController().navigate(R.id.action_WaitFragment_to_QuizFragment) 48 | } 49 | 50 | is QuizUiEvent.QuizFinish -> { 51 | requireActivity().finish() 52 | } 53 | 54 | is QuizUiEvent.OtherPlaying -> { 55 | showSnackBar(R.string.other_not) 56 | requireActivity().finish() 57 | } 58 | 59 | else -> {} 60 | } 61 | 62 | private fun setBackPressedCallback() { 63 | backPressedCallback = object : OnBackPressedCallback(true) { 64 | override fun handleOnBackPressed() { 65 | if (backPressTime + 3000 > System.currentTimeMillis()) { 66 | MapSocket.quitGame() 67 | requireActivity().finish() 68 | } else { 69 | Toast.makeText(requireContext(), resources.getString(R.string.finish_quiz_toast_message), Toast.LENGTH_LONG).show() 70 | backPressTime = System.currentTimeMillis() 71 | } 72 | } 73 | } 74 | requireActivity().onBackPressedDispatcher.addCallback( 75 | this, 76 | backPressedCallback 77 | ) 78 | } 79 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/AccessPermission.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login 2 | 3 | import android.Manifest 4 | 5 | object AccessPermission { 6 | const val LOCATION_PERMISSION_REQUEST_CODE = 5000 7 | 8 | val profilePermissionList = arrayOf( 9 | Manifest.permission.CAMERA, 10 | Manifest.permission.WRITE_EXTERNAL_STORAGE 11 | ) 12 | 13 | val locationPermissionList = arrayOf( 14 | Manifest.permission.ACCESS_FINE_LOCATION, 15 | Manifest.permission.ACCESS_COARSE_LOCATION 16 | ) 17 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/TitleActivity.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login 2 | 3 | import android.graphics.Rect 4 | import androidx.appcompat.app.AppCompatActivity 5 | import android.os.Bundle 6 | import android.view.MotionEvent 7 | import android.view.View 8 | import android.view.inputmethod.InputMethodManager 9 | import android.widget.EditText 10 | import androidx.activity.viewModels 11 | import com.rpg.funbox.databinding.ActivityTitleBinding 12 | import com.rpg.funbox.presentation.login.title.TitleViewModel 13 | 14 | class TitleActivity : AppCompatActivity() { 15 | 16 | private lateinit var binding: ActivityTitleBinding 17 | private val viewModel: TitleViewModel by viewModels() 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | binding = ActivityTitleBinding.inflate(layoutInflater) 22 | setContentView(binding.root) 23 | 24 | binding.vm = viewModel 25 | } 26 | 27 | override fun dispatchTouchEvent(event: MotionEvent?): Boolean { 28 | if (event?.action != MotionEvent.ACTION_DOWN) { 29 | return super.dispatchTouchEvent(event) 30 | } 31 | 32 | if (this.currentFocus is EditText) { 33 | val outRect = Rect() 34 | this.currentFocus?.let { 35 | it.getGlobalVisibleRect(outRect) 36 | hideKeyboard(outRect, event, it) 37 | } 38 | } 39 | 40 | return super.dispatchTouchEvent(event) 41 | } 42 | 43 | private fun hideKeyboard( 44 | outRect: Rect, 45 | event: MotionEvent, 46 | it: View 47 | ) { 48 | if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) { 49 | it.clearFocus() 50 | 51 | val inputMethodManager: InputMethodManager = 52 | this.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager 53 | inputMethodManager.hideSoftInputFromWindow( 54 | it.windowToken, 55 | InputMethodManager.HIDE_NOT_ALWAYS 56 | ) 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/nickname/NicknameFragment.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.nickname 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.activityViewModels 6 | import androidx.navigation.fragment.findNavController 7 | import com.rpg.funbox.R 8 | import com.rpg.funbox.databinding.FragmentNicknameBinding 9 | import com.rpg.funbox.presentation.BaseFragment 10 | import com.rpg.funbox.presentation.login.title.TitleViewModel 11 | 12 | class NicknameFragment : BaseFragment(R.layout.fragment_nickname) { 13 | 14 | private val viewModel: TitleViewModel by activityViewModels() 15 | 16 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 17 | super.onViewCreated(view, savedInstanceState) 18 | binding.vm = viewModel 19 | 20 | collectLatestFlow(viewModel.nicknameUiEvent) { handleUiEvent(it) } 21 | } 22 | 23 | private fun handleUiEvent(event: NicknameUiEvent) = when (event) { 24 | is NicknameUiEvent.NicknameSubmit -> { 25 | findNavController().navigate(R.id.action_NicknameFragment_to_ProfileFragment) 26 | } 27 | 28 | is NicknameUiEvent.NetworkErrorEvent -> { 29 | showSnackBar(R.string.network_error_message) 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/nickname/NicknameUiEvent.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.nickname 2 | 3 | sealed class NicknameUiEvent { 4 | data class NetworkErrorEvent(val message: String = "Network Error") : NicknameUiEvent() 5 | 6 | data object NicknameSubmit : NicknameUiEvent() 7 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/nickname/NicknameUiState.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.nickname 2 | 3 | data class NicknameUiState( 4 | val nicknameValidState: NicknameValidState = NicknameValidState.None 5 | ) { 6 | val isNextBtnEnable: Boolean = (nicknameValidState == NicknameValidState.Valid) 7 | } 8 | 9 | enum class NicknameValidState { 10 | None, Valid 11 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/profile/ProfileBindingAdapters.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.profile 2 | 3 | import android.net.Uri 4 | import android.widget.ImageButton 5 | import androidx.databinding.BindingAdapter 6 | import coil.load 7 | import coil.size.Scale 8 | import coil.transform.RoundedCornersTransformation 9 | import com.rpg.funbox.R 10 | 11 | @BindingAdapter("app:profile_image_uri") 12 | fun ImageButton.bindImageUri(uri: Uri?) { 13 | if (uri == null) { 14 | setImageResource(R.drawable.group_1940) 15 | } else { 16 | load(uri) { 17 | scale(Scale.FILL) 18 | crossfade(enable = true) 19 | transformations(RoundedCornersTransformation(10F)) 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/profile/ProfileUiEvent.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.profile 2 | 3 | sealed class ProfileUiEvent { 4 | 5 | data class NetworkErrorEvent(val message: String = "Network Error") : ProfileUiEvent() 6 | data object ProfileSubmit : ProfileUiEvent() 7 | data object ProfileSelect : ProfileUiEvent() 8 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/profile/ProfileUiState.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.profile 2 | 3 | data class ProfileUiState( 4 | val profileValidState: ProfileValidState = ProfileValidState.None 5 | ) { 6 | val isBtnProfileEnable: Boolean = (profileValidState == ProfileValidState.Valid) 7 | } 8 | 9 | enum class ProfileValidState { 10 | None, Valid 11 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/splash/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.splash 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Intent 5 | import androidx.appcompat.app.AppCompatActivity 6 | import android.os.Bundle 7 | import androidx.activity.viewModels 8 | import androidx.lifecycle.lifecycleScope 9 | import com.rpg.funbox.app.MainApplication 10 | import com.rpg.funbox.presentation.MainActivity 11 | import com.rpg.funbox.presentation.login.TitleActivity 12 | import kotlinx.coroutines.flow.collectLatest 13 | import kotlinx.coroutines.launch 14 | import timber.log.Timber 15 | 16 | @SuppressLint("CustomSplashScreen") 17 | class SplashActivity : AppCompatActivity() { 18 | 19 | private val viewModel: SplashViewModel by viewModels() 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | 24 | // MainApplication.mySharedPreferences.setJWT("jwt", "") 25 | 26 | collectLatestUiEvent() 27 | } 28 | 29 | private fun collectLatestUiEvent() { 30 | lifecycleScope.launch { 31 | viewModel.getUsersLocations(0.0, 0.0) 32 | viewModel.splashUiEvent.collectLatest { splashUiEvent -> 33 | when (splashUiEvent) { 34 | is SplashUiEvent.NetworkErrorEvent -> { 35 | val intent = Intent(this@SplashActivity, TitleActivity::class.java) 36 | intent.putExtra("ServerError", true) 37 | Timber.d("NetworkError") 38 | startActivity(intent) 39 | } 40 | 41 | is SplashUiEvent.Unauthorized -> { 42 | val intent = Intent(this@SplashActivity, TitleActivity::class.java) 43 | intent.putExtra("ServerError", false) 44 | startActivity(intent) 45 | } 46 | 47 | is SplashUiEvent.GetUsersLocationsSuccess -> { 48 | startActivity(Intent(this@SplashActivity, MainActivity::class.java)) 49 | } 50 | } 51 | this@SplashActivity.finish() 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/splash/SplashUiEvent.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.splash 2 | 3 | sealed class SplashUiEvent { 4 | 5 | data class NetworkErrorEvent(val message: String = "Network Error") : SplashUiEvent() 6 | 7 | data object GetUsersLocationsSuccess : SplashUiEvent() 8 | 9 | data object Unauthorized : SplashUiEvent() 10 | } 11 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/splash/SplashViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.splash 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.rpg.funbox.data.repository.UserRepository 6 | import com.rpg.funbox.data.repository.UserRepositoryImpl 7 | import com.rpg.funbox.data.repository.UsersLocationRepository 8 | import com.rpg.funbox.data.repository.UsersLocationRepositoryImpl 9 | import kotlinx.coroutines.flow.MutableSharedFlow 10 | import kotlinx.coroutines.flow.asSharedFlow 11 | import kotlinx.coroutines.launch 12 | import timber.log.Timber 13 | 14 | class SplashViewModel : ViewModel() { 15 | 16 | private val usersLocationRepository: UsersLocationRepository = UsersLocationRepositoryImpl() 17 | private val userRepository: UserRepository = UserRepositoryImpl() 18 | 19 | private val _splashUiEvent = MutableSharedFlow() 20 | val splashUiEvent = _splashUiEvent.asSharedFlow() 21 | 22 | private fun validateUserName(userName: String?): Boolean { 23 | return (userName != null) 24 | } 25 | 26 | fun getUsersLocations(locX: Double, locY: Double) { 27 | viewModelScope.launch { 28 | try { 29 | usersLocationRepository.getUsersLocation(locX, locY)?.let { userLocationResponse -> 30 | when (userLocationResponse.resultMessage) { 31 | "OK" -> { 32 | if (validateUserName(userRepository.getUserInfo()?.userName)) { 33 | _splashUiEvent.emit(SplashUiEvent.GetUsersLocationsSuccess) 34 | Timber.d("OK") 35 | } else { 36 | _splashUiEvent.emit(SplashUiEvent.Unauthorized) 37 | Timber.d("No Nickname") 38 | } 39 | } 40 | 41 | "Unauthorized" -> { 42 | _splashUiEvent.emit(SplashUiEvent.Unauthorized) 43 | Timber.d("Unauthorized") 44 | } 45 | 46 | else -> { 47 | _splashUiEvent.emit(SplashUiEvent.NetworkErrorEvent()) 48 | Timber.d("else") 49 | } 50 | } 51 | } 52 | } catch (e: Exception) { 53 | _splashUiEvent.emit(SplashUiEvent.NetworkErrorEvent()) 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/title/TitleBindingAdapters.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.title 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.res.ColorStateList 5 | import android.widget.ImageView 6 | import androidx.databinding.BindingAdapter 7 | import com.rpg.funbox.R 8 | 9 | @SuppressLint("ResourceAsColor") 10 | @BindingAdapter("app:image_tint") 11 | fun ImageView.bindTint(titleUiState: TitleUiState) { 12 | when (titleUiState.networkSuccess) { 13 | false -> { 14 | imageTintList = ColorStateList.valueOf(R.color.disabled_color) 15 | } 16 | 17 | else -> {} 18 | } 19 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/title/TitleUiEvent.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.title 2 | 3 | sealed class TitleUiEvent { 4 | 5 | data class NetworkErrorEvent(val message: String = "Network Error") : TitleUiEvent() 6 | 7 | data object NaverLoginStart : TitleUiEvent() 8 | 9 | data object SignUpStart : TitleUiEvent() 10 | 11 | data object NaverLoginSuccess : TitleUiEvent() 12 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/login/title/TitleUiState.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.login.title 2 | 3 | data class TitleUiState( 4 | val networkSuccess: Boolean = true 5 | ) { 6 | val isNaverLoginBtnEnabled: Boolean = (networkSuccess) 7 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/map/GameData.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.map 2 | 3 | data class ApplyGameToServerData( 4 | val opponentId: Int 5 | ) 6 | 7 | data class ApplyGameFromServerData( 8 | val userId: String, 9 | val roomId: String 10 | ) 11 | 12 | data class GameApplyAnswerToServerData( 13 | val roomId: String, 14 | val answer: String, 15 | ) 16 | 17 | data class GameApplyAnswerFromServerData( 18 | val answer: String, 19 | ) 20 | 21 | data class QuizFromServer( 22 | val roomId: String, 23 | val quiz: String, 24 | val target: Int 25 | ) 26 | 27 | data class QuizAnswerToServer( 28 | val answer: String, 29 | val roomId: String 30 | ) 31 | 32 | data class QuizAnswerFromServer( 33 | val answer: String, 34 | val roomId: String 35 | ) 36 | 37 | data class VerifyAnswerToServer( 38 | val isCorrect: Boolean, 39 | val roomId: String 40 | ) 41 | 42 | class ScoreFromServer : ArrayList() 43 | 44 | data class ScoreFromServerItem( 45 | val id: Int, 46 | val score: Int, 47 | val username: String 48 | ) 49 | 50 | data class Chat( 51 | val userId: Int, 52 | val message: String 53 | ) -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/map/GetGameDialog.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.map 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.activityViewModels 6 | import androidx.lifecycle.lifecycleScope 7 | import com.rpg.funbox.R 8 | import com.rpg.funbox.databinding.DialogGetGameBinding 9 | import com.rpg.funbox.presentation.BaseDialogFragment 10 | import com.rpg.funbox.presentation.setting.SettingViewModel 11 | import kotlinx.coroutines.flow.collectLatest 12 | import kotlinx.coroutines.launch 13 | 14 | class GetGameDialog : BaseDialogFragment(R.layout.dialog_get_game) { 15 | 16 | private val viewModel: MapViewModel by activityViewModels() 17 | private val settingViewModel: SettingViewModel by activityViewModels() 18 | 19 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 20 | super.onViewCreated(view, savedInstanceState) 21 | binding.vm = viewModel 22 | 23 | isCancelable = false 24 | 25 | lifecycleScope.launch { 26 | viewModel.mapUiEvent.collectLatest { uiEvent -> 27 | if (uiEvent == MapUiEvent.CancelGame) dismiss() 28 | } 29 | } 30 | 31 | binding.btnOk.setOnClickListener { 32 | viewModel.toGame() 33 | settingViewModel.toGame() 34 | dismiss() 35 | } 36 | 37 | binding.btnNo.setOnClickListener{ 38 | viewModel.rejectGame() 39 | settingViewModel.rejectGame() 40 | dismiss() 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/map/MapBindingAdapters.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.map 2 | 3 | class MapBindingAdapters { 4 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/map/MapProfile.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.map 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.os.Build 6 | import android.util.AttributeSet 7 | import android.view.LayoutInflater 8 | import androidx.annotation.RequiresApi 9 | import androidx.constraintlayout.widget.ConstraintLayout 10 | import com.rpg.funbox.databinding.MapProfileBinding 11 | import kotlinx.coroutines.GlobalScope 12 | import kotlinx.coroutines.launch 13 | 14 | class MapProfile @JvmOverloads constructor( 15 | context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 16 | ) : ConstraintLayout(context, attrs, defStyleAttr) { 17 | private val layoutInflater = LayoutInflater.from(context) 18 | private val binding: MapProfileBinding = MapProfileBinding.inflate(layoutInflater, this, true) 19 | 20 | @RequiresApi(Build.VERSION_CODES.P) 21 | fun setProfile(image: Bitmap) { 22 | binding.ivProfile.setImageBitmap(image) 23 | binding.ivProfile.invalidate() 24 | } 25 | 26 | fun setName(text: String) { 27 | binding.tvProfileName.text = text 28 | } 29 | 30 | fun setMsg(text: String) { 31 | binding.tvProfileMsg.text = text 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/map/MapProfileAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.map 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.os.Build 6 | import android.view.View 7 | import androidx.annotation.RequiresApi 8 | import com.naver.maps.map.overlay.InfoWindow 9 | import com.rpg.funbox.data.dto.UserDetail 10 | import com.rpg.funbox.data.dto.UserInfoResponse 11 | 12 | class MapProfileAdapter( 13 | private val pContext: Context, 14 | private val userDetail: UserDetail?, 15 | private val image: Bitmap?, 16 | ) : InfoWindow.DefaultViewAdapter(pContext) { 17 | @RequiresApi(Build.VERSION_CODES.P) 18 | override fun getContentView(p0: InfoWindow): View { 19 | val mapProfile = MapProfile(pContext) 20 | if (image != null) { 21 | mapProfile.setProfile(image) 22 | } 23 | mapProfile.invalidate() 24 | userDetail?.let { 25 | mapProfile.setMsg(it.msg) 26 | mapProfile.setName(it.name) 27 | } 28 | 29 | return mapProfile 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/map/MapUiEvent.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.map 2 | 3 | sealed class MapUiEvent { 4 | 5 | data class NetworkErrorEvent(val message: String = "Network Error") : MapUiEvent() 6 | 7 | data object LocationPermitted : MapUiEvent() 8 | 9 | data object MessageOpen : MapUiEvent() 10 | 11 | data object ToGame : MapUiEvent() 12 | 13 | data object RejectGame : MapUiEvent() 14 | 15 | data object GetGame : MapUiEvent() 16 | 17 | data object ToSetting : MapUiEvent() 18 | 19 | data object Toggle : MapUiEvent() 20 | 21 | data object GameStart : MapUiEvent() 22 | 23 | data object MessageSubmit : MapUiEvent() 24 | 25 | data object CancelGame : MapUiEvent() 26 | 27 | data object Change : MapUiEvent() 28 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/map/MessageDialog.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.map 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.activityViewModels 6 | import androidx.lifecycle.lifecycleScope 7 | import com.rpg.funbox.R 8 | import com.rpg.funbox.databinding.DialogMessageBinding 9 | import com.rpg.funbox.presentation.BaseDialogFragment 10 | import com.rpg.funbox.presentation.setting.SettingViewModel 11 | import kotlinx.coroutines.flow.collectLatest 12 | import kotlinx.coroutines.launch 13 | 14 | class MessageDialog: BaseDialogFragment(R.layout.dialog_message) { 15 | 16 | private val viewModel: MapViewModel by activityViewModels() 17 | 18 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 19 | super.onViewCreated(view, savedInstanceState) 20 | binding.vm = viewModel 21 | 22 | lifecycleScope.launch { 23 | viewModel.mapUiEvent.collectLatest { uiEvent -> 24 | if (uiEvent == MapUiEvent.MessageSubmit) dismiss() 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/map/OtherUserState.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.map 2 | 3 | data class OtherUserState (val otherState: OtherState = OtherState.Online){ 4 | val canStart : Boolean = (otherState == OtherState.Online) 5 | } 6 | 7 | enum class OtherState { 8 | Online, 9 | Offline, 10 | Playing 11 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/map/SocketApplication.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.map 2 | import com.rpg.funbox.app.MainApplication 3 | import io.socket.client.IO 4 | import io.socket.client.Socket 5 | import java.net.URISyntaxException 6 | import java.util.Collections.singletonList 7 | import java.util.Collections.singletonMap 8 | 9 | class SocketApplication { 10 | companion object { 11 | private lateinit var socket : Socket 12 | fun get(): Socket { 13 | try { 14 | val options = IO.Options() 15 | options.extraHeaders = singletonMap("Authorization",singletonList("Bearer ${MainApplication.mySharedPreferences.getJWT("jwt", "")}")) 16 | socket = IO.socket("http://175.45.193.191:3000/socket",options) 17 | } catch (e: URISyntaxException) { 18 | e.printStackTrace() 19 | } 20 | return socket 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/setting/SetNameDialog.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.setting 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.activityViewModels 6 | import androidx.lifecycle.lifecycleScope 7 | import com.rpg.funbox.R 8 | import com.rpg.funbox.databinding.DialogPositiveBinding 9 | import com.rpg.funbox.presentation.BaseDialogFragment 10 | import kotlinx.coroutines.flow.collectLatest 11 | import kotlinx.coroutines.launch 12 | 13 | class SetNameDialog : BaseDialogFragment(R.layout.dialog_positive) { 14 | 15 | private val viewModel: SettingViewModel by activityViewModels() 16 | 17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 18 | super.onViewCreated(view, savedInstanceState) 19 | binding.vm = viewModel 20 | 21 | lifecycleScope.launch { 22 | viewModel.settingUiEvent.collectLatest { 23 | if (it == SettingUiEvent.CloseSetNameDialog) dismiss() 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/setting/SettingBindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.setting 2 | 3 | import android.net.Uri 4 | import android.widget.ImageButton 5 | import android.widget.ImageView 6 | import androidx.databinding.BindingAdapter 7 | import coil.load 8 | import com.rpg.funbox.R 9 | import timber.log.Timber 10 | import java.io.IOException 11 | 12 | @BindingAdapter("app:imageUrlBtn") 13 | fun ImageButton.loadImage(uri: Uri?) { 14 | if (uri == null) { 15 | load(R.drawable.profile_none) 16 | } else { 17 | load(uri) 18 | } 19 | } 20 | 21 | @BindingAdapter("app:imageUrl") 22 | fun ImageView.loadImage(uri: Uri?) { 23 | if (uri == null) { 24 | load(R.drawable.profile_none) 25 | } else { 26 | load(uri) 27 | } 28 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/setting/SettingUiEvent.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.setting 2 | 3 | import com.rpg.funbox.presentation.map.MapUiEvent 4 | 5 | sealed class SettingUiEvent { 6 | 7 | data object GoToMapFragment : SettingUiEvent() 8 | 9 | data object SetName : SettingUiEvent() 10 | 11 | data object SelectProfile : SettingUiEvent() 12 | 13 | data object SetProfile : SettingUiEvent() 14 | 15 | data object StartWithdrawal : SettingUiEvent() 16 | 17 | data object Withdraw : SettingUiEvent() 18 | 19 | data object CloseSetNameDialog : SettingUiEvent() 20 | 21 | data object CloseSetProfileDialog : SettingUiEvent() 22 | 23 | data object ToGame : SettingUiEvent() 24 | 25 | data object RejectGame : SettingUiEvent() 26 | 27 | data object GetGame : SettingUiEvent() 28 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/setting/SettingUiState.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.setting 2 | 3 | import com.rpg.funbox.presentation.login.nickname.NicknameValidState 4 | 5 | data class SettingUiState( 6 | val nicknameValidState: NicknameValidState = NicknameValidState.None 7 | ) { 8 | val isSubmitBtnEnable: Boolean = (nicknameValidState == NicknameValidState.Valid) 9 | } 10 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/java/com/rpg/funbox/presentation/setting/WithdrawalDialog.kt: -------------------------------------------------------------------------------- 1 | package com.rpg.funbox.presentation.setting 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.activityViewModels 6 | import androidx.lifecycle.lifecycleScope 7 | import com.rpg.funbox.R 8 | import com.rpg.funbox.databinding.DialogRedWithTextBinding 9 | import com.rpg.funbox.presentation.BaseDialogFragment 10 | import kotlinx.coroutines.flow.collectLatest 11 | import kotlinx.coroutines.launch 12 | 13 | class WithdrawalDialog : BaseDialogFragment(R.layout.dialog_red_with_text) { 14 | 15 | private val viewModel: SettingViewModel by activityViewModels() 16 | 17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 18 | super.onViewCreated(view, savedInstanceState) 19 | binding.vm = viewModel 20 | 21 | lifecycleScope.launch { 22 | viewModel.settingUiEvent.collectLatest { uiEvent -> 23 | if (uiEvent == SettingUiEvent.Withdraw) { 24 | dismiss() 25 | } 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/anim/slide_left_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/anim/slide_left_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/answer_edittext.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/autorenew_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/baseline_arrow_back_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/bg_dialog_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/bg_green_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/bg_input_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/bg_red_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/close_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/fail_button.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/funbox_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/drawable/funbox_logo.png -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/group_1940.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 13 | 17 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/map_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/menu_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/my_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/drawable/my_location.png -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/naver_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/drawable/naver_login.png -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/navi_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/drawable/navi_icon.png -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/navigate_next_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/nickname_edittext.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/other_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/drawable/other_location.png -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/profile_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/drawable/profile_add.png -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/profile_game.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/profile_none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/drawable/profile_none.png -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/round_radius.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/rounded_corner_rect_shadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/settings_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/splash_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/success_button.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/wait_background.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/drawable/write_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/activity_game.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 18 | 19 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 19 | 20 | 31 | 32 | 33 | 34 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/activity_title.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 18 | 19 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/dialog_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 14 | 15 | 29 | 30 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/dialog_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 17 | 18 | 27 | 28 | 39 | 40 | 54 | 55 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/dialog_positive.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 17 | 18 | 27 | 28 | 44 | 45 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/dialog_profile_change.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 18 | 19 | 28 | 29 | 44 | 45 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/dialog_red.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 24 | 25 | 40 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/dialog_red_with_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 18 | 19 | 33 | 34 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/fragment_game_select.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 17 | 18 | 23 | 24 | 34 | 35 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/fragment_network_alert.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 19 | 20 | 33 | 34 | 48 | 49 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/fragment_profile.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 18 | 19 | 31 | 32 | 46 | 47 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/fragment_title.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 22 | 23 | 34 | 35 | 48 | 49 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/item_chat.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 22 | 23 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/item_chat_other.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 22 | 23 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/item_game_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 33 | 34 | 44 | 45 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/map_profile.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 18 | 19 | 32 | 33 | 46 | 47 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/menu_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 18 | 19 | 30 | 31 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/quiz_question_count_spinner.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/layout/to_be_determined_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 23 | 24 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/menu/side_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-anydpi-v26/ic_funbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-anydpi-v26/ic_funbox_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-anydpi/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-hdpi/ic_funbox.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-hdpi/ic_funbox.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-hdpi/ic_funbox_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-hdpi/ic_funbox_foreground.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-hdpi/ic_funbox_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-hdpi/ic_funbox_round.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-mdpi/ic_funbox.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-mdpi/ic_funbox.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-mdpi/ic_funbox_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-mdpi/ic_funbox_foreground.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-mdpi/ic_funbox_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-mdpi/ic_funbox_round.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xhdpi/ic_funbox.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xhdpi/ic_funbox.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xhdpi/ic_funbox_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xhdpi/ic_funbox_foreground.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xhdpi/ic_funbox_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xhdpi/ic_funbox_round.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xxhdpi/ic_funbox.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xxhdpi/ic_funbox.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xxhdpi/ic_funbox_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xxhdpi/ic_funbox_foreground.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xxhdpi/ic_funbox_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xxhdpi/ic_funbox_round.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xxxhdpi/ic_funbox.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xxxhdpi/ic_funbox.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xxxhdpi/ic_funbox_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xxxhdpi/ic_funbox_foreground.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xxxhdpi/ic_funbox_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xxxhdpi/ic_funbox_round.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/and05-funbox/54835eff4fd565bc48690de51d31b0cf4e07b94f/Android/FunBox/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/navigation/game_nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 21 | 22 | 27 | 28 | 31 | 34 | 37 | 40 | 41 | 42 | 43 | 47 | 48 | 52 | 53 | 56 | 57 | 58 | 59 | 63 | 64 | 68 | 69 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/navigation/title_nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 18 | 19 | 20 | 25 | 30 | 31 | 32 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF000000 4 | #FFFFFFFF 5 | #FFF5EFE7 6 | #FF00DFA2 7 | #FFE8DFCA 8 | #FFD9D9D9 9 | #00000000 10 | #CCADFF 11 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/values/ic_funbox_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /Android/FunBox/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 |