├── .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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
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 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/file_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/method.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/clicker/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.clicker
2 |
3 | import com.example.clicker.network.clients.TwitchClient
4 | import com.example.clicker.network.domain.TwitchRepo
5 | import com.example.clicker.network.models.twitchRepo.FollowedLiveStreams
6 | import com.example.clicker.util.Response
7 | import kotlinx.coroutines.flow.first
8 | import kotlinx.coroutines.test.runTest
9 | import org.junit.Assert.*
10 | import org.junit.Test
11 | import org.junit.runner.RunWith
12 | import org.mockito.Mock
13 | import org.mockito.Mockito
14 | import org.mockito.junit.MockitoJUnitRunner
15 |
16 | /**
17 | * Example local unit test, which will execute on the development machine (host).
18 | *
19 | * See [testing documentation](http://d.android.com/tools/testing).
20 | */
21 |
22 | @RunWith(MockitoJUnitRunner::class)
23 | class ExampleUnitTest {
24 |
25 | @Mock
26 | lateinit var twitchClient: TwitchClient
27 | lateinit var twitchRepository: TwitchRepo
28 |
29 | @Test
30 | fun addition_isCorrect() = runTest {
31 | /* Given */
32 | val testingCode = retrofit2.Response.success(
33 | 200,
34 | FollowedLiveStreams(data = listOf())
35 |
36 | )
37 |
38 | // twitchRepository = TwitchRepoImpl(twitchClient)
39 | // todo: I THINK I NEED TO DO Response.success() WITH A CODE OF 200
40 | // Mockito.`when`(twitchClient.getFollowedStreams("", "", "")).thenReturn(testingCode)
41 | //
42 | // /* When */
43 | // val getFollowedStreams = twitchRepository.getFollowedLiveStreams("", "", "").first()
44 | //
45 | //
46 | // /* Then */
47 | // // Then check it's the expected item
48 | // assertEquals(Response.Loading, getFollowedStreams)
49 | // Log.d("TESTINGEXCEPTIONS",getFollowedStreams.toString())
50 | //
51 | // assertEquals(true, testingCode.isSuccessful)
52 | }
53 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/example/clicker/network/repository/util/EmoteParsingTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.clicker.network.repository.util
2 |
3 | class EmoteParsingTest {
4 |
5 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/example/clicker/network/repository/util/SortedEmoteMapTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.clicker.network.repository.util
2 |
3 | import com.example.clicker.util.SortedEmoteMap
4 | import org.junit.Test
5 | import org.junit.Assert
6 |
7 | class SortedEmoteMapTest {
8 |
9 | @Test
10 | fun hash_code_test(){
11 | /**GIVEN*/
12 | val givenString ="testing"
13 | val secondGivenString = "testing"
14 | val sortedEmoteMap = SortedEmoteMap()
15 |
16 | /**WHEN*/
17 | val hashCodeOne = sortedEmoteMap.hashCodeCyclicShift(givenString)
18 | val hashCodeTwo = sortedEmoteMap.hashCodeCyclicShift(secondGivenString)
19 |
20 |
21 | /**THEN*/
22 | Assert.assertEquals(hashCodeOne,hashCodeTwo)
23 |
24 | }
25 |
26 | @Test
27 | fun hash_normal_code_test(){
28 | /**GIVEN*/
29 | val givenString ="testing"
30 | val secondGivenString = "testing"
31 |
32 | /**WHEN*/
33 | val hashCodeOne = givenString.hashCode()
34 | val hashCodeTwo = secondGivenString.hashCode()
35 |
36 |
37 | /**THEN*/
38 | Assert.assertEquals(hashCodeOne,hashCodeTwo)
39 |
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/example/clicker/presentation/stream/StreamViewModelTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.clicker.presentation.stream
2 |
3 | import com.example.clicker.domain.TwitchDataStore
4 | import com.example.clicker.network.domain.TwitchStream
5 | import com.example.clicker.network.domain.TwitchSocket
6 | import com.example.clicker.network.models.websockets.RoomState
7 | import com.example.clicker.network.websockets.models.MessageType
8 | import com.example.clicker.util.objectMothers.TwitchUserDataObjectMother
9 | import kotlinx.coroutines.Dispatchers
10 | import kotlinx.coroutines.flow.MutableStateFlow
11 | import kotlinx.coroutines.flow.asStateFlow
12 | import kotlinx.coroutines.test.StandardTestDispatcher
13 | import kotlinx.coroutines.test.TestDispatcher
14 | import kotlinx.coroutines.test.UnconfinedTestDispatcher
15 | import kotlinx.coroutines.test.resetMain
16 | import kotlinx.coroutines.test.runTest
17 | import kotlinx.coroutines.test.setMain
18 | import org.junit.Assert
19 | import org.junit.Rule
20 | import org.junit.Test
21 | import org.junit.rules.TestWatcher
22 | import org.junit.runner.Description
23 | import org.junit.runner.RunWith
24 | import org.mockito.Mockito
25 | import org.mockito.junit.MockitoJUnitRunner
26 |
27 | class StreamViewModelTestDispatcherRule(
28 | val testDispatcher: TestDispatcher = StandardTestDispatcher(),
29 | ) : TestWatcher() {
30 | override fun starting(description: Description) {
31 | Dispatchers.setMain(testDispatcher)
32 | }
33 |
34 | override fun finished(description: Description) {
35 | Dispatchers.resetMain()
36 | }
37 | }
38 |
39 | @RunWith(MockitoJUnitRunner::class)
40 | class StreamViewModelTest {
41 |
42 |
43 | @get:Rule
44 | val mainDispatcherRule = StreamViewModelTestDispatcherRule()
45 |
46 |
47 |
48 | @Test
49 | fun testingTHingers()= runTest{
50 | val dispatcher = mainDispatcherRule.testDispatcher
51 |
52 |
53 | /**THEN*/
54 |
55 | /**THEN*/
56 |
57 | Assert.assertEquals(1, 1)
58 | }
59 |
60 |
61 |
62 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/example/clicker/presentation/stream/util/ScannerTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.clicker.presentation.stream.util
2 |
3 | import kotlinx.coroutines.flow.first
4 | import kotlinx.coroutines.test.runTest
5 | import org.junit.Assert
6 | import org.junit.Test
7 |
8 | class ScannerTest() {
9 |
10 | // val underTest = Scanner()
11 | //
12 | // @Test
13 | // fun scanTokens_Normal_message_with_username() = runTest {
14 | // /**GIVEN*/
15 | // val chatMessage = "LOL it do be like that sometimes @Peanutdude33"
16 | // val expectedNumberOfTextTokens = 7
17 | // val expectedNumberOfUsernameToken = 1
18 | //
19 | //
20 | // /**WHEN*/
21 | // underTest.scanTokens(chatMessage)
22 | //
23 | // var actualNumberOfTextTokens = 0
24 | // var actualNumberOfUsernameTokens = 0
25 | //
26 | // for (token in underTest.tokenList){
27 | // if(token.tokenType == TokenType.TEXT){
28 | // actualNumberOfTextTokens++
29 | // }
30 | // if(token.tokenType == TokenType.USERNAME){
31 | // actualNumberOfUsernameTokens++
32 | // }
33 | // }
34 | //
35 | //
36 | //
37 | // /**THEN*/
38 | // Assert.assertEquals(expectedNumberOfUsernameToken, actualNumberOfUsernameTokens)
39 | // Assert.assertEquals(actualNumberOfTextTokens, expectedNumberOfTextTokens)
40 | //
41 | // }
42 |
43 |
44 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/example/clicker/presentation/stream/util/TextParsingTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.clicker.presentation.stream.util
2 |
3 |
4 | import androidx.compose.ui.text.input.TextFieldValue
5 | import kotlinx.coroutines.test.runTest
6 | import org.junit.Assert
7 | import org.junit.Test
8 |
9 | class TextParsingTest {
10 | val underTest:TextParsing =TextParsing()
11 |
12 | @Test
13 | fun clickSlashCommandTextAutoChange_success() = runTest {
14 | /**GIVEN*/
15 | val clickedOnCommand ="/ban"
16 | val expectedResult = "/ban "
17 |
18 |
19 | /**WHEN*/
20 | underTest.parsingMethod( // represents the user typing in the text field
21 | textFieldValue = TextFieldValue("/"),
22 | allChatters = listOf("")
23 | )
24 | underTest.clickSlashCommandTextAutoChange(clickedOnCommand)
25 |
26 | /**THEN*/
27 | val actualValue = underTest.textFieldValue.value.text
28 | val expectedValue = expectedResult
29 |
30 |
31 |
32 | Assert.assertEquals(expectedValue, actualValue)
33 |
34 | }
35 | @Test
36 | fun clickUsernameAutoTextChange_success() = runTest {
37 | /**GIVEN*/
38 | val clickedOnUsername ="Bob3324"
39 | val expectedResult = "Bob3324 "
40 |
41 |
42 | /**WHEN*/
43 | underTest.parsingMethod( // represents the user typing in the text field
44 | textFieldValue = TextFieldValue("@"),
45 | allChatters = listOf("Bob3324","MoreFakeTest444","fakeTestName55t")
46 | )
47 | underTest.clickUsernameAutoTextChange(clickedOnUsername)
48 |
49 | /**THEN*/
50 | val actualValue = underTest.textFieldValue.value.text
51 | val expectedValue = expectedResult
52 |
53 |
54 |
55 | Assert.assertEquals(expectedValue, actualValue)
56 |
57 | }
58 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/example/clicker/utility/UtilityTests.kt:
--------------------------------------------------------------------------------
1 | package com.example.clicker.utility
2 |
3 | import com.example.clicker.network.clients.TopGame
4 | import com.example.clicker.network.models.twitchRepo.StreamData
5 | import com.example.clicker.presentation.selfStreaming.util.UrlParser
6 | import com.example.clicker.util.replaceChannelName
7 | import org.junit.Assert
8 | import org.junit.Test
9 |
10 | class UtilityTests {
11 |
12 | @Test
13 | fun testing_replace_channelName_on_url() {
14 | // occurs when an attempt is made to convert a string with an incorrect format to a numeric value
15 | val url = "https://player.twitch.tv/?channel=channelName&controls=false&muted=false&parent=modderz"
16 | val channelNameReplacement = "Bob443sal"
17 | val expectedUrl = "https://player.twitch.tv/?channel=$channelNameReplacement&controls=false&muted=false&parent=modderz"
18 | val actualUrl = replaceChannelName(url,channelNameReplacement)
19 |
20 |
21 | Assert.assertEquals(expectedUrl, actualUrl)
22 | }
23 |
24 | @Test
25 | fun url_parser() {
26 | // occurs when an attempt is made to convert a string with an incorrect format to a numeric value
27 | //url of the stream like: protocol://ip:port/application/streamName
28 | val EXPECTED_HOST ="sfo.contribute.live-video.net"
29 | val url ="rtmp://sfo.contribute.live-video.net/app/live_user_123456789?bandwidthtest=true"
30 | val validSchemes = arrayOf("rtmp", "rtmps", "rtmpt", "rtmpts")
31 | val parser = UrlParser.parse(url, validSchemes)
32 | val testing = parser.scheme.startsWith("rtmp")
33 |
34 |
35 |
36 | Assert.assertEquals(EXPECTED_HOST, parser.host)
37 | }
38 |
39 |
40 |
41 |
42 | }
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id 'com.android.application' version '8.0.1' apply false
4 | id 'com.android.library' version '8.0.1' apply false
5 |
6 | id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
7 | id 'com.google.dagger.hilt.android' version '2.44' apply false
8 | id 'com.android.test' version '8.0.1' apply false
9 | // id 'org.jetbrains.kotlin.kapt' version '1.8.10' apply false
10 |
11 |
12 |
13 |
14 | }
15 |
16 |
17 | //subprojects {
18 | // ext.initScriptRun = false
19 | // println("------------RUNNING subprojects TASK------------------")
20 | // println(project.buildDir.absolutePath + "/compose_compiler")
21 | //
22 | // if (!ext.initScriptRun) {
23 | //
24 | // println("------------RUNNING CONDITIONAL------------------")
25 | //
26 | // if (project.findProperty("composeCompilerReports") == "true") {
27 | //
28 | // freeCompilerArgs += [
29 | // "-P",
30 | // "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
31 | // project.buildDir.absolutePath + "/compose_compiler"
32 | // ]
33 | // freeCompilerArgs += [
34 | // "-P",
35 | // "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
36 | // project.buildDir.absolutePath + "/compose_compiler"
37 | // ]
38 | // }
39 | // }
40 | //
41 | //}
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thePlebDev/Clicker/e96f4c5f0762c7d0cd63c33a9cb52dfd3e98c47b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Jul 08 09:43:39 ADT 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/macrobenchmark/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/macrobenchmark/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.test'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | namespace 'com.buildinginpublic.macrobenchmark'
8 | compileSdk 34
9 |
10 | compileOptions {
11 | sourceCompatibility = JavaVersion.VERSION_1_8
12 | targetCompatibility = JavaVersion.VERSION_1_8
13 | }
14 |
15 | kotlinOptions {
16 | jvmTarget = "1.8"
17 | }
18 |
19 | defaultConfig {
20 | minSdk 24
21 | targetSdk 34
22 |
23 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
24 | }
25 |
26 | buildTypes {
27 | // This benchmark buildType is used for benchmarking, and should function like your
28 | // release build (for example, with minification on). It's signed with a debug key
29 | // for easy local/CI testing.
30 | benchmark {
31 | debuggable = false
32 | signingConfig = debug.signingConfig
33 | matchingFallbacks = ["release"]
34 | }
35 |
36 | }
37 |
38 | targetProjectPath = ":app"
39 | experimentalProperties["android.experimental.self-instrumenting"] = true
40 | }
41 |
42 | dependencies {
43 | implementation 'androidx.test.ext:junit:1.1.5'
44 | implementation 'androidx.test.espresso:espresso-core:3.5.1'
45 | implementation 'androidx.test.uiautomator:uiautomator:2.2.0'
46 | implementation 'androidx.benchmark:benchmark-macro-junit4:1.3.0-alpha01'
47 | }
48 |
49 | androidComponents {
50 | beforeVariants(selector().all()) {
51 | enabled = buildType == "benchmark"
52 | }
53 | }
--------------------------------------------------------------------------------
/macrobenchmark/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/macrobenchmark/src/main/java/com/buildinginpublic/macrobenchmark/ExampleStartupBenchmark.kt:
--------------------------------------------------------------------------------
1 | package com.buildinginpublic.macrobenchmark
2 |
3 | import androidx.benchmark.macro.CompilationMode
4 | import androidx.benchmark.macro.FrameTimingMetric
5 | import androidx.benchmark.macro.StartupMode
6 | import androidx.benchmark.macro.StartupTimingMetric
7 | import androidx.benchmark.macro.junit4.MacrobenchmarkRule
8 | import androidx.test.ext.junit.runners.AndroidJUnit4
9 | import androidx.test.uiautomator.By
10 | import androidx.test.uiautomator.Direction
11 | import org.junit.Rule
12 | import org.junit.Test
13 | import org.junit.runner.RunWith
14 |
15 | /**
16 | * This is an example startup benchmark.
17 | *
18 | * It navigates to the device's home screen, and launches the default activity.
19 | *
20 | * Before running this benchmark:
21 | * 1) switch your app's active build variant in the Studio (affects Studio runs only)
22 | * 2) add `` to your app's manifest, within the `` tag
23 | *
24 | * Run this benchmark from Studio to see startup measurements, and captured system traces
25 | * for investigating your app's performance.
26 | */
27 | @RunWith(AndroidJUnit4::class)
28 | class ExampleStartupBenchmark {
29 | @get:Rule
30 | val benchmarkRule = MacrobenchmarkRule()
31 |
32 | @Test
33 | fun startup() = benchmarkRule.measureRepeated(
34 | packageName = "elliott.software.clicker",
35 | metrics = listOf(StartupTimingMetric()),
36 | iterations = 5,
37 | startupMode = StartupMode.COLD
38 | ) {
39 | pressHome()
40 | startActivityAndWait()
41 | }
42 |
43 | @Test
44 | fun scroll() = benchmarkRule.measureRepeated(
45 | packageName = "elliott.software.clicker",
46 | metrics = listOf(FrameTimingMetric()),
47 | iterations = 5,
48 | startupMode = StartupMode.COLD
49 | ) {
50 | pressHome()
51 | startActivityAndWait()
52 | val list = device.findObject(By.res("streamersListLoading"))
53 |
54 | // list.setGestureMargin(device.displayWidth/5)
55 | list.fling(Direction.DOWN)
56 | }
57 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 | rootProject.name = "Clicker"
16 | include ':app'
17 | include ':macrobenchmark'
18 |
--------------------------------------------------------------------------------