├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── engineering-issue-.md │ └── feature_request.md └── pull_request_template.md ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── androidTestResultsUserPreferences.xml ├── compiler.xml ├── deploymentTargetDropDown.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── clicker │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── CMakeLists.txt │ │ ├── blur_effect.cpp │ │ ├── camera_engine.h │ │ ├── camera_manager.cpp │ │ ├── camera_manager.h │ │ ├── camera_stream.cpp │ │ ├── cube_code.cpp │ │ ├── dino_run │ │ │ ├── dino_run.cpp │ │ │ ├── dino_run.h │ │ │ ├── dino_run_actions.cpp │ │ │ ├── dino_run_actions.h │ │ │ ├── dino_run_setup.cpp │ │ │ └── dino_run_setup.h │ │ ├── gl_code.cpp │ │ ├── my_class.cpp │ │ ├── my_class.h │ │ ├── parsing │ │ │ ├── amf_encoding.cpp │ │ │ └── parse_url.cpp │ │ ├── ping_pong │ │ │ ├── ping_pong.cpp │ │ │ ├── ping_pong_movement.cpp │ │ │ └── ping_pong_movement.h │ │ ├── rtmp_client.cpp │ │ ├── rtmp_client.h │ │ ├── square_code.cpp │ │ ├── triangle_strip │ │ │ └── triangle_strip.cpp │ │ ├── understand_triangle.cpp │ │ └── video_encoder.cpp │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── clicker │ │ │ ├── MainActivity.kt │ │ │ ├── broadcastReceivers │ │ │ └── ShutDownBroadcastReceiver.kt │ │ │ ├── cameraNDK │ │ │ └── CameraNDKNativeActivity.kt │ │ │ ├── composeMetricsInitScript.gradle │ │ │ ├── data │ │ │ ├── TokenDataStore.kt │ │ │ ├── TokenValidationWorker.kt │ │ │ ├── domains │ │ │ │ └── PinnedItemInter.kt │ │ │ ├── repos │ │ │ │ └── PinnedItemRepo.kt │ │ │ ├── room │ │ │ │ ├── DataAccessObjects.kt │ │ │ │ ├── DataEntities.kt │ │ │ │ └── PinnedItemsDatabase.kt │ │ │ └── workManager │ │ │ │ └── OAuthTokeValidationWorker.kt │ │ │ ├── di │ │ │ ├── HiltApplication.kt │ │ │ └── modules │ │ │ │ ├── SingletonModule.kt │ │ │ │ └── ViewModelModule.kt │ │ │ ├── domain │ │ │ ├── ChatSettingsDataStore.kt │ │ │ ├── TwitchDataStore.kt │ │ │ └── TwitchTokenValidationWorker.kt │ │ │ ├── nativeLibraryClasses │ │ │ ├── CameraStreamNDK.kt │ │ │ └── NativeLoading.kt │ │ │ ├── network │ │ │ ├── clients │ │ │ │ ├── BetterTTVEmoteClient.kt │ │ │ │ ├── TwitchAuthenticationClient.kt │ │ │ │ ├── TwitchClient.kt │ │ │ │ ├── TwitchEmoteClient.kt │ │ │ │ ├── TwitchHomeClient.kt │ │ │ │ ├── TwitchModClient.kt │ │ │ │ ├── TwitchSearchClient.kt │ │ │ │ ├── TwitchStreamInfoClient.kt │ │ │ │ └── TwitchVODClient.kt │ │ │ ├── domain │ │ │ │ ├── BetterTTVEmotes.kt │ │ │ │ ├── NetworkMonitorRepo.kt │ │ │ │ ├── StreamInfoRepo.kt │ │ │ │ ├── TwitchAuthentication.kt │ │ │ │ ├── TwitchEmoteRepo.kt │ │ │ │ ├── TwitchEventSubscriptionWebSocket.kt │ │ │ │ ├── TwitchEventSubscriptions.kt │ │ │ │ ├── TwitchModRepo.kt │ │ │ │ ├── TwitchRepo.kt │ │ │ │ ├── TwitchSearch.kt │ │ │ │ ├── TwitchSocket.kt │ │ │ │ ├── TwitchStream.kt │ │ │ │ └── TwitchVODRepo.kt │ │ │ ├── interceptors │ │ │ │ ├── LiveNetworkMonitor.kt │ │ │ │ ├── LoggingInterceptor.kt │ │ │ │ ├── NetworkMonitor.kt │ │ │ │ ├── NetworkMonitorInterceptor.kt │ │ │ │ ├── NoNetworkException.kt │ │ │ │ ├── RetryInterceptor.kt │ │ │ │ └── responseCodeInterceptors │ │ │ │ │ ├── Authentication401Exception.kt │ │ │ │ │ └── Authentication401Interceptor.kt │ │ │ ├── models │ │ │ │ ├── emotes │ │ │ │ │ ├── BetterTTVEmotes.kt │ │ │ │ │ ├── TwitchChannelEmotes.kt │ │ │ │ │ └── TwitchGlobalEmotes.kt │ │ │ │ ├── twitchAuthentication │ │ │ │ │ ├── AuthenticatedUser.kt │ │ │ │ │ └── ValidatedUser.kt │ │ │ │ ├── twitchClient │ │ │ │ │ └── GetModChannels.kt │ │ │ │ ├── twitchRepo │ │ │ │ │ ├── FollowedLiveStreams.kt │ │ │ │ │ └── StreamData.kt │ │ │ │ ├── twitchStream │ │ │ │ │ ├── AutoModSettings.kt │ │ │ │ │ ├── BanUserResponse.kt │ │ │ │ │ ├── ChatSettings.kt │ │ │ │ │ └── UpdatedChatSettings.kt │ │ │ │ └── websockets │ │ │ │ │ └── DataClases.kt │ │ │ ├── repository │ │ │ │ ├── BetterTTVEmotesImpl.kt │ │ │ │ ├── DebugRetrofitResponse.kt │ │ │ │ ├── NetworkMonitorImpl.kt │ │ │ │ ├── StreamInfoRepoImpl.kt │ │ │ │ ├── TwitchAuthenticationImpl.kt │ │ │ │ ├── TwitchEmotImpl.kt │ │ │ │ ├── TwitchEventSub.kt │ │ │ │ ├── TwitchModRepoImpl.kt │ │ │ │ ├── TwitchRepoImpl.kt │ │ │ │ ├── TwitchSearchImpl.kt │ │ │ │ ├── TwitchStreamImpl.kt │ │ │ │ ├── TwitchVODRepoImpl.kt │ │ │ │ ├── models │ │ │ │ │ └── EmoteModels.kt │ │ │ │ └── util │ │ │ │ │ ├── AutoModMessageParsing.kt │ │ │ │ │ ├── ChatSettingsParsing.kt │ │ │ │ │ ├── EmoteParsing.kt │ │ │ │ │ ├── ExceptionHandling.kt │ │ │ │ │ └── ModActionParsing.kt │ │ │ └── websockets │ │ │ │ ├── ParsingEngine.kt │ │ │ │ ├── TwitchEventSubWebSocket.kt │ │ │ │ ├── TwitchWebSocket.kt │ │ │ │ └── models │ │ │ │ ├── ParsingEngineModels.kt │ │ │ │ └── TwitchWebSocketModels.kt │ │ │ ├── presentation │ │ │ ├── authentication │ │ │ │ ├── Constants.kt │ │ │ │ ├── CustomVRLoginPage.kt │ │ │ │ ├── logout │ │ │ │ │ ├── LogoutFragment.kt │ │ │ │ │ ├── LogoutViewModel.kt │ │ │ │ │ └── views │ │ │ │ │ │ └── MainComponent.kt │ │ │ │ ├── newUser │ │ │ │ │ ├── NewUserFragment.kt │ │ │ │ │ └── views │ │ │ │ │ │ └── NewUserComponent.kt │ │ │ │ └── views │ │ │ │ │ └── LogoutNewUserViews.kt │ │ │ ├── enhancedModView │ │ │ │ ├── Models.kt │ │ │ │ ├── viewModels │ │ │ │ │ ├── ModVersionThreeViewModel.kt │ │ │ │ │ ├── ModViewDragStateViewModel.kt │ │ │ │ │ └── ModViewViewModel.kt │ │ │ │ └── views │ │ │ │ │ ├── MainView.kt │ │ │ │ │ └── ModVersionThree.kt │ │ │ ├── home │ │ │ │ ├── HomeFragment.kt │ │ │ │ ├── HomeView.kt │ │ │ │ ├── HomeViewModel.kt │ │ │ │ ├── WorkerViewModel.kt │ │ │ │ ├── models │ │ │ │ │ └── HomeViewModelModels.kt │ │ │ │ ├── testing3DCode │ │ │ │ │ └── CubesNTriangles.kt │ │ │ │ ├── util │ │ │ │ │ └── ModeratorParsing.kt │ │ │ │ └── views │ │ │ │ │ ├── HomeComponents.kt │ │ │ │ │ ├── HomeStreamChatViews.kt │ │ │ │ │ └── ScaffoldComponents.kt │ │ │ ├── horizontalStreamOverlay │ │ │ │ └── OverlayStreamRow.kt │ │ │ ├── minigames │ │ │ │ ├── GameComposeUI.kt │ │ │ │ ├── MiniGameFragment.kt │ │ │ │ ├── dinoRun │ │ │ │ │ ├── ComposeDinoRunViews.kt │ │ │ │ │ ├── DinoRunJNI.kt │ │ │ │ │ ├── DinoRunRenderer.kt │ │ │ │ │ └── TriangleStrip.kt │ │ │ │ └── views │ │ │ │ │ └── MiniGameViews.kt │ │ │ ├── moderatedChannelsHome │ │ │ │ ├── ModChannelsFragment.kt │ │ │ │ └── views │ │ │ │ │ ├── MainComponents.kt │ │ │ │ │ └── MainView.kt │ │ │ ├── search │ │ │ │ ├── SearchFragment.kt │ │ │ │ ├── SearchViewModel.kt │ │ │ │ └── views │ │ │ │ │ ├── MainView.kt │ │ │ │ │ └── SearchView.kt │ │ │ ├── selfStreaming │ │ │ │ ├── EncoderWrapper.kt │ │ │ │ ├── RTMPNativeClient.kt │ │ │ │ ├── SelfStreamingFragment.kt │ │ │ │ ├── SoftwarePipeline.kt │ │ │ │ ├── clients │ │ │ │ │ ├── RtmpClient.kt │ │ │ │ │ └── StreamToTwitchClient.kt │ │ │ │ ├── domain │ │ │ │ │ ├── SelfStreaming.kt │ │ │ │ │ └── SelfStreamingSocket.kt │ │ │ │ ├── openGLSurfaces │ │ │ │ │ └── CustomGLSurfaceView.kt │ │ │ │ ├── repository │ │ │ │ │ └── SeflStreamingImpl.kt │ │ │ │ ├── surfaces │ │ │ │ │ └── AutoFitSurfaceView.kt │ │ │ │ ├── util │ │ │ │ │ └── URLParser.kt │ │ │ │ ├── viewModels │ │ │ │ │ └── SelfStreamingViewModel.kt │ │ │ │ ├── views │ │ │ │ │ └── SelfStreamingView.kt │ │ │ │ └── websocket │ │ │ │ │ ├── RTMPSTUFF.kt │ │ │ │ │ ├── RtmpSocket.kt │ │ │ │ │ ├── SelfStreamingWebsSocket.kt │ │ │ │ │ └── TcpTunneledSocket.kt │ │ │ ├── sharedViews │ │ │ │ ├── Buttons.kt │ │ │ │ ├── Dialogs.kt │ │ │ │ ├── Errors.kt │ │ │ │ ├── PullToRefresh.kt │ │ │ │ ├── Scaffolds.kt │ │ │ │ ├── SharedScopes.kt │ │ │ │ └── Switches.kt │ │ │ ├── stream │ │ │ │ ├── AutoModViewModel.kt │ │ │ │ ├── HorizontalChat.kt │ │ │ │ ├── StreamFragment.kt │ │ │ │ ├── StreamView.kt │ │ │ │ ├── StreamViewModel.kt │ │ │ │ ├── clearHorizontalChat │ │ │ │ │ └── ClearHorizontalChat.kt │ │ │ │ ├── customWebViews │ │ │ │ │ ├── HorizontalWebView.kt │ │ │ │ │ └── VerticalWebView.kt │ │ │ │ ├── models │ │ │ │ │ └── StreamViewModelModels.kt │ │ │ │ ├── util │ │ │ │ │ ├── LexicalAnalysis.kt │ │ │ │ │ ├── NetworkMonitoring.kt │ │ │ │ │ ├── TextParsing.kt │ │ │ │ │ ├── TokenCommand.kt │ │ │ │ │ ├── TokenMonitoring.kt │ │ │ │ │ └── domain │ │ │ │ │ │ ├── TextFieldParsing.kt │ │ │ │ │ │ ├── TokenCommandParsing.kt │ │ │ │ │ │ └── TokenParsing.kt │ │ │ │ └── views │ │ │ │ │ ├── BottomModal.kt │ │ │ │ │ ├── chat │ │ │ │ │ ├── ChatMessages.kt │ │ │ │ │ ├── DragState.kt │ │ │ │ │ ├── FullChatModView.kt │ │ │ │ │ ├── ImprovedChatUI.kt │ │ │ │ │ ├── SliderUI.kt │ │ │ │ │ └── chatSettings │ │ │ │ │ │ ├── ChatSettings.kt │ │ │ │ │ │ └── ChatSettingsViewModel.kt │ │ │ │ │ ├── dialogs │ │ │ │ │ ├── Dialogs.kt │ │ │ │ │ └── PollsPredictions.kt │ │ │ │ │ ├── horizontalLongPress │ │ │ │ │ └── LongPress.kt │ │ │ │ │ └── overlays │ │ │ │ │ ├── HorizontalOverlayViews.kt │ │ │ │ │ └── VerticalOverlayViews.kt │ │ │ └── streamInfo │ │ │ │ ├── StreamInfoViewModel.kt │ │ │ │ └── StreamInfoViews.kt │ │ │ ├── rtmp │ │ │ ├── BitrateChecker.kt │ │ │ ├── ConnectChecker.kt │ │ │ └── GenericStream.kt │ │ │ ├── services │ │ │ ├── BackgroundStreamService.kt │ │ │ ├── NetworkMonitorService.kt │ │ │ ├── NetworkMonitorViewModel.kt │ │ │ └── ScreenRecordingService.kt │ │ │ ├── ui │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ └── util │ │ │ ├── AndroidVersion.kt │ │ │ ├── CustomRetrofitCallAdapter.kt │ │ │ ├── Draggables.kt │ │ │ ├── LogWrap.kt │ │ │ ├── Response.kt │ │ │ ├── SortedEmoteMap.kt │ │ │ ├── Util.kt │ │ │ └── objectMothers │ │ │ ├── IndividualAutoModSettingsDataObjectMother.kt │ │ │ ├── LoggedInUserDataObjectMother.kt │ │ │ └── TwitchUserDataObjectMother.kt │ └── res │ │ ├── drawable │ │ ├── alert_24.xml │ │ ├── autorenew_24.xml │ │ ├── back_arrow.xml │ │ ├── ban_24.xml │ │ ├── baseline_announcement_24.xml │ │ ├── baseline_backspace_24.xml │ │ ├── baseline_category_24.xml │ │ ├── baseline_check_24.xml │ │ ├── baseline_close_24.xml │ │ ├── baseline_fast_forward_24.xml │ │ ├── baseline_fast_rewind_24.xml │ │ ├── baseline_hourglass_empty_24.xml │ │ ├── baseline_keyboard_arrow_up_24.xml │ │ ├── baseline_location_on_24.xml │ │ ├── baseline_new_releases_24.xml │ │ ├── baseline_pause_24.xml │ │ ├── baseline_play_arrow_24.xml │ │ ├── baseline_privacy_policy.xml │ │ ├── baseline_question_mark_24.xml │ │ ├── baseline_rocket_launch_24.xml │ │ ├── baseline_settings_24.xml │ │ ├── baseline_star_outline.xml │ │ ├── channel_emotes_24.xml │ │ ├── clear_chat_alt_24.xml │ │ ├── clear_chat_on_24.xml │ │ ├── delete_outline_24.xml │ │ ├── edit_24.xml │ │ ├── emote_face_24.xml │ │ ├── error_outline_24.xml │ │ ├── favorite_24.xml │ │ ├── flip_camera_android_24.xml │ │ ├── gift.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_send.xml │ │ ├── keyboard_24.xml │ │ ├── keyboard_arrow_down_24.xml │ │ ├── lock_24.xml │ │ ├── lock_open_24.xml │ │ ├── menu_open_24.xml │ │ ├── mod_view_24.xml │ │ ├── moderator_secondary_color.xml │ │ ├── moderator_white.xml │ │ ├── person_outline_24.xml │ │ ├── push_pin_24.xml │ │ ├── shared_24.xml │ │ ├── sparkle_awesome_24.xml │ │ ├── time_out_24.xml │ │ ├── videogame_asset.xml │ │ ├── visibility_24.xml │ │ └── world_emotes_24.xml │ │ ├── layout-land │ │ └── fragment_stream.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── camera_ndk_native_activity.xml │ │ ├── fragment_home.xml │ │ ├── fragment_logout.xml │ │ ├── fragment_mini_game.xml │ │ ├── fragment_mod_channels.xml │ │ ├── fragment_new_user.xml │ │ ├── fragment_search.xml │ │ ├── fragment_self_streaming.xml │ │ ├── fragment_stream.xml │ │ └── new_testin_stream_layout.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ ├── verify_links_last_arrow.png │ │ ├── verify_links_second_arrow.png │ │ └── verify_links_start_arrow.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ └── nav_graph.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── splashScreen.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ ├── data_extraction_rules.xml │ │ ├── file_paths.xml │ │ └── method.xml │ └── test │ └── java │ └── com │ └── example │ └── clicker │ ├── ExampleUnitTest.kt │ ├── ParsingEngineTest.kt │ ├── network │ └── repository │ │ ├── TwitchAuthenticationImplTest.kt │ │ ├── TwitchEmoteImplTest.kt │ │ ├── TwitchRepoImplTest.kt │ │ ├── TwitchStreamImplTest.kt │ │ ├── util │ │ ├── EmoteParsingTest.kt │ │ ├── SortedEmoteMapTest.kt │ │ └── TwitchClientBuilder.kt │ │ └── websockets │ │ └── TwitchEventSubWebSocketTest.kt │ ├── presentation │ ├── HomeViewModelTest.kt │ ├── authentication │ │ └── AuthenticationViewModelTest.kt │ ├── stream │ │ ├── StreamViewModelTest.kt │ │ └── util │ │ │ ├── ScannerTest.kt │ │ │ ├── TextParsingTest.kt │ │ │ └── TokenCommandTest.kt │ └── util │ │ └── FakeHomeViewModelDependencies.kt │ └── utility │ └── UtilityTests.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── macrobenchmark ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── buildinginpublic │ └── macrobenchmark │ └── ExampleStartupBenchmark.kt └── settings.gradle /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Smartphone (please complete the following information):** 27 | - Device: [e.g. iPhone6] 28 | - OS: [e.g. iOS8.1] 29 | - Browser [e.g. stock browser, safari] 30 | - Version [e.g. 22] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/engineering-issue-.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Engineering issue ' 3 | about: An engineering task 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | #Proposed change 11 | 12 | #Why is this important 13 | 14 | #Additional context 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Related Issue 2 | - enter related issue 3 | 4 | 5 | # Proposed changes 6 | - list proposed changes 7 | 8 | 9 | # Additional context(optional) 10 | - any additional information neccessary 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | 17 | app/release/ 18 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Clicker -------------------------------------------------------------------------------- /.idea/androidTestResultsUserPreferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 36 | 37 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 41 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | -assumenosideeffects class android.util.Log { 24 | public static boolean isLoggable(java.lang.String, int); 25 | public static int v(...); 26 | public static int i(...); 27 | public static int w(...); 28 | public static int d(...); 29 | public static int e(...); 30 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/clicker/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.* 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.example.clicker", appContext.packageName) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/cpp/camera_manager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Tristan on 2024-11-08. 3 | // 4 | 5 | #include "camera_manager.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #define LOG_TAG "streamLogging" 24 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 25 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 26 | 27 | 28 | 29 | 30 | 31 | //NDKCamera::NDKCamera() 32 | // :cameraMgr_(nullptr), 33 | // activeCameraId_(""){ 34 | // 35 | // cameras_.clear(); 36 | // cameraMgr_ = ACameraManager_create(); 37 | // 38 | //} 39 | // 40 | //NDKCamera::~NDKCamera() { 41 | // cameras_.clear(); 42 | // if (cameraMgr_) { 43 | // //CALL_MGR(unregisterAvailabilityCallback(cameraMgr_, GetManagerListener())); 44 | // ACameraManager_delete(cameraMgr_); 45 | // cameraMgr_ = nullptr; 46 | // } 47 | //} 48 | 49 | /** 50 | * GetSensorOrientation() 51 | * Retrieve current sensor orientation regarding to the phone device 52 | * orientation 53 | * SensorOrientation is NOT settable. 54 | */ 55 | //bool NDKCamera::GetSensorOrientation(int32_t* facing, int32_t* angle) { 56 | // if (!cameraMgr_) { 57 | // return false; 58 | // } 59 | // 60 | // ACameraMetadata* metadataObj; 61 | // ACameraMetadata_const_entry face, orientation; 62 | // CALL_MGR(getCameraCharacteristics(cameraMgr_, activeCameraId_.c_str(), 63 | // &metadataObj)); 64 | // 65 | // cameraFacing_ = static_cast(face.data.u8[0]); 66 | // 67 | // 68 | // 69 | // LOGI("Current SENSOR_ORIENTATION-----> %8d", orientation.data.i32[0]); 70 | // 71 | //// 72 | //// ACameraMetadata_free(metadataObj); 73 | //// cameraOrientation_ = orientation.data.i32[0]; 74 | //// 75 | //// if (facing) *facing = cameraFacing_; 76 | //// if (angle) *angle = cameraOrientation_; 77 | // return true; 78 | //} 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /app/src/main/cpp/camera_manager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by 14035 on 2024-11-08. 3 | // 4 | 5 | #ifndef CLICKER_CAMERA_MANAGER_H 6 | #define CLICKER_CAMERA_MANAGER_H 7 | 8 | 9 | #endif //CLICKER_CAMERA_MANAGER_H 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/cpp/camera_stream.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Tristan on 2024-11-04. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "camera_manager.h" 17 | #include "camera_engine.h" 18 | #include 19 | 20 | 21 | 22 | 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define LOG_TAG "streamLogging" 33 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 34 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 35 | #define ASSERT(cond, fmt, ...) //takes a condition to be evaluated, a string format and any extra variables 36 | 37 | 38 | 39 | 40 | 41 | 42 | /** 43 | * Called when the NativeActivity is created 44 | * */ 45 | extern "C" void android_main(struct android_app* state) { 46 | 47 | } 48 | 49 | 50 | extern "C" 51 | JNIEXPORT void JNICALL 52 | Java_com_example_clicker_cameraNDK_CameraNDKNativeActivity_notifyCameraPermission(JNIEnv *env,jobject thiz,jboolean granted) { 53 | 54 | 55 | LOGI("PERMISSION GRANTED"); 56 | } 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/cpp/dino_run/dino_run.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Tristan on 2025-03-11. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "dino_run.h" 13 | #include "dino_run_setup.h" 14 | #include "dino_run_actions.h" 15 | 16 | #define LOG_TAG "DINORUN" 17 | #define LOGI(TAG, ...) ((void)__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)) 18 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 19 | 20 | 21 | 22 | //todo: these two should be part of a larger GAME-OBJECT 23 | TransformShader* shaders = new TransformShader(); 24 | Actions* actions = new Actions(shaders); 25 | 26 | 27 | 28 | 29 | extern "C" 30 | JNIEXPORT void JNICALL 31 | Java_com_example_clicker_presentation_minigames_dinoRun_DinoRunJNI_init(JNIEnv *env, jobject thiz,jint width, jint height) { 32 | LOGI("APSECTrATIOtESTINGaGAIN", "INIT"); 33 | float aspectRatio = (float)width / (float)height; 34 | LOGI("APSECTrATIOtESTINGaGAIN", "ratio -->%f",aspectRatio); 35 | 36 | shaders->setupGraphics(width, height); 37 | 38 | shaders->addToVector(aspectRatio); 39 | 40 | } 41 | 42 | 43 | 44 | 45 | 46 | 47 | //need a function called set to call `renderFrame();` 48 | extern "C" 49 | JNIEXPORT void JNICALL 50 | Java_com_example_clicker_presentation_minigames_dinoRun_DinoRunJNI_step(JNIEnv *env, jobject thiz) { 51 | std::vector& vertices = shaders->getSquareVertices(); 52 | actions->jump(shaders->getSquareVertices()); 53 | LOGI("JUMPINGCHECK", "STEP!!!!!!!!!"); 54 | 55 | switch(actions->getGameValue()){ 56 | case init : break; 57 | case start: actions->moveSecondSquare(shaders->getSquareVertices(),env); break; 58 | case stop :break; 59 | } 60 | 61 | shaders->renderFrame(); 62 | } 63 | extern "C" 64 | JNIEXPORT void JNICALL 65 | Java_com_example_clicker_presentation_minigames_dinoRun_DinoRunJNI_jump(JNIEnv *env,jobject thiz) { 66 | actions->setStartJumpTrue(); 67 | LOGI("JUMPINGCHECK", "JUMP"); 68 | switch(actions->getGameValue()){ 69 | case init : actions->removeStartUI(env); break; 70 | case start: break; 71 | case stop : actions->removeStartUI(env); break; 72 | } 73 | 74 | 75 | 76 | } 77 | 78 | 79 | -------------------------------------------------------------------------------- /app/src/main/cpp/dino_run/dino_run.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Tristan on 2025-03-15. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #ifndef CLICKER_DINO_RUN_H 14 | #define CLICKER_DINO_RUN_H 15 | 16 | #endif //CLICKER_DINO_RUN_H 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/cpp/dino_run/dino_run_actions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Tristan on 2025-03-19. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifndef CLICKER_DINO_RUN_ACTIONS_H 15 | #define CLICKER_DINO_RUN_ACTIONS_H 16 | 17 | #endif //CLICKER_DINO_RUN_ACTIONS_H 18 | 19 | enum GameStatus { init, start, stop }; 20 | 21 | 22 | 23 | class Actions { 24 | private: 25 | 26 | bool startJump; 27 | bool hitTop; 28 | float velocity; 29 | void resetSquare(std::vector& vertices); 30 | float secondSquareMovementSpeed = -0.02f; 31 | int successfulJumps = 0; 32 | GameStatus gameValue = init; 33 | 34 | // Add a pointer to TransformShader 35 | TransformShader* transformShader; 36 | 37 | public: 38 | Actions(TransformShader* shader); 39 | void jump(std::vector& vertices); 40 | void setStartJumpTrue(); 41 | void moveSecondSquare(std::vector& vertices,JNIEnv *env); 42 | void updateTextFromNative(const char *message,JNIEnv *env); 43 | void showGameOverUI(JNIEnv *env); 44 | void resetSecondSquare(std::vector& vertices); 45 | void showSpeedIncrease(JNIEnv *env); 46 | void removeStartUI(JNIEnv *env); 47 | 48 | //todo: this needs to be looked into 49 | GameStatus getGameValue() { 50 | return gameValue; 51 | } 52 | void setShowCoin(bool value){ 53 | transformShader->setShowCoin(value); 54 | } 55 | bool getShowCoin(){ 56 | return transformShader->getShowCoin(); 57 | } 58 | void resetCoin(){ 59 | transformShader->resetCircle(); 60 | } 61 | 62 | 63 | 64 | }; -------------------------------------------------------------------------------- /app/src/main/cpp/dino_run/dino_run_setup.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Tristan on 2025-03-17. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifndef CLICKER_DINO_RUN_SETUP_H 16 | #define CLICKER_DINO_RUN_SETUP_H 17 | 18 | #endif //CLICKER_DINO_RUN_SETUP_H 19 | 20 | 21 | 22 | /** 23 | * basic TransformShader 24 | */ 25 | class TransformShader { 26 | 27 | private: 28 | std::vector m_squareVertices = { 29 | -0.800f, -0.0375f, -0.650f, -0.0375f, -0.650f, 0.0375f, 30 | -0.800f, -0.0375f, -0.650f, 0.0375f, -0.800f, 0.0375f, 31 | 0.85f, -0.0375f, 1.0f, -0.0375f, 1.0f, 0.0375f, 32 | 0.85f, -0.0375f, 1.0f, 0.0375f, 0.85f, 0.0375f 33 | }; 34 | GLuint simpleTriangleProgram; 35 | GLuint vPosition; 36 | bool showCoin = false; 37 | int width = 0.0f; 38 | int height = 0.0f; 39 | 40 | 41 | 42 | public: 43 | TransformShader(); 44 | ~TransformShader(); 45 | std::string m_glVertexShader; 46 | std::string m_glFragmentShader; 47 | GLuint createProgram(std::string vertexSource, std::string fragmentSource); 48 | GLuint loadShader(GLenum shaderType, std::string shaderSource); 49 | std::vector& getSquareVertices() { 50 | return m_squareVertices; // Returns a reference 51 | } 52 | bool setupGraphics(int w, int h); 53 | void renderFrame(); 54 | void addToVector(float aspectRatio); 55 | void setShowCoin(bool value){ 56 | showCoin =value; 57 | } 58 | bool getShowCoin(){ 59 | return showCoin; 60 | } 61 | void resetCircle(){ 62 | float aspectRatio = (float)width / (float)height; 63 | addToVector(aspectRatio); 64 | } 65 | 66 | 67 | }; 68 | -------------------------------------------------------------------------------- /app/src/main/cpp/my_class.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by 14035 on 2024-09-24. 3 | // 4 | 5 | // my_class.cpp 6 | #include "my_class.h" 7 | #include 8 | #include 9 | #include 10 | 11 | #define LOG_TAG "libgl2jni" 12 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 13 | //using namespace N; 14 | // 15 | //namespace N { 16 | // void my_class::do_something() { 17 | // const int size = 1024; 18 | // LOGI("-----------THIS IS DONE THROUGH THE TESTING OF THE HEADER FILES -----------> %d,", size); 19 | // } 20 | //} 21 | void do_something() { 22 | const int size = 1024; 23 | LOGI("-----------THIS IS DONE THROUGH THE TESTING OF THE HEADER FILES -----------> %d,", size); 24 | } -------------------------------------------------------------------------------- /app/src/main/cpp/my_class.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Tristan on 2024-09-24. 3 | // 4 | 5 | 6 | #ifndef CLICKER_MY_CLASS_H 7 | #define CLICKER_MY_CLASS_H 8 | 9 | 10 | //namespace N 11 | //{ 12 | // class my_class 13 | // { 14 | // public: 15 | // void do_something(); 16 | // }; 17 | // 18 | //} 19 | #endif //CLICKER_MY_CLASS_H 20 | void do_something(); 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/cpp/ping_pong/ping_pong_movement.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by 14035 on 2025-03-11. 3 | // 4 | 5 | #ifndef CLICKER_PING_PONG_MOVEMENT_H 6 | #define CLICKER_PING_PONG_MOVEMENT_H 7 | 8 | #endif //CLICKER_PING_PONG_MOVEMENT_H 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | 19 | 20 | void moveTopPaddleXAxis(GLfloat *vertices, GLfloat x); 21 | void moveBottomPaddleXAxis(GLfloat *vertices, GLfloat x); 22 | void resetBall(GLfloat *vertices); 23 | void moveBall(GLfloat *vertices, GLfloat dy); -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/broadcastReceivers/ShutDownBroadcastReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.broadcastReceivers 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.util.Log 7 | import com.example.clicker.services.BackgroundStreamService 8 | import com.example.clicker.services.ServiceActions 9 | 10 | /** 11 | * - **ShutDownBroadcastReceiver** is a [BroadcastReceiver] object meant to capture the action from the 12 | * notification and send a [END][com.example.clicker.services.BackgroundStreamService.Actions.END] action to 13 | * the [BackgroundStreamService] object 14 | * 15 | * */ 16 | class ShutDownBroadcastReceiver : BroadcastReceiver() { 17 | 18 | 19 | override fun onReceive(p0: Context?, p1: Intent?) { 20 | 21 | Log.d("ShutDownBroadcastReceiver","SHUT IT DOWN!!!!!!!!!!") 22 | p0?.let{context -> 23 | val startIntent = Intent(context, BackgroundStreamService::class.java) 24 | startIntent.action = BackgroundStreamService.Actions.END.toString() 25 | context.startService(startIntent) 26 | } 27 | 28 | } 29 | 30 | } 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/cameraNDK/CameraNDKNativeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.cameraNDK 2 | 3 | 4 | import android.Manifest 5 | import android.app.NativeActivity 6 | import android.content.pm.PackageManager 7 | import android.hardware.display.DisplayManager 8 | import android.os.Bundle 9 | import android.os.Handler 10 | import android.os.Looper 11 | import android.util.Log 12 | import android.view.Display 13 | import android.view.WindowManager 14 | import android.widget.RelativeLayout 15 | import androidx.core.app.ActivityCompat 16 | import androidx.core.content.ContextCompat 17 | import androidx.core.content.getSystemService 18 | 19 | 20 | class CameraNDKNativeActivity : NativeActivity() { 21 | private val PERMISSION_REQUEST_CODE_CAMERA = 1 22 | 23 | 24 | 25 | 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | 30 | //this.setContentView(R.layout.camera_ndk_native_activity) 31 | 32 | val mainLayout = RelativeLayout(this) 33 | 34 | this.setContentView(mainLayout) 35 | //checkCameraPermission() 36 | // Delay notifyCameraPermission by 1 second (1000 milliseconds) 37 | //so I think this only needs to be called if we do not have the appropriate permissions 38 | //so we can fix this later, to properly handle the permissions 39 | // Handler(Looper.getMainLooper()).postDelayed({ 40 | // notifyCameraPermission(true) 41 | // }, 1000) 42 | } 43 | 44 | private fun checkCameraPermission() { 45 | Log.d("PermissionCheckingNative","CHECKING") 46 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { 47 | // Request camera permission 48 | Log.d("PermissionCheckingNative","not GRANTED") 49 | ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), PERMISSION_REQUEST_CODE_CAMERA) 50 | } else { 51 | // Permission already granted 52 | 53 | Log.d("PermissionCheckingNative","ALREADY GRANTED") 54 | notifyCameraPermission(true) 55 | } 56 | } 57 | 58 | // get current rotation method 59 | fun getRotationDegree(): Int { 60 | val defaultDisplay = getSystemService()?.getDisplay(Display.DEFAULT_DISPLAY)?.rotation?:0 61 | return 90 * defaultDisplay 62 | } 63 | 64 | 65 | external fun notifyCameraPermission(granted: Boolean) 66 | 67 | init{ 68 | System.loadLibrary("camera_stream"); 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/composeMetricsInitScript.gradle: -------------------------------------------------------------------------------- 1 | 2 | // Define a global flag in the init script 3 | ext.initScriptRun = false 4 | 5 | // Hook into the projectsEvaluated phase to apply configurations after all projects are evaluated 6 | //gradle.projectsEvaluated { 7 | // if (!ext.initScriptRun) { 8 | // ext.initScriptRun = true 9 | // 10 | // // Iterate over all projects 11 | // gradle.rootProject.allprojects { project -> 12 | // if (project.name == 'app') { 13 | // project.plugins.withId('org.jetbrains.kotlin.jvm') { 14 | // project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { task -> 15 | // if (project.findProperty("composeCompilerReports") == "true") { 16 | // task.kotlinOptions { 17 | // freeCompilerArgs += [ 18 | // "-P", 19 | // "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + 20 | // project.buildDir.absolutePath + "/compose_metrics" 21 | // ] 22 | // freeCompilerArgs += [ 23 | // "-P", 24 | // "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + 25 | // project.buildDir.absolutePath + "/compose_metrics" 26 | // ] 27 | // } 28 | // } 29 | // } 30 | // } 31 | // } 32 | // } 33 | // } 34 | //} 35 | //gradlew assembleRelease --init-script .\app\src\main\java\com\example\clicker\composeMetricsInitScript.gradle -PcomposeCompilerReports=true 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/data/TokenValidationWorker.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.data 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import androidx.lifecycle.LiveData 6 | import androidx.work.Data 7 | import androidx.work.ExistingPeriodicWorkPolicy 8 | import androidx.work.PeriodicWorkRequestBuilder 9 | import androidx.work.WorkInfo 10 | import androidx.work.WorkManager 11 | import com.example.clicker.data.workManager.OAuthTokeValidationWorker 12 | import com.example.clicker.domain.TwitchTokenValidationWorker 13 | import java.util.concurrent.TimeUnit 14 | import javax.inject.Inject 15 | 16 | class TokenValidationWorker @Inject constructor( 17 | private val context: Context 18 | ): TwitchTokenValidationWorker { 19 | 20 | private val uniqueId: String = "Validating" 21 | 22 | private val workManager = WorkManager.getInstance(context) 23 | 24 | override fun enqueueRequest(oAuthToken: String): LiveData { 25 | val workRequest = PeriodicWorkRequestBuilder( 26 | 1, // repeating interval 27 | TimeUnit.HOURS 28 | ) // todo:MAKE THIS PERIODIC 29 | val data = Data.Builder() 30 | data.putString("token", oAuthToken) 31 | workRequest.setInputData(data.build()) 32 | 33 | val builtWorkRequest = workRequest.build() 34 | 35 | // workManager.enqueueUniqueWork(uniqueId, ExistingWorkPolicy.KEEP,builtWorkRequest) 36 | workManager.enqueueUniquePeriodicWork( 37 | uniqueId, 38 | ExistingPeriodicWorkPolicy.KEEP, 39 | builtWorkRequest 40 | ) 41 | Log.d("ENQUEDID", "IT DO BE LIKE THAT SOMETIMES") 42 | return workManager.getWorkInfoByIdLiveData(builtWorkRequest.id) 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/data/domains/PinnedItemInter.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.data.domains 2 | 3 | import androidx.annotation.WorkerThread 4 | import com.example.clicker.data.room.PinnedItem 5 | import com.example.clicker.network.clients.TopGame 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | 9 | /** 10 | * PinnedItemInter is the interface that acts as the API for all the methods needed to interact with internal storage system 11 | * 12 | * @property getAllPinnedItems a [Flow] containing a [MutableList] of [PinnedItem] objects. Meant to represent clicked 13 | * category 14 | * 15 | * @property insertPinnedItem a function, when call with a [PinnedItem] object, is meant to store said object in the internal system 16 | * @property deletePinnedItem a function, when call with a [PinnedItem] object, is meant to remove said object in the internal system 17 | * */ 18 | interface PinnedItemInter { 19 | 20 | val getAllPinnedItems: Flow> 21 | 22 | suspend fun insertPinnedItem(pinnedItem: PinnedItem) 23 | 24 | fun deletePinnedItem( pinnedItem: PinnedItem) 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/data/repos/PinnedItemRepo.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.data.repos 2 | 3 | import androidx.annotation.WorkerThread 4 | import androidx.room.Delete 5 | import androidx.room.Insert 6 | import androidx.room.OnConflictStrategy 7 | import androidx.room.Query 8 | import com.example.clicker.data.domains.PinnedItemInter 9 | import com.example.clicker.data.room.PinnedItem 10 | import com.example.clicker.data.room.PinnedItemsDAO 11 | import com.example.clicker.network.clients.TopGame 12 | import kotlinx.coroutines.flow.Flow 13 | import javax.inject.Inject 14 | 15 | /** 16 | * - **PinnedItemRepo** is the repository layer for interacting with the underlying room database. This class is meant to 17 | * help users interact with the room database and store [PinnedItem] objects 18 | * 19 | * @param pinnedItemDao injected [PinnedItemsDAO] that is the room database. Users will interact with the in order to store and 20 | * remove the [PinnedItem] objects 21 | * 22 | * */ 23 | class PinnedItemRepo @Inject constructor( 24 | private val pinnedItemDao: PinnedItemsDAO 25 | ): PinnedItemInter { 26 | 27 | 28 | 29 | //Room executes all the queries on a separate thread 30 | // Observed Flow will notify the observer when the data has changed 31 | override val getAllPinnedItems: Flow> = pinnedItemDao.getAllPinnedItems(); 32 | 33 | 34 | @Suppress("RedundantSuspendModifier") 35 | @WorkerThread 36 | override suspend fun insertPinnedItem(pinnedItem: PinnedItem) { 37 | //suspend tells the compiler the this needs to be called from a coroutine or another suspending function 38 | pinnedItemDao.insertPinnedItem(pinnedItem) 39 | } 40 | 41 | 42 | @WorkerThread 43 | override fun deletePinnedItem(pinnedItem: PinnedItem){ 44 | pinnedItemDao.deletePinnedItem(pinnedItem) 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/data/room/DataAccessObjects.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.data.room 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Delete 5 | import androidx.room.Insert 6 | import androidx.room.OnConflictStrategy 7 | import androidx.room.Query 8 | import com.example.clicker.network.clients.TopGame 9 | import kotlinx.coroutines.flow.Flow 10 | 11 | 12 | /** 13 | * - **PinnedItemsDAO** is the interface that acts as the main Data Access Object for the underlying room database 14 | * - you can read more about data access objects, [HERE](https://developer.android.com/training/data-storage/room/accessing-data) 15 | * 16 | * @property getAllPinnedItems a [Flow] containing a [MutableList] of [PinnedItem] objects. Meant to represent clicked 17 | * category 18 | * 19 | * @property insertPinnedItem a function, when call with a [PinnedItem] object, is meant to store said object in the internal system 20 | * @property deletePinnedItem a function, when call with a [PinnedItem] object, is meant to remove said object in the internal system 21 | * */ 22 | @Dao 23 | interface PinnedItemsDAO { 24 | @Query("SELECT * FROM pinned") 25 | fun getAllPinnedItems(): Flow> 26 | 27 | @Insert(onConflict = OnConflictStrategy.REPLACE) 28 | fun insertPinnedItem( pinnedItem: PinnedItem) 29 | 30 | @Delete 31 | fun deletePinnedItem( pinnedItem: PinnedItem) 32 | 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/data/room/DataEntities.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.data.room 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import com.example.clicker.network.clients.TopGame 7 | /** 8 | * - **PinnedItem** is a data class that is used to be the base for a database [Entity] object. You 9 | * can read more about Entity classes, [HERE](https://developer.android.com/training/data-storage/room/defining-data) 10 | * 11 | * @param uid a Int that is used by the internal room database to uniquley identify the class 12 | * @param firstName A String used to represent the name of this pinned item 13 | * @param boxArtUrl a String used to hold the URL of the image that will be shown to the suer 14 | * @param igdbId a String used to identify this object inside of Twitch's system 15 | * @param clicked a Boolean used to determine if the user has double clicked this object or not 16 | * 17 | * */ 18 | @Entity(tableName = "pinned") 19 | data class PinnedItem( 20 | @PrimaryKey val uid: Int, 21 | @ColumnInfo(name = "name") val firstName: String, 22 | @ColumnInfo(name = "box_art_url") val boxArtUrl: String, 23 | @ColumnInfo(name = "igdb_id") val igdbId: String, 24 | @ColumnInfo(name = "clicked") val clicked: Boolean 25 | ) 26 | 27 | /** 28 | * - **toTopGame** is an extension utility function used to turn [PinnedItem] objects into [TopGame] objects 29 | * */ 30 | fun PinnedItem.toTopGame(): TopGame { 31 | return TopGame( 32 | id = uid.toString(), 33 | name = firstName, 34 | box_art_url = boxArtUrl, 35 | igdb_id = igdbId, 36 | clicked = clicked 37 | ) 38 | } 39 | 40 | /** 41 | * - **toTopGame** is an extension utility function used to turn [TopGame] objects into [PinnedItem] objects 42 | * */ 43 | fun TopGame.toPinnedItem(): PinnedItem { 44 | return PinnedItem( 45 | uid = id.toInt(), 46 | firstName = name, 47 | boxArtUrl = box_art_url, 48 | igdbId = igdb_id, 49 | clicked = clicked 50 | ) 51 | } 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/data/room/PinnedItemsDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.data.room 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import androidx.room.TypeConverters 8 | 9 | 10 | /** 11 | * - **PinnedItemRoomDatabase** acts as the actual database. It extends [RoomDatabase] class to tell Android this class is to be 12 | * treated as underlying database 13 | * 14 | * @property pinnedItemsDAO a function that when called will give an API to interact with the database. This function is abstract 15 | * but it gets instantiated upon creation of this class do to an internal companion object 16 | * 17 | * */ 18 | @Database(entities = [PinnedItem::class], version = 1, exportSchema = false) 19 | public abstract class PinnedItemRoomDatabase : RoomDatabase(){ 20 | 21 | //For each DAO class that is associated with the database, 22 | // the database class must define an abstract method that has zero arguments and returns an instance of the DAO class. 23 | abstract fun pinnedItemsDAO(): PinnedItemsDAO 24 | 25 | companion object{ 26 | // Singleton prevents multiple instances of database opening at the 27 | // same time. 28 | @Volatile 29 | private var INSTANCE: PinnedItemRoomDatabase? = null //companion property 30 | 31 | fun getDatabase(context: Context): PinnedItemRoomDatabase { 32 | // if the INSTANCE is not null, then return it, 33 | // if it is, then create the database 34 | return INSTANCE ?: synchronized(this){ 35 | 36 | val instance = Room.databaseBuilder( 37 | context.applicationContext, 38 | PinnedItemRoomDatabase::class.java, 39 | "pinned_item_database" 40 | ).build() 41 | INSTANCE = instance 42 | instance 43 | } 44 | } 45 | 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/di/HiltApplication.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.di 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.util.Log 6 | import androidx.work.Configuration 7 | import androidx.work.ListenableWorker 8 | import androidx.work.WorkerFactory 9 | import androidx.work.WorkerParameters 10 | import com.example.clicker.data.workManager.OAuthTokeValidationWorker 11 | import com.example.clicker.network.domain.TwitchAuthentication 12 | import dagger.hilt.android.HiltAndroidApp 13 | import javax.inject.Inject 14 | 15 | @HiltAndroidApp 16 | class HiltApplication : Application(), Configuration.Provider { 17 | 18 | @Inject 19 | lateinit var workerFactory: CustomWorkerFactory 20 | override fun getWorkManagerConfiguration(): Configuration { 21 | return Configuration.Builder() 22 | .setMinimumLoggingLevel(Log.DEBUG) 23 | .setWorkerFactory(workerFactory) 24 | .build() 25 | } 26 | } 27 | 28 | class CustomWorkerFactory @Inject constructor( 29 | private val twitchRepoImpl: TwitchAuthentication 30 | ) : WorkerFactory() { 31 | override fun createWorker( 32 | appContext: Context, 33 | workerClassName: String, 34 | workerParameters: WorkerParameters 35 | ): ListenableWorker? = OAuthTokeValidationWorker(appContext, workerParameters, twitchRepoImpl) 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/di/modules/ViewModelModule.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.di.modules 2 | 3 | import android.content.Context 4 | import com.example.clicker.data.TokenValidationWorker 5 | import com.example.clicker.domain.TwitchTokenValidationWorker 6 | import dagger.Module 7 | import dagger.Provides 8 | import dagger.hilt.InstallIn 9 | import dagger.hilt.android.components.ViewModelComponent 10 | import dagger.hilt.android.qualifiers.ApplicationContext 11 | 12 | // As a dependency of another class. 13 | @Module 14 | @InstallIn(ViewModelComponent::class) 15 | object ViewModelModule { 16 | 17 | // @Binds 18 | // abstract fun bindsTwitchRepo( 19 | // twitchRepoImpl: TwitchRepoImpl 20 | // ):TwitchRepo 21 | 22 | // @Provides 23 | // fun provideTwitchRepo(twitchRepoImpl: TwitchRepoImpl): TwitchRepo { 24 | // return twitchRepoImpl 25 | // } 26 | 27 | @Provides 28 | fun provideTokenValidationWorker( 29 | @ApplicationContext appContext: Context 30 | ): TwitchTokenValidationWorker { 31 | return TokenValidationWorker(appContext) 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/domain/TwitchTokenValidationWorker.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.domain 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.work.WorkInfo 5 | 6 | interface TwitchTokenValidationWorker { 7 | fun enqueueRequest(oAuthToken: String): LiveData 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/nativeLibraryClasses/CameraStreamNDK.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.nativeLibraryClasses 2 | 3 | class CameraStreamNDK { 4 | 5 | init{ 6 | System.loadLibrary("camera_stream"); 7 | } 8 | 9 | 10 | 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/nativeLibraryClasses/NativeLoading.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.nativeLibraryClasses 2 | 3 | import android.view.Surface 4 | 5 | object NativeLoading { 6 | 7 | init { 8 | System.loadLibrary("gl_code"); 9 | System.loadLibrary("my_class"); 10 | } 11 | 12 | 13 | /** 14 | * @param width the current view width 15 | * @param height the current view height 16 | */ 17 | external fun init(width: Int, height: Int) 18 | external fun step() 19 | } 20 | 21 | object NativeSquareLoading{ 22 | 23 | init{ 24 | System.loadLibrary("square_code"); 25 | } 26 | 27 | 28 | /** 29 | * @param width the current view width 30 | * @param height the current view height 31 | */ 32 | external fun init(width: Int, height: Int) 33 | external fun step() 34 | 35 | external fun click() 36 | 37 | } 38 | 39 | 40 | object NativeBlurEffect{ 41 | 42 | init{ 43 | System.loadLibrary("blur_effect"); 44 | } 45 | 46 | 47 | /** 48 | * @param width the current view width 49 | * @param height the current view height 50 | */ 51 | external fun init(width: Int, height: Int) 52 | external fun step() 53 | 54 | 55 | } 56 | 57 | /** 58 | * THIS IS THE CUBE CODE 59 | * */ 60 | object NativeCube{ 61 | 62 | init{ 63 | System.loadLibrary("cube_code"); 64 | } 65 | 66 | 67 | /** 68 | * @param width the current view width 69 | * @param height the current view height 70 | */ 71 | external fun init(width: Int, height: Int) 72 | external fun step() 73 | 74 | 75 | 76 | 77 | // Initialize OpenGL with the shared surface 78 | 79 | 80 | 81 | } 82 | 83 | object VideoEncoder{ 84 | 85 | init{ 86 | System.loadLibrary("video_encoder"); 87 | } 88 | 89 | 90 | /** 91 | * @param width the current view width 92 | * @param height the current view height 93 | */ 94 | external fun init(width: Int, height: Int) 95 | external fun step() 96 | 97 | 98 | 99 | } 100 | 101 | object NativeUnderstandTriangle{ 102 | 103 | init{ 104 | System.loadLibrary("understand_triangle"); 105 | } 106 | 107 | 108 | /** 109 | * @param width the current view width 110 | * @param height the current view height 111 | */ 112 | external fun init(width: Int, height: Int) 113 | external fun step() 114 | 115 | 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/clients/BetterTTVEmoteClient.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.clients 2 | 3 | import com.example.clicker.network.models.emotes.BetterTTVChannelEmotes 4 | import com.example.clicker.network.models.emotes.IndivBetterTTVEmote 5 | import retrofit2.Response 6 | import retrofit2.http.GET 7 | import retrofit2.http.Path 8 | 9 | /** 10 | * - **BetterTTVEmoteClient** is an interface that will act as the API to the BetterTTV emote servers 11 | * 12 | * @property getGlobalEmotes a function used to get the global BetterBTTV Emote 13 | * @property getChannelEmotes a function used to get the channel specific BetterBTTV Emote 14 | * */ 15 | interface BetterTTVEmoteClient { 16 | 17 | /** 18 | * - **getGlobalEmotes** is a GET request to the BetterTTV backend to request all global emotes. 19 | * - You can read more about the BetterTTV documentation, [HERE](https://betterttv.com/developers/api#global-emotes) 20 | * */ 21 | @GET("cached/emotes/global") 22 | suspend fun getGlobalEmotes( 23 | ): Response> 24 | 25 | 26 | /** 27 | * - **getGlobalEmotes** is a GET request to the BetterTTV backend to request all channel specific emotes. 28 | * - You can read more about the BetterTTV documentation, [HERE](https://betterttv.com/developers/api#channel-emotes) 29 | * 30 | * @param broadcasterId a String representing the unique identifier of the Twitch streamer 31 | * */ 32 | @GET("cached/users/twitch/{broadcasterId}") 33 | suspend fun getChannelEmotes( 34 | @Path("broadcasterId") broadcasterId: String 35 | ): Response 36 | 37 | 38 | } 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/clients/TwitchAuthenticationClient.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.clients 2 | 3 | import com.example.clicker.network.models.twitchAuthentication.ValidatedUser 4 | import retrofit2.Response 5 | import retrofit2.http.Field 6 | import retrofit2.http.FormUrlEncoded 7 | import retrofit2.http.GET 8 | import retrofit2.http.Header 9 | import retrofit2.http.Headers 10 | import retrofit2.http.POST 11 | 12 | /** 13 | * TwitchAuthenticationClient is the interface meant to act as the HTTP client that interacts with the Twitch Authentication 14 | * servers.All methods regarding authentication with reside inside of this interface 15 | * 16 | * @property validateToken a function meant to validate a token with the Twitch servers 17 | * @property logout a function meant to end the users logged in session 18 | * */ 19 | interface TwitchAuthenticationClient { 20 | 21 | /** 22 | * - represented as a GET method. This function is used to verify the validity of the [authorization] token with 23 | * the Twitch servers 24 | * 25 | * @param url a String used to represent a dynamic URL. 26 | * @param authorization a String used to represent the OAuth token that is being sent to be validated 27 | * */ 28 | @GET("validate") 29 | suspend fun validateToken( 30 | @Header("Authorization") authorization: String 31 | ): Response 32 | 33 | /** 34 | * - logout represents a POST method. This function meant to end the users logged in session with the Twitch server 35 | * 36 | * @param url a String used to represent a dynamic URL of where this request is being sent. 37 | * @param clientId a String used to represent the clientId(unique identifier) of this application 38 | * @param token a String used to represent the OAuth token that uniquely identifies this user's granted abilities 39 | * */ 40 | @Headers("Content-Typ: application/x-www-form-urlencoded") 41 | @FormUrlEncoded 42 | @POST("revoke") 43 | suspend fun logout( 44 | @Field("client_id") clientId: String, 45 | @Field("token") token: String 46 | ): Response 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/clients/TwitchEmoteClient.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.clients 2 | 3 | import com.example.clicker.network.models.emotes.ChannelEmoteResponse 4 | import com.example.clicker.network.models.emotes.EmoteData 5 | import com.example.clicker.network.models.emotes.GlobalChatBadgesData 6 | import com.example.clicker.network.models.twitchAuthentication.ValidatedUser 7 | import com.example.clicker.network.models.twitchStream.ChatSettings 8 | import com.google.gson.annotations.SerializedName 9 | import retrofit2.Response 10 | import retrofit2.http.GET 11 | import retrofit2.http.Header 12 | import retrofit2.http.Query 13 | 14 | /** 15 | * TwitchEmoteClient is the interface that Retrofit will use and turn into a HTTP client. Specifically, this interface 16 | * is meant to interact with the Twitch API Emote servers 17 | * 18 | * @property getGlobalEmotes a function, when called, will get the Twitch global emotes 19 | * @property getChannelEmotes a function, when called, will get the channel specific Twitch emotes 20 | * @property getGlobalChatBadges a function, when called, will get the Twitch global subscriber badges 21 | * */ 22 | interface TwitchEmoteClient { 23 | 24 | /** 25 | * - represented as a GET method. This function is used to get the available Global Twitch Emotes 26 | * 27 | * @param authorization a String used to represent the OAuth token that uniquely identifies this user's granted abilities 28 | * @param clientId a String used to represent the clientId(unique identifier) of this application 29 | * */ 30 | @GET("chat/emotes/global") 31 | suspend fun getGlobalEmotes( 32 | @Header("Authorization") authorization: String, 33 | @Header("Client-Id") clientId: String, 34 | ): Response 35 | 36 | @GET("chat/emotes") 37 | suspend fun getChannelEmotes( 38 | @Header("Authorization") authorization: String, 39 | @Header("Client-Id") clientId: String, 40 | @Query("broadcaster_id") broadcasterId: String 41 | ): Response 42 | 43 | /** 44 | * getGlobalChatBadges is a function meant to get the all the global chat badges that Twitch has available. You can read more about 45 | * getting global chat badges [HERE](https://dev.twitch.tv/docs/api/reference/#get-global-chat-badges) 46 | * */ 47 | @GET("chat/badges/global") 48 | suspend fun getGlobalChatBadges( 49 | @Header("Authorization") authorization: String, 50 | @Header("Client-Id") clientId: String, 51 | ):Response 52 | } 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/clients/TwitchHomeClient.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.clients 2 | 3 | import com.example.clicker.network.models.twitchClient.GetModChannels 4 | import com.example.clicker.network.models.twitchRepo.FollowedLiveStreams 5 | import retrofit2.Response 6 | import retrofit2.http.GET 7 | import retrofit2.http.Header 8 | import retrofit2.http.Query 9 | 10 | 11 | /** 12 | * TwitchHomeClient is the interface that Retrofit will use and turn into a HTTP client. Specifically, this interface 13 | * is meant to interact with the Twitch API for the home page 14 | * 15 | * @property getFollowedStreams a function meant to get all of the user's live followed streams 16 | * @property getModeratedChannels a function meant to get all of the user's channels they are a moderator for (offline and online) 17 | * */ 18 | interface TwitchHomeClient { 19 | 20 | 21 | /** 22 | * - getFollowedStreams represents a GET method. a function meant to get all of the user's live followed streams 23 | * 24 | * @param authorization a String used to represent the OAuth token that uniquely identifies this user's granted abilities 25 | * @param clientId a String used to represent the clientId(unique identifier) of this application 26 | * @param userId a String used to represent the OAuth token that uniquely identifies this user 27 | * 28 | * @return a [Response] object containing [FollowedLiveStreams] 29 | * */ 30 | 31 | @GET("streams/followed") 32 | suspend fun getFollowedStreams( 33 | @Header("Authorization") authorization: String, 34 | @Header("Client-Id") clientId: String, 35 | @Query("user_id") userId: String 36 | ): Response 37 | 38 | /** 39 | * - getModeratedChannels represents a GET method. a function meant to get all of the user's channels they are a moderator for 40 | * 41 | * @param authorizationToken a String used to represent the OAuth token that uniquely identifies this user's granted abilities 42 | * @param clientId a String used to represent the clientId(unique identifier) of this application 43 | * @param userId a String used to represent the OAuth token that uniquely identifies this user 44 | * 45 | * @return a [Response] object containing [GetModChannels] 46 | * */ 47 | @GET("moderation/channels") 48 | suspend fun getModeratedChannels( 49 | @Header("Authorization") authorizationToken: String, 50 | @Header("Client-Id") clientId: String, 51 | @Query("user_id") userId: String 52 | ):Response 53 | 54 | 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/clients/TwitchVODClient.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.clients 2 | 3 | import com.example.clicker.network.models.twitchRepo.FollowedLiveStreams 4 | import retrofit2.Response 5 | import retrofit2.http.GET 6 | import retrofit2.http.Header 7 | import retrofit2.http.Query 8 | import javax.annotation.concurrent.Immutable 9 | 10 | interface TwitchVODClient { 11 | 12 | 13 | /** 14 | * - getChannelVODs represents a GET method. a function meant to get all of the user's VOD videos 15 | * - [Official Twitch documentation on getting VODS](https://dev.twitch.tv/docs/api/reference/#get-videos) 16 | * 17 | * @param authorization a String used to represent the OAuth token that uniquely identifies this user's granted abilities 18 | * @param clientId a String used to represent the clientId(unique identifier) of this application 19 | * @param userId a String used to represent the user we are getting VODS for 20 | * 21 | * @return a [Response] object containing [VODData] 22 | * */ 23 | 24 | @GET("videos") 25 | suspend fun getChannelVODs( 26 | @Header("Authorization") authorization: String, 27 | @Header("Client-Id") clientId: String, 28 | @Query("user_id") userId: String 29 | ): Response 30 | } 31 | 32 | 33 | @Immutable //adding immutable to make it stable 34 | data class VODData( 35 | val data: List 36 | ) 37 | 38 | data class VOD( 39 | val id: String, 40 | val stream_id: String, 41 | val user_id: String, 42 | val user_login: String, 43 | val user_name: String, 44 | val title: String, 45 | val description: String, 46 | val created_at: String, 47 | val published_at: String, 48 | val url: String, 49 | val thumbnail_url: String, 50 | val viewable: String, 51 | val view_count: Int, 52 | val language: String, 53 | val type: String, 54 | val duration: String, 55 | val muted_segments: List? // This can be more specific if you know the structure 56 | ) 57 | 58 | data class MutedSegment( 59 | val duration: Int, 60 | val offset: Int 61 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/domain/BetterTTVEmotes.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.domain 2 | 3 | 4 | import com.example.clicker.network.models.emotes.IndivBetterTTVEmote 5 | 6 | import com.example.clicker.util.Response 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | interface BetterTTVEmotes { 10 | 11 | suspend fun getBetterTTVGlobalEmotes(): Flow>> 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/domain/NetworkMonitorRepo.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.domain 2 | 3 | import kotlinx.coroutines.flow.StateFlow 4 | 5 | 6 | /** 7 | * NetworkMonitorRepo is the interface that acts as the API for all the methods needed to interact with the 8 | * current state of the Network's status 9 | * 10 | * @property connectionLost a function called when the connection is lost 11 | * @property connectionAvailable a function called when there is a connection 12 | * @property networkAvailable a [StateFlow] object representing the current state of the Network. 13 | * */ 14 | interface NetworkMonitorRepo { 15 | 16 | fun connectionLost():Unit 17 | fun connectionAvailable():Unit 18 | 19 | val networkAvailable: StateFlow 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/domain/TwitchAuthentication.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.domain 2 | 3 | import com.example.clicker.network.models.twitchAuthentication.ValidatedUser 4 | import com.example.clicker.util.NetworkAuthResponse 5 | import com.example.clicker.util.NetworkNewUserResponse 6 | import com.example.clicker.util.NetworkResponse 7 | import com.example.clicker.util.Response 8 | import kotlinx.coroutines.flow.Flow 9 | import kotlinx.coroutines.flow.StateFlow 10 | 11 | /** 12 | * TwitchAuthentication is the interface that acts as the API for all the methods needed to interact with Twitch's authentication API 13 | * 14 | * @property validateToken a function meant to validate a user's OAuth token 15 | * @property logout a function meant to end the user's logged in session 16 | * */ 17 | interface TwitchAuthentication { 18 | 19 | /** 20 | * validateToken is a function that is called to validate [token] with the Twitch servers 21 | * 22 | * @param token a String representing a oAuth token 23 | * 24 | * @return a flow containing a [NetworkAuthResponse] object of type [ValidatedUser] 25 | * */ 26 | suspend fun validateToken( 27 | token: String 28 | ): Flow> 29 | 30 | /** 31 | * logout is a function that is called to end the user's logged in session 32 | * 33 | * @param token a String representing a oAuth token 34 | * @param clientId a String representing the unique identifier of this device 35 | * */ 36 | fun logout(clientId: String, token: String): Flow> 37 | 38 | 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/domain/TwitchEventSubscriptionWebSocket.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.domain 2 | 3 | import com.example.clicker.network.clients.UnbanRequestItem 4 | import com.example.clicker.network.models.twitchStream.ChatSettingsData 5 | import com.example.clicker.network.repository.util.AutoModMessageUpdate 6 | import com.example.clicker.network.repository.util.AutoModQueueMessage 7 | import com.example.clicker.network.websockets.ResolvedUnBanRequestStatusNId 8 | import com.example.clicker.presentation.enhancedModView.ModActionData 9 | 10 | import kotlinx.coroutines.flow.StateFlow 11 | 12 | 13 | /** 14 | * TwitchEventSubscriptionWebSocket is the interface that acts as the API for all the methods needed to interact with Twitch's 15 | * EventSub Web Socket 16 | * - You can read more about it [HERE](https://dev.twitch.tv/docs/eventsub/manage-subscriptions/) 17 | * 18 | * @property parsedSessionId a [StateFlow] object containing a String that represents the id sent in the initial welcoming message from 19 | * the Twitch server. You can read more about the welcome message [HERE](https://dev.twitch.tv/docs/eventsub/handling-websocket-events/#welcome-message) 20 | * @property autoModMessageQueue a [StateFlow] object containing the most recent [AutoModQueueMessage] object sent from the Twitch server 21 | * @property messageIdForAutoModQueue a [StateFlow] object containing the most recent [AutoModMessageUpdate] object sent from the Twitch server 22 | * @property updatedChatSettingsData a [StateFlow] object containing the most recent [ChatSettingsData] object sent from the Twitch server 23 | * @property modActions a [StateFlow] object containing the most recent [ModActionData] object sent from the Twitch server 24 | * 25 | * @property newWebSocket() 26 | * @property closeWebSocket() 27 | * 28 | * */ 29 | interface TwitchEventSubscriptionWebSocket { 30 | 31 | val parsedSessionId: StateFlow 32 | val autoModMessageQueue: StateFlow 33 | val messageIdForAutoModQueue: StateFlow 34 | val updatedChatSettingsData: StateFlow 35 | val mostRecentResolvedUnbanRequest: StateFlow 36 | val mostRecentUnbanRequest: StateFlow 37 | 38 | val modActions: StateFlow 39 | 40 | 41 | /** 42 | * a function that when called will create a new Web Socket 43 | * */ 44 | fun newWebSocket():Unit 45 | 46 | /** 47 | * a function that when called will close a existing Web Socket 48 | * */ 49 | fun closeWebSocket():Unit 50 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/domain/TwitchRepo.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.domain 2 | 3 | import com.example.clicker.network.models.twitchClient.GetModChannels 4 | import com.example.clicker.network.models.twitchRepo.StreamData 5 | import com.example.clicker.util.NetworkAuthResponse 6 | import com.example.clicker.util.NetworkNewUserResponse 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | /** 10 | * TwitchRepo is the interface that acts as the API for all the methods needed to interact with Twitch's base API. So any 11 | * end point that is not related to a individual users stream 12 | * 13 | * @property getFollowedLiveStreams a function meant to be called to get the logged in user's live channels 14 | * @property getModeratedChannels a function meant to be called to get the logged in user's live and offline channels they mod for 15 | * */ 16 | interface TwitchRepo { 17 | 18 | /** 19 | * - getFollowedLiveStreams a function meant to be called to get the logged in user's live channels 20 | * 21 | * @param authorizationToken a String used to represent the OAuth token that uniquely identifies this user's granted abilities 22 | * @param clientId a String used to represent the clientId(unique identifier) of this application 23 | * @param userId a String used to represent the unique identifier of the current logged in user 24 | * */ 25 | suspend fun getFollowedLiveStreams( 26 | authorizationToken: String, 27 | clientId: String, 28 | userId: String 29 | ): Flow>> 30 | 31 | /** 32 | * - getModeratedChannels() Gets a list of channels that the specified user has moderator privileges in. 33 | * 34 | * @param authorizationToken a String used to represent the OAuth token that uniquely identifies this user's granted abilities 35 | * @param clientId a String used to represent the clientId(unique identifier) of this application 36 | * @param userId a String used to represent the unique identifier of the current logged in user 37 | * */ 38 | suspend fun getModeratedChannels( 39 | authorizationToken: String, 40 | clientId: String, 41 | userId: String 42 | ):Flow> 43 | 44 | 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/domain/TwitchVODRepo.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.domain 2 | 3 | import com.example.clicker.network.clients.VODData 4 | import com.example.clicker.network.models.twitchStream.ChatSettings 5 | import com.example.clicker.util.Response 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | 9 | /** 10 | * TwitchRepo is the interface that acts as the API for all the methods needed to interact with Twitch's base API. So any 11 | * end point that is not related to a individual users stream 12 | * 13 | * @property getFollowedLiveStreams a function meant to be called to get the logged in user's live channels 14 | * @property getModeratedChannels a function meant to be called to get the logged in user's live and offline channels they mod for 15 | * */ 16 | interface TwitchVODRepo { 17 | 18 | 19 | 20 | 21 | /** 22 | * - getChannelVODs is used to get all the channels VODs 23 | * 24 | * @param oAuthToken a String used to represent the OAuth token that uniquely identifies this user's granted abilities 25 | * @param clientId a String used to represent the clientId(unique identifier) of this application 26 | * @param userId a String used to represent the user we are getting VODS for 27 | * */ 28 | suspend fun getChannelVODs(oAuthToken: String, clientId: String, userId: String): Flow> 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/interceptors/LiveNetworkMonitor.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.interceptors 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | import android.net.Network 6 | import android.net.NetworkCapabilities 7 | import android.net.NetworkRequest 8 | import android.util.Log 9 | import javax.inject.Inject 10 | 11 | /** 12 | * LiveNetworkMonitor is a class meant to check the application's network connectivity 13 | * 14 | * @param context a [Context] object. This is what is used to get the system's connectivity manager 15 | * */ 16 | class LiveNetworkMonitor @Inject constructor( 17 | private val context: Context 18 | ):NetworkMonitor { 19 | private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 20 | //todo: move below 21 | 22 | 23 | //todo: move above 24 | 25 | override fun isConnected(): Boolean { 26 | val network =connectivityManager.activeNetwork 27 | return network != null 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/interceptors/LoggingInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.interceptors 2 | 3 | import android.util.Log 4 | import okhttp3.Interceptor 5 | import okhttp3.Request 6 | import okhttp3.Response 7 | 8 | class LoggingInterceptor: Interceptor { 9 | 10 | 11 | override fun intercept(chain: Interceptor.Chain): Response { 12 | val request: Request = chain.request() 13 | 14 | 15 | val response = chain.proceed(request) 16 | Log.d("LoggingInterceptorBetterTTV","response ->${response.body}") 17 | val responseBody = response.body 18 | val contentLength = responseBody?.contentLength() ?: 0L 19 | 20 | if (contentLength != 0L) { 21 | val source = responseBody?.source() 22 | source?.request(Long.MAX_VALUE) // Buffer the entire body. 23 | val buffer = source?.buffer 24 | val responseBodyString = buffer?.clone()?.readString(Charsets.UTF_8) 25 | 26 | 27 | //logging the indiv bytes below will crash 28 | // Log.d("LoggingInterceptorBetterTTV", "Response Body Bytes:") 29 | // buffer?.readByteArray()?.also {byteArray-> 30 | // for (byte in byteArray) { 31 | // Log.d("LoggingInterceptorBetterTTV", "byte -->${byte.toInt()}") 32 | // } 33 | // } 34 | 35 | } 36 | 37 | 38 | return response 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/interceptors/NetworkMonitor.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.interceptors 2 | 3 | import android.content.Context 4 | 5 | /** 6 | * NetworkMonitor is a interface meant to act as the api to all network monitoring related issues 7 | * 8 | * @param isConnected a function used to determine if the application is connected to a network or not 9 | * */ 10 | interface NetworkMonitor { 11 | fun isConnected():Boolean 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/interceptors/NetworkMonitorInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.interceptors 2 | 3 | import android.util.Log 4 | import okhttp3.Interceptor 5 | import okhttp3.Request 6 | import okhttp3.Response 7 | import javax.inject.Inject 8 | 9 | /** 10 | * NetworkMonitorInterceptor is a [application-interceptor](https://square.github.io/okhttp/features/interceptors/#application-interceptors) 11 | * meant to first check the status of the Network before sending the request 12 | * 13 | * @param liveNetworkMonitor a [NetworkMonitor] implementation that handles all of the actual network logic checking 14 | * */ 15 | class NetworkMonitorInterceptor @Inject constructor( 16 | private val liveNetworkMonitor:NetworkMonitor 17 | ): Interceptor { 18 | 19 | 20 | @Throws(NoNetworkException::class) 21 | override fun intercept(chain: Interceptor.Chain): Response { 22 | 23 | val request: Request = chain.request() 24 | 25 | 26 | if(liveNetworkMonitor.isConnected()){ 27 | return chain.proceed(request) 28 | }else{ 29 | 30 | throw NoNetworkException("Network Error") 31 | } 32 | 33 | 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/interceptors/NoNetworkException.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.interceptors 2 | 3 | import java.io.IOException 4 | 5 | class NoNetworkException(message:String): IOException(message) 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/interceptors/RetryInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.interceptors 2 | 3 | import android.util.Log 4 | import okhttp3.Interceptor 5 | import okhttp3.Request 6 | import okhttp3.Response 7 | import java.io.IOException 8 | 9 | 10 | class RetryInterceptor( 11 | private val retry:Retry =RetryWithThreeRequests() 12 | ) : Interceptor { 13 | 14 | @Throws(IOException::class) 15 | override fun intercept(chain: Interceptor.Chain): Response { 16 | Log.d("checkingInterceptors","RetryInterceptor") 17 | val request = chain.request() 18 | val isSuccess = retry.requestWithThreeRetries(chain, request) 19 | if (!isSuccess) { 20 | throw IOException("Error. Please try again") 21 | } 22 | return chain.proceed(request) 23 | } 24 | 25 | 26 | } 27 | 28 | interface Retry{ 29 | fun requestWithThreeRetries(chain: Interceptor.Chain, request: Request): Boolean 30 | } 31 | 32 | class RetryWithThreeRequests:Retry{ 33 | override fun requestWithThreeRetries(chain: Interceptor.Chain, request: Request): Boolean { 34 | val retryLimit =3 35 | var tryCount = 0 36 | var response: Response 37 | while (tryCount < retryLimit) { 38 | Log.d("tryRequestWithRetries","trycount --> $tryCount") 39 | response = chain.proceed(request) 40 | if (response.isSuccessful) { 41 | return true 42 | } 43 | response.close() 44 | tryCount++ 45 | } 46 | return false 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/interceptors/responseCodeInterceptors/Authentication401Exception.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.interceptors.responseCodeInterceptors 2 | 3 | import java.io.IOException 4 | 5 | class Authentication401Exception(message:String): IOException(message) {} -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/interceptors/responseCodeInterceptors/Authentication401Interceptor.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.interceptors.responseCodeInterceptors 2 | 3 | import android.util.Log 4 | import com.example.clicker.network.interceptors.NoNetworkException 5 | import okhttp3.Interceptor 6 | import okhttp3.Request 7 | import okhttp3.Response 8 | 9 | class Authentication401Interceptor( 10 | private val authChecker:AuthenticationInterceptor 11 | ): Interceptor { 12 | 13 | @Throws(Authentication401Exception::class) 14 | override fun intercept(chain: Interceptor.Chain): Response { 15 | val request: Request = chain.request() 16 | 17 | // Proceed with the request 18 | val response: Response = chain.proceed(request) 19 | val code = response.code 20 | val codeIs401 = authChecker.responseCodeIs401(code) 21 | 22 | if(codeIs401){ 23 | 24 | throw Authentication401Exception("Problem with OAuth token") 25 | } 26 | 27 | 28 | //else run the multiple requests 29 | return response 30 | } 31 | } 32 | 33 | 34 | interface AuthenticationInterceptor { 35 | fun responseCodeIs401(code:Int):Boolean 36 | 37 | } 38 | 39 | class ResponseChecker(): AuthenticationInterceptor{ 40 | override fun responseCodeIs401(code: Int): Boolean { 41 | return code == 401 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/models/emotes/TwitchChannelEmotes.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.models.emotes 2 | 3 | 4 | 5 | 6 | /** 7 | * - **ChannelEmoteResponse** represents the channel specific Twitch emotes 8 | * 9 | * @param data a List of [ChannelEmote] objects, each representing a unique set of Twitch channel emotes 10 | * */ 11 | data class ChannelEmoteResponse( 12 | val data: List 13 | ) 14 | 15 | /** 16 | * - **Emote** represents the Twitch global emotes 17 | * 18 | * @param id a String used to represent the unique identifier for the Emote 19 | * @param name a String used to represent the 20 | * @param images a [ChannelImages] object used to represent the all the sizes of the Emote 21 | * @param format a List of Strings used to represent the format of the emote 22 | * @param scale a List of Strings used to represent the scale of the emote 23 | * @param theme_mode a List of Strings used to represent the themes of the emote 24 | * @param emote_type a Strings used to represent the type of the channel emote 25 | * */ 26 | data class ChannelEmote( 27 | val id: String, 28 | val name: String, 29 | val images: ChannelImages, 30 | val format: List, 31 | val scale: List, 32 | val theme_mode: List, 33 | val emote_type:String 34 | ) 35 | 36 | 37 | /** 38 | * - **ChannelImages** represents the possible sizes of the channel specific Twitch emotes 39 | * 40 | * @param url_1x a String representing a URL to the small version (28px x 28px) of the emote 41 | * @param url_2x a String representing a URL to the medium version (56px x 56px) of the emote. 42 | * @param url_4x a String representing a URL to the large version (112px x 112px) of the emote. 43 | * */ 44 | data class ChannelImages( 45 | val url_1x: String, 46 | val url_2x: String, 47 | val url_4x: String 48 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/models/twitchAuthentication/AuthenticatedUser.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.models.twitchAuthentication 2 | 3 | data class AuthenticatedUser( 4 | val clientId: String, 5 | val userId: String, 6 | val userName: String 7 | ) 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/models/twitchAuthentication/ValidatedUser.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.models.twitchAuthentication 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** 6 | * - ValidatedUser is a class that represents all of the data returned from a successful GET request 7 | * to the ***https://id.twitch.tv/oauth2/validate*** endpoint. 8 | * - You can read more about the token validation process in the [Twitch Documentation](https://dev.twitch.tv/docs/authentication/validate-tokens/) 9 | * 10 | * @param clientId the individual identifier of the application 11 | * @param login the username of the user logged in 12 | * @param scopes a list of Strings representing all the information this application has access to 13 | * @param userId the unique identifier of this user 14 | * @param expiresIn the time a user's session expires in 15 | * 16 | * */ 17 | data class ValidatedUser( 18 | @SerializedName("client_id") 19 | val clientId: String, 20 | val login: String, 21 | val scopes: List, 22 | @SerializedName("user_id") 23 | val userId: String, 24 | @SerializedName("expires_in") 25 | val expiresIn: Int 26 | 27 | ) 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/models/twitchClient/GetModChannels.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.models.twitchClient 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** 6 | * GetModChannels represents the data that Twitch's servers send back from a `get moderated channels` request 7 | * - [HERE](https://dev.twitch.tv/docs/api/reference/#get-moderated-channels) is the official twitch documentation on getting moderated 8 | * channels. 9 | * 10 | * @param data a list of [GetModChannelsData] objects representing the individual channel the user moderates for 11 | * */ 12 | data class GetModChannels( 13 | val data:List 14 | ) 15 | 16 | /** 17 | * GetModChannelsData represents a channel that a user moderates for 18 | * 19 | * @param broadcasterId uniquely identifies the channel this user can moderate. 20 | * @param broadcasterLogin The channel’s login name 21 | * @param broadcasterName The channel's display name. 22 | * */ 23 | data class GetModChannelsData( 24 | @SerializedName("broadcaster_id") 25 | val broadcasterId: String, 26 | @SerializedName("broadcaster_login") 27 | val broadcasterLogin: String, 28 | @SerializedName("broadcaster_name") 29 | val broadcasterName: String, 30 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/models/twitchRepo/FollowedLiveStreams.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.models.twitchRepo 2 | 3 | import com.example.clicker.network.models.twitchRepo.StreamData 4 | 5 | data class FollowedLiveStreams( 6 | val data: List 7 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/models/twitchStream/AutoModSettings.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.models.twitchStream 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** 6 | * AutoModSettings is used to represent data received from the ***https://api.twitch.tv/helix/moderation/automod/settings*** 7 | * end point 8 | * - Read more about the end point on the official documentation, [HERE](https://dev.twitch.tv/docs/api/reference/#get-automod-settings) 9 | * */ 10 | data class AutoModSettings( 11 | val data: List 12 | ) 13 | 14 | /** 15 | * IndividualAutoModSettings is used to represent the individual AutoMod settings received from the ***https://api.twitch.tv/helix/moderation/automod/settings*** 16 | * end point 17 | * - Read more about the end point on the official documentation, [HERE](https://dev.twitch.tv/docs/api/reference/#get-automod-settings) 18 | * */ 19 | data class IndividualAutoModSettings( 20 | @SerializedName("broadcaster_id") 21 | val broadcasterId: String, 22 | @SerializedName("moderator_id") 23 | val moderatorId: String, 24 | @SerializedName("overall_level") 25 | val overallLevel: Int?, 26 | @SerializedName("sexuality_sex_or_gender") 27 | val sexualitySexOrGender: Int, 28 | @SerializedName("race_ethnicity_or_religion") 29 | val raceEthnicityOrReligion: Int, 30 | @SerializedName("sex_based_terms") 31 | val sexBasedTerms: Int, 32 | 33 | val disability: Int, 34 | val aggression: Int, 35 | val misogyny:Int, 36 | val bullying:Int, 37 | val swearing:Int 38 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/models/twitchStream/BanUserResponse.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.models.twitchStream 2 | 3 | 4 | /** 5 | * BanUserData represents all of the data necessary to ban a user from chat 6 | * 7 | * @param user_id the unique identifier of this user 8 | * @param reason The reason a user was banned 9 | * @param duration a integer used to represent the length of the users ban 10 | * */ 11 | data class BanUserResponse( 12 | val data: List 13 | ) 14 | 15 | data class BanUserResponseData( 16 | val broadcaster_id: String, 17 | val moderator_id: String, 18 | val user_id: String, 19 | val created_at: String, 20 | val end_time: String? // Note that end_time can be null 21 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/models/twitchStream/ChatSettings.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.models.twitchStream 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class ChatSettings( 6 | val data: List 7 | ) 8 | 9 | /** 10 | * Represent the data that the Switches in [ChatSettingsContainer][com.example.clicker.presentation.stream.views.ChatSettingsContainer] 11 | * are manipulating 12 | * */ 13 | data class ChatSettingsData( 14 | @SerializedName("slow_mode") 15 | val slowMode: Boolean, 16 | @SerializedName("slow_mode_wait_time") 17 | val slowModeWaitTime: Int?, 18 | @SerializedName("follower_mode") 19 | val followerMode: Boolean, // 20 | @SerializedName("follower_mode_duration") 21 | val followerModeDuration: Int?, // 22 | @SerializedName("subscriber_mode") 23 | val subscriberMode: Boolean, 24 | @SerializedName("emote_mode") // 25 | val emoteMode: Boolean, 26 | // @SerializedName("unique_chat_mode") 27 | // val uniqueChatMode: Boolean 28 | ) 29 | 30 | /** 31 | * Represent the data that is sent as the body to the endpoint: PATCH https://api.twitch.tv/helix/chat/settings 32 | * - Read more in the documentation, [HERE](https://dev.twitch.tv/docs/api/reference/#update-chat-settings) 33 | * */ 34 | data class UpdateChatSettings( 35 | val emote_mode: Boolean, 36 | val follower_mode: Boolean, 37 | val slow_mode: Boolean, 38 | val subscriber_mode: Boolean 39 | ) 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/models/twitchStream/UpdatedChatSettings.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.models.twitchStream 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** 6 | * - CURRENTLY NOT BEING USED BUT DO NOT DELETE! This will be used in the future when more options are given to the user 7 | * 8 | * The UpdatedChatSettings holds all the data that needs to be sent to the Twitch servers to update the current chat settings. 9 | * Can read more about it in the Twitch documentation, [HERE](https://dev.twitch.tv/docs/api/reference/#update-chat-settings) 10 | * */ 11 | data class UpdatedChatSettings( 12 | @SerializedName("emote_mode") 13 | val emoteMode: Boolean, 14 | @SerializedName("follower_mode") 15 | val followerMode: Boolean, 16 | @SerializedName("follower_mode_duration") 17 | val followerModeDuration: Int, 18 | @SerializedName("non_moderator_chat_delay") 19 | val nonModeratorChatDelay: Boolean, 20 | @SerializedName("non_moderator_chat_delay_duration") 21 | val nonModeratorChatDelayDuration: Int, 22 | @SerializedName("slow_mode") 23 | val slowMode: Boolean, 24 | @SerializedName("slow_mode_wait_time") 25 | val slowModeWaitTime: Int, 26 | @SerializedName("subscriber_mode") 27 | val subscriberMode: Boolean, 28 | @SerializedName("unique_chat_mode") 29 | val uniqueChatMode: Boolean 30 | ) 31 | 32 | data class ChatSettingsResponse( 33 | val data: List 34 | ) 35 | 36 | data class ChatSetting( 37 | val broadcaster_id: String, 38 | val moderator_id: String, 39 | val slow_mode: Boolean, 40 | val slow_mode_wait_time: Int, 41 | val follower_mode: Boolean, 42 | val follower_mode_duration: Int?, 43 | val subscriber_mode: Boolean, 44 | val emote_mode: Boolean, 45 | val unique_chat_mode: Boolean, 46 | val non_moderator_chat_delay: Boolean, 47 | val non_moderator_chat_delay_duration: Int? 48 | ) 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/repository/BetterTTVEmotesImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.repository 2 | 3 | import android.util.Log 4 | 5 | import com.example.clicker.network.clients.BetterTTVEmoteClient 6 | import com.example.clicker.network.domain.BetterTTVEmotes 7 | import com.example.clicker.network.repository.util.handleNetworkAuthExceptions 8 | import com.example.clicker.util.NetworkAuthResponse 9 | import com.example.clicker.util.Response 10 | import kotlinx.coroutines.flow.Flow 11 | import kotlinx.coroutines.flow.flow 12 | import javax.inject.Inject 13 | 14 | class BetterTTVEmotesImpl @Inject constructor( 15 | private val betterTTVClient: BetterTTVEmoteClient 16 | ): BetterTTVEmotes { 17 | 18 | 19 | 20 | override suspend fun getBetterTTVGlobalEmotes()= flow{ 21 | emit(Response.Loading) 22 | Log.d("getGlobalBetterTTVEmotes", "LOADING") 23 | val response = betterTTVClient.getGlobalEmotes() 24 | if (response.isSuccessful) { 25 | 26 | val data = response.body() ?: listOf() 27 | Log.d("getGlobalBetterTTVEmotes", "DATA ->${data}") 28 | emit(Response.Success(data)) 29 | } else { 30 | Log.d("getGlobalBetterTTVEmotes", "message ->${response.message()}") 31 | Log.d("getGlobalBetterTTVEmotes", "code ->${response.code()}") 32 | Log.d("getGlobalBetterTTVEmotes", "FAILED ->${response.body()}") 33 | emit(Response.Failure(Exception("Failed to get emote"))) 34 | } 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/repository/DebugRetrofitResponse.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.repository 2 | 3 | import android.util.Log 4 | import okhttp3.Interceptor 5 | import okhttp3.Response 6 | 7 | class DebugRetrofitResponse: Interceptor { 8 | override fun intercept(chain: Interceptor.Chain): Response { 9 | val request = chain.request() 10 | val response = chain.proceed(request) 11 | val rawJson = response.body!!.string() 12 | Log.d("DebugRetrofitResponse","response -->${response.body}") 13 | Log.d("DebugRetrofitResponse","rawJson -->${rawJson}") 14 | return response 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/repository/NetworkMonitorImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.repository 2 | 3 | import com.example.clicker.network.domain.NetworkMonitorRepo 4 | import com.example.clicker.network.domain.TwitchAuthentication 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.StateFlow 7 | import kotlinx.coroutines.flow.asStateFlow 8 | 9 | /** 10 | * NetworkMonitorImpl the implementation class of [NetworkMonitorRepo]. This class contains all the methods 11 | * relating to the state of the network 12 | * */ 13 | class NetworkMonitorImpl() : NetworkMonitorRepo { 14 | private val _networkAvailable = MutableStateFlow(true) 15 | override val networkAvailable: StateFlow = _networkAvailable.asStateFlow() 16 | 17 | 18 | override fun connectionLost() { 19 | _networkAvailable.tryEmit(false) 20 | } 21 | 22 | override fun connectionAvailable() { 23 | _networkAvailable.tryEmit(true) 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/repository/TwitchVODRepoImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.repository 2 | 3 | import android.util.Log 4 | import com.example.clicker.network.clients.TwitchVODClient 5 | import com.example.clicker.network.clients.VODData 6 | import com.example.clicker.network.domain.TwitchVODRepo 7 | import com.example.clicker.util.Response 8 | import kotlinx.coroutines.flow.Flow 9 | import kotlinx.coroutines.flow.catch 10 | import kotlinx.coroutines.flow.flow 11 | import javax.inject.Inject 12 | 13 | class TwitchVODRepoImpl @Inject constructor( 14 | val twitchVODClient: TwitchVODClient 15 | ): TwitchVODRepo { 16 | 17 | 18 | 19 | 20 | 21 | override suspend fun getChannelVODs( 22 | oAuthToken: String, 23 | clientId: String, 24 | userId: String 25 | ): Flow> = flow> { 26 | 27 | val response = twitchVODClient.getChannelVODs( 28 | authorization = "Bearer $oAuthToken", 29 | clientId = clientId, 30 | userId = userId 31 | ) 32 | if (response.isSuccessful) { 33 | val body = response.body() 34 | Log.d("getChannelVODs","SUCCESS") 35 | Log.d("getChannelVODs","list -> $body") 36 | 37 | emit(Response.Success(body!!)) 38 | } else { 39 | Log.d("getChannelVODs","FAILED") 40 | Log.d("getChannelVODs","code -> ${response.code()}") 41 | emit(Response.Failure(Exception("Error! Please try again"))) 42 | } 43 | }.catch { 44 | emit(Response.Failure(Exception("Error! Please try again"))) 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/repository/util/EmoteParsing.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.repository.util 2 | 3 | import androidx.compose.foundation.layout.fillMaxSize 4 | import androidx.compose.foundation.layout.padding 5 | import androidx.compose.foundation.text.InlineTextContent 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.res.stringResource 8 | import androidx.compose.ui.text.Placeholder 9 | import androidx.compose.ui.text.PlaceholderVerticalAlign 10 | import androidx.compose.ui.unit.dp 11 | import androidx.compose.ui.unit.sp 12 | import coil.compose.AsyncImage 13 | import com.example.clicker.R 14 | import com.example.clicker.network.repository.models.EmoteNameUrl 15 | import com.example.clicker.network.repository.models.EmoteNameUrlEmoteType 16 | 17 | 18 | class EmoteParsing { 19 | 20 | 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/network/websockets/models/ParsingEngineModels.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.network.websockets.models 2 | 3 | /** 4 | * PrivateMessageType represents the two types of messages sent in chat, 1) [MESSAGE] 5 | * and 2) [EMOTE] 6 | * 7 | * @property MESSAGE represents the normal message a user will send 8 | * @property EMOTE represents the emote a user will send 9 | * **/ 10 | enum class PrivateMessageType { 11 | MESSAGE, EMOTE 12 | } 13 | /** 14 | * MessageToken represents the individualized token a user sends 15 | * 16 | * @param messageType a [PrivateMessageType] object representing if this contains either a message or an emote 17 | * @param messageValue A String representing what the user sent 18 | * @param url A String that will be filled with a URL if the [messageType] is EMOTE. Otherwise is will be empty 19 | * */ 20 | data class MessageToken( 21 | val messageType:PrivateMessageType, 22 | val messageValue:String ="", 23 | val url:String="" 24 | ) 25 | 26 | 27 | data class EmoteInText( 28 | val emoteUrl:String, 29 | val startIndex:Int, 30 | val endIndex:Int 31 | ) 32 | 33 | fun findEmoteNames(input: String, emoteNames: List): List { 34 | val regex = Regex("\\b(?:${emoteNames.joinToString("|")})\\b") 35 | return regex.findAll(input).map { 36 | EmoteInText( 37 | emoteUrl = "https://static-cdn.jtvnw.net/emoticons/v2/64138/static/light/1.0", 38 | startIndex = it.range.first, 39 | endIndex = it.range.last 40 | ) 41 | }.toList() 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/authentication/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.authentication 2 | 3 | import com.example.clicker.BuildConfig 4 | 5 | private val clientId = BuildConfig.CLIENT_ID 6 | private val redirectUrl = BuildConfig.REDIRECT_URL 7 | val twitchAuthorizationScopeURL ="https://id.twitch.tv/oauth2/authorize?client_id=$clientId&redirect_uri=$redirectUrl&response_type=token&scope=user:read:follows+channel:moderate+moderation:read+chat:read+chat:edit+channel:read:editors+moderator:manage:chat_settings+moderator:read:automod_settings+moderator:manage:chat_messages+moderator:manage:automod_settings+moderator:manage:banned_users+user:read:moderated_channels+channel:manage:broadcast+user:edit:broadcast+moderator:manage:automod+moderator:manage:blocked_terms+user:read:chat+user:bot+channel:bot+moderator:read:moderators+moderator:read:vips+moderator:manage:warnings+user:read:subscriptions+moderator:read:unban_requests+moderator:manage:unban_requests+channel:read:redemptions+channel:manage:redemptions+channel:read:stream_key" -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/authentication/CustomVRLoginPage.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.authentication 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.webkit.WebView 6 | 7 | class CustomVRLoginPage: WebView { 8 | 9 | constructor(context: Context?) : super(context!!) {} 10 | constructor(context: Context?, attrs: AttributeSet?) : super( 11 | context!!, attrs 12 | ) { 13 | } 14 | 15 | 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/home/WorkerViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.home 2 | 3 | import androidx.compose.runtime.MutableState 4 | import androidx.compose.runtime.State 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.lifecycle.LiveData 7 | import androidx.lifecycle.ViewModel 8 | import androidx.work.WorkInfo 9 | import com.example.clicker.network.models.twitchAuthentication.AuthenticatedUser 10 | import com.example.clicker.network.models.twitchRepo.StreamData 11 | import com.example.clicker.util.Response 12 | import dagger.hilt.android.lifecycle.HiltViewModel 13 | import javax.inject.Inject 14 | import kotlinx.coroutines.flow.MutableStateFlow 15 | 16 | data class WorkerUIState( 17 | 18 | val streamStatus: Response> = Response.Loading, 19 | val authStatus: String = "Checking if token is available", 20 | val testingState: Response = Response.Loading, 21 | val loggingOut: Response? = null 22 | ) 23 | 24 | @HiltViewModel 25 | class WorkerViewModel @Inject constructor( 26 | 27 | ) : ViewModel() { 28 | 29 | // TODO: MAKE THIS A DO NOTHING CLASS THAT WILL JUST KEEP RUNNING EVER HOUR. TO SATISFY THE TWITCH DOCS 30 | 31 | private var _uiState: MutableState = mutableStateOf(WorkerUIState()) 32 | val state: State = _uiState 33 | 34 | private val _oAuthUserToken: MutableStateFlow = MutableStateFlow(null) 35 | private val _AuthenticatedUser: MutableStateFlow = MutableStateFlow(null) 36 | var liveDataWork: LiveData? = null 37 | 38 | 39 | } 40 | 41 | data class MainStates( 42 | val oAuthToken: String? = null, 43 | val authUser: AuthenticatedUser? = null 44 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/home/util/ModeratorParsing.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.home.util 2 | 3 | import com.example.clicker.network.models.twitchClient.GetModChannelsData 4 | import com.example.clicker.network.models.twitchRepo.StreamData 5 | 6 | /** 7 | * createOfflineAndOnlineLists() is used to create the list of online mod channels and offline mod channels 8 | * 9 | * @param modChannelList is a list of [GetModChannelsData] objects representing all the channels the user is a moderator for 10 | * @param liveFollowedStreamers is a list of [StreamData] objects representing all the live channels that the user follows 11 | * 12 | * @return [ModeratorOfflineOnlineLists] 13 | * */ 14 | fun createOfflineAndOnlineLists( 15 | modChannelList: List, 16 | liveFollowedStreamers:List 17 | ):ModeratorOfflineOnlineLists{ 18 | //todo: return both of these 19 | val offlineModList = mutableListOf() 20 | val onlineList = mutableListOf() 21 | 22 | val listOfModName = modChannelList.map{it.broadcasterName} 23 | val listOfStreamerName = liveFollowedStreamers.map { it.userName } 24 | 25 | for (name in listOfModName){ 26 | if(listOfStreamerName.contains(name)){ 27 | val item = liveFollowedStreamers.first { it.userName == name } 28 | onlineList.add(item) 29 | }else{ 30 | val offlineItem = modChannelList.first{it.broadcasterName ==name} 31 | offlineModList.add(offlineItem.broadcasterName) 32 | } 33 | } 34 | return ModeratorOfflineOnlineLists( 35 | offlineModList =offlineModList, 36 | onlineList=onlineList 37 | ) 38 | } 39 | 40 | /** 41 | * - ModeratorOfflineOnlineLists is used to represent the offline and online moderation channels 42 | * @param offlineModList a list containing on the names of the offline mod channels 43 | * @param onlineList a list of [StreamData] objects that represent all of the online mod channels 44 | * */ 45 | data class ModeratorOfflineOnlineLists( 46 | val offlineModList: MutableList, 47 | val onlineList: MutableList 48 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/minigames/MiniGameFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.minigames 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.compose.material.Text 9 | import androidx.compose.ui.graphics.Color 10 | import androidx.compose.ui.platform.ViewCompositionStrategy 11 | import androidx.compose.ui.unit.sp 12 | import androidx.core.view.size 13 | import androidx.navigation.Navigation.findNavController 14 | import androidx.navigation.findNavController 15 | import com.example.clicker.R 16 | import com.example.clicker.databinding.FragmentMiniGameBinding 17 | import com.example.clicker.databinding.FragmentModChannelsBinding 18 | import com.example.clicker.presentation.minigames.views.MiniGameViews 19 | import com.example.clicker.ui.theme.AppTheme 20 | 21 | 22 | class MiniGameFragment : Fragment() { 23 | 24 | private var _binding: FragmentMiniGameBinding? = null 25 | private val binding get() = _binding!! 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | 30 | } 31 | 32 | override fun onCreateView( 33 | inflater: LayoutInflater, container: ViewGroup?, 34 | savedInstanceState: Bundle? 35 | ): View? { 36 | // Inflate the layout for this fragment 37 | _binding = FragmentMiniGameBinding.inflate(inflater, container, false) 38 | val view = binding.root 39 | binding.composeView.apply { 40 | setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) 41 | setContent { 42 | AppTheme { 43 | MiniGameViews( 44 | onNavigate = {dest -> findNavController().navigate(dest) } 45 | ) 46 | 47 | } 48 | } 49 | } 50 | return view 51 | } 52 | 53 | 54 | 55 | override fun onDestroyView() { 56 | super.onDestroyView() 57 | _binding = null 58 | } 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/minigames/dinoRun/DinoRunJNI.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.minigames.dinoRun 2 | 3 | import android.util.Log 4 | 5 | 6 | object DinoRunJNI{ 7 | 8 | 9 | init{ 10 | //todo: I need to make this file and add it to the CMakeList 11 | System.loadLibrary("dino_run"); 12 | } 13 | 14 | 15 | private var onTextUpdate: ((String) -> Unit)? = null 16 | 17 | private var onSpeedIncrease: (() -> Unit)? = null 18 | private var removeStartGame: (() -> Unit)? = null 19 | private var showGameOver: (() -> Unit)? = null 20 | 21 | @JvmStatic 22 | fun setOnTextUpdateCallback(callback: (String) -> Unit) { 23 | onTextUpdate = callback 24 | } 25 | 26 | @JvmStatic 27 | fun updateTextFromNative(newText: String) { 28 | onTextUpdate?.invoke(newText) 29 | } 30 | @JvmStatic 31 | fun setOnSpeedIncreaseCallback(callback: () -> Unit) { 32 | onSpeedIncrease = callback 33 | } 34 | 35 | @JvmStatic 36 | fun updateOnSpeedIncreaseFromNative() { 37 | onSpeedIncrease?.invoke() 38 | } 39 | 40 | 41 | @JvmStatic 42 | fun setRemoveStartGameCallback(callback: () -> Unit) { 43 | removeStartGame = callback 44 | } 45 | 46 | @JvmStatic 47 | fun updateRemoveStartGameFromNative() { 48 | removeStartGame?.invoke() 49 | } 50 | //below is new 51 | @JvmStatic 52 | fun setShowGameOverCallback(callback: () -> Unit) { 53 | showGameOver = callback 54 | } 55 | 56 | @JvmStatic 57 | fun updateShowGameOverFromNative() { 58 | showGameOver?.invoke() 59 | } 60 | 61 | 62 | /** 63 | * @param width the current view width 64 | * @param height the current view height 65 | */ 66 | external fun init(width: Int, height: Int) 67 | external fun step() 68 | external fun jump() 69 | // external fun triggerUpdate() 70 | 71 | 72 | 73 | 74 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/minigames/dinoRun/DinoRunRenderer.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.minigames.dinoRun 2 | 3 | import android.content.Context 4 | import android.opengl.GLSurfaceView 5 | import android.util.Log 6 | import android.view.MotionEvent 7 | import android.view.View 8 | import javax.microedition.khronos.egl.EGLConfig 9 | import javax.microedition.khronos.opengles.GL10 10 | 11 | 12 | class DinoRunView(context: Context?) : GLSurfaceView(context), View.OnTouchListener{ 13 | private val renderer = DinoRunRenderer() 14 | 15 | 16 | init{ 17 | init() 18 | setOnTouchListener(this) 19 | } 20 | 21 | private fun init(){ 22 | 23 | setEGLContextClientVersion(2) 24 | setRenderer(renderer) 25 | 26 | } 27 | 28 | 29 | 30 | override fun onTouch(v: View?, event: MotionEvent?): Boolean { 31 | event ?: return false 32 | //v?.parent?.requestDisallowInterceptTouchEvent(true) // this remove the swiping from the horizontal pager 33 | 34 | 35 | 36 | 37 | when (event.action) { 38 | MotionEvent.ACTION_DOWN -> { 39 | 40 | Log.d("TESTINGACtionMoveTouch","JUMP!!!!!") 41 | DinoRunJNI.jump() 42 | 43 | 44 | return true 45 | } 46 | 47 | 48 | MotionEvent.ACTION_MOVE -> { 49 | Log.d("TESTINGACtionMoveTouch","MOVE!!!!!") 50 | 51 | 52 | return true 53 | } 54 | MotionEvent.ACTION_UP -> { 55 | Log.d("TESTINGACtionMove","UPP") 56 | 57 | return true 58 | } 59 | } 60 | return false 61 | } 62 | 63 | } 64 | 65 | 66 | 67 | class DinoRunRenderer : GLSurfaceView.Renderer { 68 | 69 | override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) { 70 | 71 | } 72 | 73 | override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) { 74 | DinoRunJNI.init(width,height) 75 | } 76 | 77 | override fun onDrawFrame(p0: GL10?) { 78 | DinoRunJNI.step() 79 | 80 | } 81 | 82 | 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/minigames/dinoRun/TriangleStrip.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.minigames.dinoRun 2 | 3 | import android.content.Context 4 | import android.opengl.GLSurfaceView 5 | import android.view.View 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.viewinterop.AndroidView 10 | import javax.microedition.khronos.egl.EGLConfig 11 | import javax.microedition.khronos.opengles.GL10 12 | 13 | 14 | @Composable 15 | fun TriangleStripTesting( 16 | modifier:Modifier, 17 | context: Context, 18 | ) { 19 | Box( 20 | modifier = modifier 21 | ) { 22 | AndroidView( 23 | factory = { 24 | TriangleStripView(context) 25 | 26 | }, 27 | 28 | ) 29 | } 30 | } 31 | 32 | class TriangleStripView(context: Context?) : GLSurfaceView(context){ 33 | private val renderer = TriangleStripRenderer() 34 | 35 | 36 | init{ 37 | init() 38 | } 39 | 40 | private fun init(){ 41 | 42 | setEGLContextClientVersion(2) 43 | setRenderer(renderer) 44 | 45 | } 46 | } 47 | 48 | object TriangleStripJNI{ 49 | init{ 50 | //todo: I need to make this file and add it to the CMakeList 51 | System.loadLibrary("triangle_strip"); 52 | } 53 | external fun init(width: Int, height: Int) 54 | external fun step() 55 | 56 | } 57 | 58 | class TriangleStripRenderer : GLSurfaceView.Renderer { 59 | 60 | override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) { 61 | 62 | } 63 | 64 | override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) { 65 | TriangleStripJNI.init(width,height) 66 | } 67 | 68 | override fun onDrawFrame(p0: GL10?) { 69 | TriangleStripJNI.step() 70 | 71 | } 72 | 73 | 74 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/selfStreaming/RTMPNativeClient.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.selfStreaming 2 | 3 | class RTMPNativeClient { 4 | 5 | 6 | companion object { 7 | init { 8 | System.loadLibrary("rtmp_client") 9 | 10 | } 11 | } 12 | 13 | 14 | 15 | external fun nativeOpen( url:String, isPublishMode:Boolean, rtmpPointer:Long, sendTimeoutInMs:Int, receiveTimeoutInMs:Int):Int 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/selfStreaming/clients/RtmpClient.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.selfStreaming.clients 2 | 3 | import com.example.clicker.presentation.selfStreaming.util.UrlParser 4 | import com.example.clicker.presentation.selfStreaming.websocket.RtmpSocket 5 | 6 | import kotlinx.coroutines.CoroutineScope 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.Job 9 | import kotlinx.coroutines.cancel 10 | import kotlinx.coroutines.cancelAndJoin 11 | import kotlinx.coroutines.launch 12 | import java.io.IOException 13 | import javax.inject.Inject 14 | 15 | class RtmpClient @Inject constructor() { 16 | 17 | private var scope = CoroutineScope(Dispatchers.IO) 18 | private var scopeRetry = CoroutineScope(Dispatchers.IO) 19 | private var job: Job? = null 20 | private var socket: RtmpSocket? = null 21 | private var tlsEnabled = false 22 | private val validSchemes = arrayOf("rtmp", "rtmps", "rtmpt", "rtmpts") 23 | 24 | private val tunneled = true 25 | private val port = 80 26 | 27 | 28 | suspend fun connect(url: String?, isRetry: Boolean) { 29 | 30 | job = scope.launch { 31 | if (url == null) { 32 | 33 | return@launch 34 | } 35 | 36 | } 37 | val urlParser = 38 | UrlParser.parse(url?:"", validSchemes) 39 | 40 | establishConnection(urlParser.host) 41 | 42 | } 43 | 44 | @Throws(IOException::class) 45 | private suspend fun establishConnection( 46 | host:String 47 | ): Boolean { 48 | // val socket = TcpTunneledSocket(host, 1935, tlsEnabled) 49 | // socket.connect() 50 | return true 51 | } 52 | 53 | //TODO: THIS IS WHAT IS GOING TO GET CALLED WHEN THE user ends the stream 54 | fun disconnect() { 55 | CoroutineScope(Dispatchers.IO).launch { 56 | disconnect(true) 57 | } 58 | } 59 | private suspend fun disconnect(clear: Boolean) { 60 | 61 | closeConnection() 62 | 63 | // jobRetry?.cancelAndJoin() THIS IS ONLY NEEDED FOR RETRYS 64 | // jobRetry = null 65 | scopeRetry.cancel() 66 | scopeRetry = CoroutineScope(Dispatchers.IO) 67 | 68 | job?.cancelAndJoin() 69 | job = null 70 | scope.cancel() 71 | scope = CoroutineScope(Dispatchers.IO) 72 | 73 | } 74 | 75 | 76 | private suspend fun closeConnection() { 77 | socket?.close() 78 | 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/selfStreaming/clients/StreamToTwitchClient.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.selfStreaming.clients 2 | 3 | import com.example.clicker.network.models.emotes.IndivBetterTTVEmote 4 | import retrofit2.Response 5 | import retrofit2.http.GET 6 | import retrofit2.http.Header 7 | import retrofit2.http.Query 8 | 9 | 10 | /** 11 | * - **StreamToTwitchClient** is an interface that will act as the API to stream to the Twitch servers 12 | * 13 | * @property getStreamKey a function used to get the Stream key needed to stream to the Twitch ingestion server 14 | * */ 15 | interface StreamToTwitchClient { 16 | 17 | 18 | /** 19 | * - **getStreamKey** is a GET request to the Twitch servers to get a user's stream key 20 | * - You can read more about the stream key documentation, [HERE](https://dev.twitch.tv/docs/api/reference/#get-stream-key) 21 | * */ 22 | @GET("streams/key") 23 | suspend fun getStreamKey( 24 | @Header("Authorization") authorization: String, 25 | @Header("Client-Id") clientId: String, 26 | @Query("broadcaster_id") broadcasterId: String 27 | ): Response 28 | } 29 | 30 | 31 | data class StreamKeyResponse( 32 | val data: List 33 | ) 34 | 35 | data class StreamData( 36 | val stream_key: String 37 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/selfStreaming/domain/SelfStreaming.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.selfStreaming.domain 2 | 3 | import com.example.clicker.util.NetworkAuthResponse 4 | import kotlinx.coroutines.flow.Flow 5 | 6 | /** 7 | * SelfStreaming is the interface that acts as the API for all the methods needed to Stream directly to Twitch's servers. You can 8 | * read more about Twitch's streaming API [HERE](https://dev.twitch.tv/docs/video-broadcast/) 9 | * 10 | * @property getStreamKey a function, when called with a [Authentication Token](https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/#implicit-grant-flow) 11 | * and the App's [Client-ID](https://dev.twitch.tv/docs/authentication/register-app/), return the information needed to stream to Twitch 12 | * 13 | * */ 14 | interface SelfStreaming { 15 | 16 | 17 | /** 18 | * - **getStreamKey** a function, when called, will return the channel’s stream key. This stream key is required for streaming 19 | * 20 | * @return a [Flow] containing a String that represents the user's stream key 21 | * */ 22 | fun getStreamKey(oAuthToken:String, clientId:String,broadcasterId:String): Flow> 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/selfStreaming/domain/SelfStreamingSocket.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.selfStreaming.domain 2 | 3 | interface SelfStreamingSocket { 4 | 5 | fun runWebSocket() 6 | 7 | fun closeWebSocket() 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/selfStreaming/openGLSurfaces/CustomGLSurfaceView.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.selfStreaming.openGLSurfaces 2 | 3 | import android.content.Context 4 | import android.opengl.GLSurfaceView 5 | import com.example.clicker.nativeLibraryClasses.NativeCube 6 | import com.example.clicker.nativeLibraryClasses.VideoEncoder 7 | import com.example.clicker.presentation.home.testing3DCode.GL2JNIView 8 | import javax.microedition.khronos.egl.EGLConfig 9 | import javax.microedition.khronos.opengles.GL10 10 | 11 | 12 | class CustomGLSurfaceView(context: Context) : GLSurfaceView(context) { 13 | 14 | init { 15 | init() 16 | } 17 | 18 | 19 | private fun init() { 20 | 21 | 22 | setEGLContextClientVersion(2) 23 | 24 | setRenderer(CustomVideoRenderer()) //called once and only once in the life-cycle of a GLSurfaceView 25 | } 26 | 27 | 28 | 29 | 30 | 31 | } 32 | 33 | /** 34 | * **Renderer** a is a [GLSurfaceView.Renderer] class used to render the C++ UI code 35 | * 36 | * */ 37 | private class CustomVideoRenderer : GLSurfaceView.Renderer { 38 | override fun onDrawFrame(gl: GL10) { 39 | // The system calls this method on each redraw of the GLSurfaceView 40 | //this is called constantly 41 | 42 | VideoEncoder.step() 43 | } 44 | 45 | override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) { 46 | // The system calls this method when the GLSurfaceView geometry changes, 47 | // including changes in size of the GLSurfaceView or orientation of the device screen. 48 | 49 | VideoEncoder.init(width,height) 50 | } 51 | 52 | override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) { 53 | // The system calls this method once, when creating the GLSurfaceView. 54 | // Do nothing. 55 | 56 | 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/selfStreaming/repository/SeflStreamingImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.selfStreaming.repository 2 | 3 | import android.util.Log 4 | import com.example.clicker.network.clients.TwitchStreamInfoClient 5 | import com.example.clicker.presentation.selfStreaming.clients.StreamToTwitchClient 6 | import com.example.clicker.presentation.selfStreaming.domain.SelfStreaming 7 | import com.example.clicker.util.NetworkAuthResponse 8 | import com.example.clicker.util.Response 9 | import kotlinx.coroutines.flow.Flow 10 | import kotlinx.coroutines.flow.catch 11 | import kotlinx.coroutines.flow.flow 12 | import javax.inject.Inject 13 | 14 | class SelfStreamingImpl @Inject constructor( 15 | private val streamToTwitch: StreamToTwitchClient 16 | ): SelfStreaming { 17 | 18 | 19 | 20 | override fun getStreamKey( 21 | oAuthToken: String, 22 | clientId: String, 23 | broadcasterId:String, 24 | 25 | ): Flow> = flow{ 26 | emit(NetworkAuthResponse.Loading) 27 | Log.d("getStreamKeyResponse","LOADING") 28 | 29 | val response = streamToTwitch.getStreamKey( 30 | authorization = "Bearer $oAuthToken", 31 | clientId = clientId, 32 | broadcasterId = broadcasterId 33 | 34 | ) 35 | 36 | if (response.isSuccessful) { 37 | val streamKey = response.body()?.data?.get(0)?.stream_key ?:"" 38 | Log.d("getStreamKeyResponse", "SUCCESS") 39 | Log.d("getStreamKeyResponse", "streamKey-->$streamKey") 40 | emit(NetworkAuthResponse.Success(streamKey)) 41 | } 42 | else if(response.code() == 401){ 43 | Log.d("getStreamKeyResponse", "401 FAIL") 44 | Log.d("getStreamKeyResponse", "message ->${response.message()}") 45 | emit(NetworkAuthResponse.Auth401Failure(Exception("Failed again"))) 46 | } 47 | else { 48 | Log.d("getStreamKeyResponse", "FAILED") 49 | Log.d("getStreamKeyResponse", "${response.code()}") 50 | Log.d("getStreamKeyResponse", "${response.message()}") 51 | 52 | emit(NetworkAuthResponse.Failure(Exception("Error! Please try again"))) 53 | } 54 | }.catch{cause -> 55 | Log.d("getStreamKeyResponse","EXCEPTION") 56 | Log.d("getStreamKeyResponse","message ->${cause.message}") 57 | Log.d("getStreamKeyResponse","cause ->${cause.cause}") 58 | Log.d("getStreamKeyResponse","cause ->${cause.javaClass}") 59 | 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/selfStreaming/surfaces/AutoFitSurfaceView.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.selfStreaming.surfaces 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.util.Log 6 | import android.view.SurfaceView 7 | import kotlin.math.roundToInt 8 | 9 | /** 10 | * A [SurfaceView] that can be adjusted to a specified aspect ratio and 11 | * performs center-crop transformation of input frames. 12 | */ 13 | class AutoFitSurfaceView @JvmOverloads constructor( 14 | context: Context, 15 | attrs: AttributeSet? = null, 16 | defStyle: Int = 0 17 | ) : SurfaceView(context, attrs, defStyle) { 18 | 19 | private var aspectRatio = 0f 20 | 21 | /** 22 | * Sets the aspect ratio for this view. The size of the view will be 23 | * measured based on the ratio calculated from the parameters. 24 | * 25 | * @param width Camera resolution horizontal size 26 | * @param height Camera resolution vertical size 27 | */ 28 | fun setAspectRatio(width: Int, height: Int) { 29 | require(width > 0 && height > 0) { "Size cannot be negative" } 30 | aspectRatio = width.toFloat() / height.toFloat() 31 | holder.setFixedSize(width, height) 32 | requestLayout() 33 | } 34 | 35 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 36 | super.onMeasure(widthMeasureSpec, heightMeasureSpec) 37 | val width = MeasureSpec.getSize(widthMeasureSpec) 38 | val height = MeasureSpec.getSize(heightMeasureSpec) 39 | if (aspectRatio == 0f) { 40 | setMeasuredDimension(width, height) 41 | } else { 42 | 43 | // Performs center-crop transformation of the camera frames 44 | val newWidth: Int 45 | val newHeight: Int 46 | val actualRatio = if (width > height) aspectRatio else 1f / aspectRatio 47 | if (width < height * actualRatio) { 48 | newHeight = height 49 | newWidth = (height * actualRatio).roundToInt() 50 | } else { 51 | newWidth = width 52 | newHeight = (width / actualRatio).roundToInt() 53 | } 54 | 55 | Log.d(TAG, "Measured dimensions set: $newWidth x $newHeight") 56 | setMeasuredDimension(newWidth, newHeight) 57 | } 58 | } 59 | 60 | companion object { 61 | private val TAG = AutoFitSurfaceView::class.java.simpleName 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/selfStreaming/util/URLParser.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.selfStreaming.util 2 | 3 | import java.net.URI 4 | import java.net.URISyntaxException 5 | 6 | class UrlParser private constructor( 7 | uri: URI, 8 | private val url: String 9 | ){ 10 | 11 | 12 | 13 | companion object { 14 | @Throws(URISyntaxException::class) 15 | fun parse(endpoint: String, requiredProtocol: Array): UrlParser { 16 | val uri = URI(endpoint) 17 | if (uri.scheme != null && !requiredProtocol.contains(uri.scheme.trim())) { 18 | throw URISyntaxException(endpoint, "Invalid protocol: ${uri.scheme}") 19 | } 20 | if (uri.userInfo != null && !uri.userInfo.contains(":")) { 21 | throw URISyntaxException(endpoint, "Invalid auth. Auth must contain ':'") 22 | } 23 | if (uri.host == null) throw URISyntaxException(endpoint, "Invalid host: ${uri.host}") 24 | if (uri.path == null) throw URISyntaxException(endpoint, "Invalid path: ${uri.host}") 25 | return UrlParser(uri, endpoint) 26 | } 27 | } 28 | 29 | var scheme: String = "" 30 | private set 31 | var host: String = "" 32 | private set 33 | var port: Int? = null 34 | private set 35 | var path: String = "" 36 | private set 37 | var query: String? = null 38 | private set 39 | var auth: String? = null 40 | private set 41 | 42 | init { 43 | val url = uri.toString() 44 | scheme = uri.scheme 45 | host = uri.host 46 | port = if (uri.port < 0) null else uri.port 47 | path = uri.path.removePrefix("/") 48 | if (uri.query != null) { 49 | val i = url.indexOf(uri.query) 50 | query = url.substring(if (i < 0) 0 else i) 51 | } 52 | auth = uri.userInfo 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/selfStreaming/websocket/RtmpSocket.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.selfStreaming.websocket 2 | 3 | /** 4 | * Socket implementation that accept: 5 | * - TCP 6 | * - TCP SSL/TLS 7 | * - UDP 8 | * - Tunneled HTTP 9 | * - Tunneled HTTPS 10 | */ 11 | abstract class RtmpSocket { 12 | 13 | abstract suspend fun connect() // todo: THIS IS FIRST ONE I CARE ABOUT 14 | abstract suspend fun close() 15 | 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/stream/customWebViews/VerticalWebView.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.stream.customWebViews 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.util.Log 6 | import android.view.GestureDetector 7 | import android.view.MotionEvent 8 | import android.webkit.JavascriptInterface 9 | import android.webkit.WebView 10 | 11 | 12 | class VerticalWebView: WebView { 13 | constructor(context: Context?) : super(context!!) {} 14 | constructor(context: Context?, attrs: AttributeSet?) : super( 15 | context!!, attrs 16 | ) { 17 | } 18 | var expanded = false 19 | init{ 20 | Log.d("onScrollDistanceDetection","INIT") 21 | } 22 | 23 | @JavascriptInterface 24 | fun postMessage(data: String) { 25 | Log.d("StreamQualitiesTesting", "Available stream qualities: $data") 26 | } 27 | 28 | 29 | var expandedMethod ={} 30 | var collapsedMethod={} 31 | var singleTapMethod={} 32 | 33 | 34 | 35 | private val myListener = object : GestureDetector.SimpleOnGestureListener() { 36 | 37 | override fun onDoubleTapEvent(motionEvent: MotionEvent): Boolean { 38 | 39 | 40 | when(motionEvent.action){ 41 | MotionEvent.ACTION_DOWN -> { 42 | if(!expanded){ 43 | expanded = !expanded 44 | expandedMethod() 45 | }else{ 46 | expanded = !expanded 47 | collapsedMethod() 48 | } 49 | } 50 | 51 | } 52 | 53 | 54 | 55 | return true 56 | } 57 | 58 | override fun onSingleTapConfirmed(e: MotionEvent): Boolean { 59 | Log.d("onSingleTapConfirmed","TAPPING") 60 | singleTapMethod() 61 | return super.onSingleTapConfirmed(e) 62 | } 63 | 64 | override fun onLongPress(e: MotionEvent) { 65 | super.onLongPress(e) 66 | Log.d("onLongPress","VERTICAL LONG PRESS") 67 | } 68 | 69 | 70 | 71 | } 72 | 73 | private val detector: GestureDetector = GestureDetector(context, myListener) 74 | 75 | override fun onTouchEvent(event: MotionEvent): Boolean { 76 | 77 | return detector.onTouchEvent(event).let { result -> 78 | performClick() 79 | true 80 | } 81 | } 82 | 83 | 84 | override fun performClick(): Boolean { 85 | 86 | return super.performClick() 87 | 88 | } 89 | 90 | 91 | 92 | }// end of the webview -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/stream/util/NetworkMonitoring.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.stream.util 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | import android.net.Network 6 | import android.util.Log 7 | import kotlinx.coroutines.flow.MutableStateFlow 8 | import kotlinx.coroutines.flow.asStateFlow 9 | import javax.inject.Inject 10 | 11 | class NetworkMonitoring @Inject constructor( 12 | private val context: Context 13 | ) { 14 | private val connectivityManager = context.getSystemService(ConnectivityManager::class.java) 15 | private val _networkStatus = MutableStateFlow(null) 16 | val networkStatus = _networkStatus.asStateFlow() 17 | 18 | 19 | init{ 20 | connectivityManager.registerDefaultNetworkCallback( 21 | object : ConnectivityManager.NetworkCallback() { 22 | override fun onAvailable(network : Network) { 23 | Log.d("NetworkMonitoring","Available") 24 | _networkStatus.tryEmit(true) 25 | // indicates that the device is connected to a new network that satisfies the capabilities 26 | // and transport type requirements specified in the NetworkRequest 27 | } 28 | 29 | override fun onLost(network: Network) { 30 | super.onLost(network) 31 | Log.d("NetworkMonitoring","Lost") 32 | _networkStatus.tryEmit(false) 33 | } 34 | 35 | } 36 | ) 37 | } 38 | 39 | 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/stream/util/domain/TokenCommandParsing.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.stream.util.domain 2 | 3 | import com.example.clicker.presentation.stream.util.TextCommands 4 | import com.example.clicker.presentation.stream.util.Token 5 | 6 | 7 | /** 8 | * TokenCommandParsing is the interface that acts as the API for all the methods needed to parse [Token] objects 9 | * that are created from the users commands 10 | * 11 | * @property checkForSlashCommands the function that will parse the [TextCommands] objects 12 | * */ 13 | interface TokenCommandParsing { 14 | 15 | /** 16 | * checkForSlashCommands is used to return a single [TextCommands] object. Which is used to determine if any commands should 17 | * be sent from the users messaging prompts. ie, /ban,/unban or /warn 18 | * 19 | * @param tokenList a list of [Token] objects meant to represent the individual words the user typed out 20 | * 21 | * @return [TextCommands] object 22 | * */ 23 | fun checkForSlashCommands(tokenList: List):TextCommands 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/presentation/stream/util/domain/TokenParsing.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.presentation.stream.util.domain 2 | 3 | import com.example.clicker.network.models.websockets.TwitchUserData 4 | import com.example.clicker.network.websockets.models.MessageToken 5 | import com.example.clicker.presentation.stream.util.TextCommands 6 | import kotlinx.coroutines.flow.StateFlow 7 | 8 | /** 9 | * TokenParsing is the interface that acts as the API for all the methods needed to parse [TextCommands] objects that the 10 | * user sends in chat 11 | * 12 | * @property runMonitorToken the function that will parse the [TextCommands] objects 13 | * */ 14 | interface TokenParsing { 15 | 16 | /** 17 | * this is a testing thing 18 | * 19 | * @param tokenCommand a [TextCommands] object the will determine what action the function will take 20 | * @param chatMessage a String representing what the user has typed and sent 21 | * @param isMod a Boolean determining if the user is a moderator or not 22 | * @param currentUsername a String representing the username of the currently logged in User 23 | * @param sendToWebSocket a function that will send the [chatMessage] to the websocket to be seen by other users 24 | * @param addMessageToListChats a function that will send the [chatMessage] to the UI so that is can be see user (not seen by others) 25 | * @param banUserSlashCommand a function called when a user types /ban 26 | * @param getUserId a function used to find a user in the chat session 27 | * @param unbanUserSlash a function called when a user types /unban 28 | * @param messageTokenList a List of [MessageToken] representing all the individual words the user has typed out 29 | * @param warnUser a function called when a user types /warn 30 | * */ 31 | fun runMonitorToken( 32 | tokenCommand: TextCommands, 33 | chatMessage:String, 34 | isMod: Boolean, 35 | currentUsername:String, 36 | sendToWebSocket:(String) ->Unit, 37 | addMessageToListChats:(TwitchUserData)->Unit, 38 | banUserSlashCommand:(String,String)->Unit, 39 | getUserId:((TwitchUserData)->Boolean)->String?, 40 | unbanUserSlash:(String)->Unit, 41 | messageTokenList: List, 42 | warnUser:(String,String,String) ->Unit, 43 | 44 | ) 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/rtmp/BitrateChecker.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.rtmp 2 | 3 | interface BitrateChecker { 4 | fun onNewBitrate(bitrate: Long) {} 5 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/rtmp/ConnectChecker.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.rtmp 2 | 3 | interface ConnectChecker:BitrateChecker { 4 | fun onConnectionStarted(url: String) 5 | fun onConnectionSuccess() 6 | fun onConnectionFailed(reason: String) 7 | fun onDisconnect() 8 | fun onAuthError() 9 | fun onAuthSuccess() 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/rtmp/GenericStream.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.rtmp 2 | 3 | import android.content.Context 4 | 5 | class GenericStream(context: Context, connectChecker: ConnectChecker) { 6 | 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/services/NetworkMonitorViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.services 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.util.Log 7 | import androidx.lifecycle.AndroidViewModel 8 | import androidx.lifecycle.ViewModel 9 | import dagger.hilt.android.lifecycle.HiltViewModel 10 | import dagger.hilt.android.qualifiers.ApplicationContext 11 | import javax.inject.Inject 12 | 13 | 14 | class NetworkMonitorViewModel ( 15 | application: Application 16 | ): AndroidViewModel(application) { 17 | 18 | override fun onCleared() { 19 | super.onCleared() 20 | val context:Application = getApplication() 21 | context.stopService(Intent(context, NetworkMonitorService::class.java)) 22 | Log.d("NetworkMonitorViewModel","cleared") 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) 12 | 13 | 14 | val md_theme_light_primary = Color(0xFFFFFFFF) 15 | val md_theme_light_onPrimary = Color(0xFF000000) 16 | val md_theme_light_secondary = Color(0xFF6650a4) 17 | val md_theme_light_onSecondary = Color(0xFFFFFFFF) 18 | 19 | 20 | val md_theme_dark_primary = Color(0xFF000000) 21 | val md_theme_dark_onPrimary = Color(0xFFFFFFFF) 22 | val md_theme_dark_secondary = Color(0xFF6650a4) 23 | val md_theme_dark_onSecondary = Color(0xFFFFFFFF) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.ui.theme 2 | 3 | 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.foundation.text.selection.LocalTextSelectionColors 7 | import androidx.compose.foundation.text.selection.TextSelectionColors 8 | import androidx.compose.material3.MaterialTheme 9 | import androidx.compose.material3.Typography 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.material3.darkColorScheme 12 | import androidx.compose.material3.lightColorScheme 13 | import androidx.compose.runtime.CompositionLocalProvider 14 | import androidx.compose.ui.text.TextStyle 15 | import androidx.compose.ui.unit.sp 16 | 17 | 18 | private val DarkColors = darkColorScheme( 19 | primary = md_theme_dark_primary, 20 | onPrimary = md_theme_dark_onPrimary, 21 | secondary = md_theme_dark_secondary, 22 | onSecondary = md_theme_dark_onSecondary 23 | ) 24 | 25 | private val LightColors = lightColorScheme( 26 | primary = md_theme_light_primary, 27 | onPrimary = md_theme_light_onPrimary, 28 | secondary = md_theme_light_secondary, 29 | onSecondary = md_theme_light_onSecondary, 30 | ) 31 | private val typography = Typography( 32 | headlineSmall = TextStyle( 33 | fontSize = 15.sp, 34 | ), 35 | headlineMedium = TextStyle( 36 | fontSize = 20.sp, 37 | ), 38 | headlineLarge = TextStyle( 39 | fontSize = 25.sp, 40 | ), 41 | ) 42 | 43 | @Composable 44 | fun AppTheme( 45 | useDarkTheme: Boolean = isSystemInDarkTheme(), 46 | content: @Composable() () -> Unit 47 | ) { 48 | // val colors = if (!useDarkTheme) { 49 | // LightColors 50 | // } else { 51 | // DarkColors 52 | // } 53 | // 54 | val colors = when{ 55 | (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) -> DarkColors 56 | (!useDarkTheme) -> LightColors 57 | else -> DarkColors 58 | } 59 | 60 | 61 | MaterialTheme( 62 | colorScheme = colors, 63 | typography = typography, 64 | content = content 65 | ) 66 | 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.ui.theme 2 | 3 | // import androidx.compose.material3.Typography 4 | 5 | // Set of Material typography styles to start with 6 | // val Typography = Typography( 7 | // bodyLarge = TextStyle( 8 | // fontFamily = FontFamily.Default, 9 | // fontWeight = FontWeight.Normal, 10 | // fontSize = 16.sp, 11 | // lineHeight = 24.sp, 12 | // letterSpacing = 0.5.sp 13 | // ) 14 | // /* Other default text styles to override 15 | // titleLarge = TextStyle( 16 | // fontFamily = FontFamily.Default, 17 | // fontWeight = FontWeight.Normal, 18 | // fontSize = 22.sp, 19 | // lineHeight = 28.sp, 20 | // letterSpacing = 0.sp 21 | // ), 22 | // labelSmall = TextStyle( 23 | // fontFamily = FontFamily.Default, 24 | // fontWeight = FontWeight.Medium, 25 | // fontSize = 11.sp, 26 | // lineHeight = 16.sp, 27 | // letterSpacing = 0.5.sp 28 | // ) 29 | // */ 30 | // ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/util/AndroidVersion.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.util 2 | 3 | import android.os.Build 4 | 5 | object AndroidVersion { 6 | /** Android 7.1 **/ 7 | inline val ATLEAST_API25_N_MR1 get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 8 | inline val ATMOST_API25_N_MR1 get() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 9 | 10 | /** Android 8 **/ 11 | inline val ATLEAST_API26_O get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O 12 | inline val ATMOST_API26_O get() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.O 13 | 14 | /** Android 8.1 **/ 15 | inline val ATLEAST_API27_O_MR1 get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 16 | inline val ATMOST_API27_O_MR1 get() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1 17 | 18 | /** Android 9 **/ 19 | inline val ATLEAST_API28_P get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P 20 | inline val ATMOST_API28_P get() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.P 21 | 22 | /** Android 10 **/ 23 | inline val ATLEAST_API29_Q get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q 24 | inline val ATMOST_API29_Q get() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q 25 | 26 | /** Android 11 **/ 27 | inline val ATLEAST_API30_R get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R 28 | inline val ATMOST_API30_R get() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.R 29 | 30 | /** Android 12 **/ 31 | inline val ATLEAST_API31_S get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S 32 | inline val ATMOST_API31_S get() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.S 33 | 34 | /** Android 12L **/ 35 | inline val ATLEAST_API32_S_V2 get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S_V2 36 | inline val ATMOST_API32_S_V2 get() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2 37 | 38 | /** Android 13 **/ 39 | inline val ATLEAST_API33_T get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU 40 | inline val ATMOST_API33_T get() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU 41 | 42 | /** Android 14 **/ 43 | inline val ATLEAST_API34_U get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE 44 | inline val ATMOST_API34_U get() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/util/CustomRetrofitCallAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.util 2 | 3 | import java.lang.reflect.Type 4 | import retrofit2.CallAdapter 5 | import retrofit2.Retrofit 6 | 7 | class CustomRetrofitCallAdapter : CallAdapter.Factory() { 8 | 9 | override fun get( 10 | returnType: Type, 11 | annotations: Array, 12 | retrofit: Retrofit 13 | ): CallAdapter<*, *>? { 14 | TODO("Not yet implemented") 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/util/LogWrap.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.util 2 | 3 | import android.util.Log 4 | /** 5 | * A wrapper class for the [Log](android.util.Log) class. 6 | * 7 | * - This class is used so ProGuard can identify all the logs and remove them for the release version 8 | * 9 | * 10 | */ 11 | class LogWrap { 12 | companion object { 13 | fun d(tag: String, message: String) { 14 | Log.d(tag, message) 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/util/SortedEmoteMap.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.util 2 | 3 | class SortedEmoteMap { 4 | 5 | fun hashCodeCyclicShift(s:String):Int{ 6 | var h = 0 7 | for (i in s.indices) { 8 | h = (h shl 5) or (h ushr 27) // 5-bit cyclic shift of the running sum 9 | h += s[i].code // add in next character 10 | } 11 | return h 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/util/Util.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.util 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.ContextWrapper 6 | import android.util.Log 7 | import kotlinx.coroutines.flow.Flow 8 | import kotlinx.coroutines.flow.map 9 | 10 | internal fun Context.findActivity(): Activity { 11 | var context = this 12 | while (context is ContextWrapper) { 13 | if (context is Activity) return context 14 | context = context.baseContext 15 | } 16 | throw IllegalStateException("Permissions should be called in the context of an Activity") 17 | } 18 | 19 | fun logCoroutineInfo(tag: String, msg: String) { 20 | Log.d(tag, "Running on: [${Thread.currentThread().name}] | $msg") 21 | } 22 | 23 | fun Flow.mapWithRetry( 24 | action: suspend (T) -> R, 25 | predicate: suspend (R, attempt: Int) -> Boolean 26 | ) = map { data -> 27 | var attempt = 0L 28 | var shallRetry: Boolean 29 | var lastValue: R? = null 30 | do { 31 | Log.d("mapWithRetryRetry","RETRY NUMBER -> $attempt") 32 | val tr = action(data) 33 | shallRetry = predicate(tr, (++attempt).toInt()) 34 | if (!shallRetry) lastValue = tr 35 | } while (shallRetry) 36 | return@map lastValue 37 | } 38 | fun replaceChannelName(original: String, newChannelName: String): String { 39 | val regex = Regex("channel=([^&]+)") 40 | return regex.replace(original) { 41 | "channel=$newChannelName" 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/util/objectMothers/IndividualAutoModSettingsDataObjectMother.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.util.objectMothers 2 | 3 | import com.example.clicker.network.models.twitchStream.IndividualAutoModSettings 4 | 5 | 6 | class IndividualAutoModSettingsDataObjectMother private constructor() { 7 | 8 | companion object { 9 | private var individualAutoModSettings= IndividualAutoModSettings( 10 | broadcasterId ="", 11 | moderatorId ="", 12 | overallLevel=null, 13 | sexualitySexOrGender=0, 14 | raceEthnicityOrReligion=0, 15 | sexBasedTerms=0, 16 | disability=0, 17 | aggression=0, 18 | misogyny=0, 19 | bullying=0, 20 | swearing=0 21 | ) 22 | 23 | fun build():IndividualAutoModSettings{ 24 | return individualAutoModSettings 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/clicker/util/objectMothers/LoggedInUserDataObjectMother.kt: -------------------------------------------------------------------------------- 1 | package com.example.clicker.util.objectMothers 2 | 3 | import com.example.clicker.network.models.websockets.LoggedInUserData 4 | 5 | class LoggedInUserDataObjectMother private constructor() { 6 | 7 | companion object { 8 | private var loggedInUserData = LoggedInUserData( 9 | color = null, 10 | displayName = "#000000", 11 | sub = false, 12 | mod = false 13 | 14 | ) 15 | fun addColor(color: String) = apply { 16 | loggedInUserData = loggedInUserData.copy( 17 | color = color 18 | ) 19 | } 20 | fun addDisplayName(displayName: String) = apply { 21 | loggedInUserData = loggedInUserData.copy( 22 | displayName = displayName 23 | ) 24 | } 25 | fun addSub(sub: Boolean) = apply { 26 | loggedInUserData = loggedInUserData.copy( 27 | sub = sub 28 | ) 29 | } 30 | fun addMod(mod: Boolean) = apply { 31 | loggedInUserData = loggedInUserData.copy( 32 | mod = mod 33 | ) 34 | } 35 | fun build(): LoggedInUserData { 36 | return loggedInUserData 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/alert_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/autorenew_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/back_arrow.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ban_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_announcement_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_backspace_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_category_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_check_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_close_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_fast_forward_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_fast_rewind_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_hourglass_empty_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_keyboard_arrow_up_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_location_on_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_new_releases_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_pause_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_play_arrow_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_privacy_policy.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_question_mark_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_rocket_launch_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_settings_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_star_outline.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/channel_emotes_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/clear_chat_alt_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/clear_chat_on_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/delete_outline_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/edit_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/emote_face_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/error_outline_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/favorite_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/flip_camera_android_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gift.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_send.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 19 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/keyboard_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/keyboard_arrow_down_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/lock_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/lock_open_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/menu_open_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/mod_view_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/moderator_secondary_color.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/moderator_white.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/person_outline_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/push_pin_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shared_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sparkle_awesome_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/time_out_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/videogame_asset.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/visibility_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/world_emotes_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/camera_ndk_native_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_logout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_mini_game.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_mod_channels.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_new_user.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_self_streaming.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/new_testin_stream_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/verify_links_last_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-hdpi/verify_links_last_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/verify_links_second_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-hdpi/verify_links_second_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/verify_links_start_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-hdpi/verify_links_start_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | #FF0000 11 | #0000FF 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #DC3D3D 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/splashScreen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |