Hello Android users!
We just pushed update 3.9.8 out to 100% of beta users this afternoon. This is a big one, so strap in for the expanded release notes!
We'll continue to monitor and roll out to production over the next few days. If you're interested in joining the beta and providing feedback to new updates, you can sign up in the Google Play Store on our app page.
SSAI:
Architecture:
Created AdRepository.kt to handle ad related logic attached to video playback like analytics.
Created PlayerRepository.kt and moved good chunk of logic into it from PlayerViewModel.kt.
Removed lower level module project dependencies that are transitively exposed by other dependencies from app/build.gradle, core/coreliveui/build.gradle, core/coreplayerui/build.gradle, core/corevodui/build.gradle, legacy/build.gradle.
Moved image loading dependency to :core:coreui.
Created new build flavorDimension called style.
This allows us to maintain our existing assets as well as add new ones based on a certain style while the underlying code references the same asset names like ic_launcher. This removes the need for maintaining extra branches for brand level changes and provides opportunities to work ahead on other style based changes like the specific app icons.
UI:
Added video_ad_fragment_layout to fragment_video.xml.
Added logic to AdCoordinator.kt to support ad viewing.
Created VideoCoordinator.kt
Added logic to VideoFragment.kt & VideoCoordinator.kt to support ad viewing.
Added PaidContentAdFree MessagingType to WelcomeFragment.kt & SubscribeFragment.kt.
Updated UpsellCoordinator’s upsellAds function with new messaging types.
Disabled Ad CTA when click through link is null
Updated played position logic to work with free users.
Updated seek logic around ad breaks and back navigation from ad views.
Data:
Renamed AdPostion.kt to AdRollType.kt
Added AdEventAdBreakStarted, AdEventAdBreakCompleted & AdEventAdClicked events for analytics.
Created AdBreakAd.kt to represent an ad within the ad break.
Created AdInterruption.kt to represent an ad to be played as an interrupt event to video playback due to a user seeking passed an ad marker.
Created AdState.kt as a sealed class consisting of AdStateDefault, AdStateContinuing, AdStateComplete & AdStateFailed
Created AdTimeline.kt to represent an ad within an ad break with formatted data and information about it’s ad break.
Added clickThroughLink property to AdBreakAd.kt
Extensions:
Created splitDuration function in StringExtensions.kt to handle time format from backend.
Legacy Code Deletions:
Deleted AdViewInterface.kt, RTAdManager.kt, PrioritizedRTAdManager.kt, VideoAnalytcsProvider.kt & VideoAnalyticsManager.kt.
Removed legacy WatchtimeBeatListener.kt, AnalyticsBeatListener.kt & PlayerAnalyticsListener.kt.
Functionality:
Heartbeats now stop on ad break starts and start on ad break completion.
Ad breaks are now properly tracked by analytics for anon and free users.
Added logic to show an ad overlay for non-premium users when ads come up during video playback.
Ad playback can be paused/resumed.
Regular playback controls are disabled during ad playback.
Ad playback seekbar is disabled for user interaction.
Seeking passed an ad break or group of ad breaks triggers an ad interruption showing the most recent ad break and then resuming to the original seek to position.
Added upsellAds function to UpsellCoordinator.kt to be used when an ad upsell has been triggered.
Dedicated Networking Layer
Goal:
Decouple existing networking layer from UI layer. Build components to allow other features to decouple their networking logic. Improve error handling. Reduce duplicated code. Increase performance.
Architecture:
Created :core:corefeature, :core:coreio, :core:coretransceiver, :core:coreuser:coreapi & :features:billing modules.
:core:corefeature includes :core:coreui, :core:coreauth, :core:coreuser, :core:storage, :core:coretransceiver, :core:coreanalytics.
:core:corefeature will be implemented by all modularized features moving forward.
Created FeatureViewModel to allows for quick access to the AuthRepo & UserRepo.
Currently LoginViewModel & ForgotPasswordViewModel inherit from this.
Moving forward all feature ViewModels will inherit from FeatureViewModel.
UI:
Created AuthCoordinator.kt as an abstract class to be implemented by Login/Sign up related feature coordinators.
Created CreateAccountCoordinator.kt which extends AuthCoordinator.kt.
This allows for error handling based on different types of errors that can occur during account creation.
Refactored ChatFragment & ChatViewModel to utilize new approach for reading the User and handling anonymous users.
Added member_required_title_chat string resource to :core:coreui for upselling RTTV chat for anonymous users.
Added PaidContentChat to MessagingTypes enum in WelcomeFragment.kt.
Proper messaging now shows when anonymous user taps the RTTV chat EditText, previously was default messaging.
Created EditProfileViewModel.
Fixed crash when opening image picker for updating the profile picture on the EditProfileFragment on Android 11 + due to how we requested all apps on device based on intents MediaStore.ACTION_IMAGE_CAPTURE & Intent.ACTION_GET_CONTENT.
Android decided that on Android 11 + that apps on a user’s device were considered personal information.
Upload of image fails with 400 InvalidParameter, but the app no longer crashes. Will work with backend to resolve.
Created WelcomeCoordinator to handle UI interactions on the WelcomeFragment.
Created LoginCoordinator to handle UI interactions on the LoginFragment.
Created CreateAccountViewModel.
Created ForgotPasswordCoordinator to handle UI interactions on ForgotPasswordFragment.
Created ForgotPasswordState sealed class with children; ForgotPasswordDefault, ForgotPasswordAttempting, ForgotPasswordComplete & ForgotPasswordFailure.
Created ForgotPasswordViewModel.
Created SettingsViewModel.
Created DebugRtSettingsViewModel.
Created UpdateEmailViewModel.
Created UpdatePasswordViewModel.
Created UpdatePasswordState sealed class with children; UpdatePasswordDefault, UpdatePasswordAttempting, UpdatePasswordComplete, UpdatePasswordFailure.
Data:
Updated ErrorResponse.kt to include the errors property, which is populated in cases where multiple errors occur from a single call, like create account.
Created HttpApiException to be a base Exception type for RT APIs.
Created BadRequestException, UnauthenticatedException, UnauthorizedException, NotFoundException, RequestTimeoutException, ConflictException, ContentGoneException, TooManyRequestException, InternalServerException, BadGatewayException, GatewayTimeoutException, EmptyExceptions to allow for more manageable consumption of common network, parsing, storage errors.
Created ApiResponse sealed class, meant to replace legacy NetworkResource.
Has 3 children; Loading, Success, Failure.
ApiResponse is meant to be returned only from a Transceiver
(networking) to the repository layer.
Created EmptyResponse
to handle empty responses.
Created ErrorResponse
to wrap information regarding why and API call failed whether locally or from request/server problems.
Created IoResponse
to act as an object to be returned from either the Transceiver
(network) layer or Local Storage
layer to the Repository layer.
This allows us to completely remove the need for UI components to directly require knowledge(depend upon) of Networking related models/logic.
The screen should only care about getting data or how to handle an error.
Created IoError
to handle wrapping errors that may occur in the networking/storage layers and make it consumable to the UI components.
Extensions:
Updated ErrorResponseExtensions’s toIoError()
function to sort the addition of multiple errors.
Created HttpExceptionExtensions to easily convert response codes into HttpApiException.
Created ErrorResponseExtensions to easily allow for conversion from ErrorResponse to IoError.
Created ThrowableExtensions to easily convert a Throwable object to our ApiException.
Networking:
Created BearerTokenInterceptor to handle appending the Authorization header to our API calls.
Created Authenticator to handle challenges/refreshes to 401 (Unauthorized) response codes.
Created ApiRepository to handle single instance setup of our networking related configuration and client setup(timeouts, JSON settings, interceptors, etc.).
Created BaseTransceiver to be implemented by our current networking client features.
Created NetworkTransceiver which extends BaseTransceiver and handles making our requests using Kotlin Flows or regular suspending functions.
The Flow wrapped request logic handles updating(emitting) our ApiResponse(Loading, Complete, Failure) states based on the API interaction.
This easily allows us to modify how we handle response from our APIs in singular place.
Removed NetworkErrorInterceptor.kt.
Errors are now handled by the Repository layer and transformed into consumable information for the ViewModel layer to update the UI accordingly.
Updated OkHttp from 4.9.0 to 4.10.0
Auth:
Created AuthenticationRepository to act as Base Repository for our various auth options.
Created OauthRepository to handle standard email/password functionality as well as token related logic(revoke, etc.).
Created GoogleAuthRepository, child of AuthenticationRepository which handles our Google related authentication logic.
Created FacebookAuthRepository, child of AuthenticationRepository which handles our Facebook related authentication logic.
Created AuthStorage to handle read/write of access token/refresh tokens.
Created AuthTransceiver as an abstract implementation of our NetworkTransceiver class, meant to be used with the networked RT API calls for Authentication.
Created OauthApi
Created OauthTransceiver, child of AuthTransceiver.
Created GoogleAuthApi
Created GoogleTransceiver, child of AuthTransceiver.
Created FacebookAuthApi
Created FacebookTransceiver, child of AuthTransceiver.
Created BillingApi
Removed ChatAuthenticationInterceptor.
If you made it through all of that, bravo! Next up we're going to be fixing the Report Bug button and getting that autoplay setting to remember what you set it as before leaving the Settings page. If you have any questions, I'll try to answer what I can here, or you can reach out to our wonderful Support team at support.roosterteeth.com.
Thanks everyone!