UserController.java


Overview

`UserController` is a core class that manages and mediates user interactions with the media player, specifically an ExoPlayer instance. It acts as the bridge between the player UI and the underlying playback engine, handling commands such as play/pause toggling, seeking, subtitle toggling, and ad interaction. The class uses Android's data binding (`BaseObservable`) to expose observable playback states and media metadata to the UI layer, ensuring real-time synchronization of UI components with the player's current status.

Primarily, it serves as a **playback control interface** and listens to player events to update UI-bound properties, while also processing user input events from UI controls (e.g., seek bar interactions). It supports specialized control states to represent different user interaction modes such as normal playback, custom seeking, and options menu navigation.

By encapsulating playback control logic and state management, `UserController` enables a clean separation between UI components and business logic, facilitating maintainable and testable code within a media playback application.


Class: UserController

**Package:** `com.tubitv.media.bindings`

**Extends:** `BaseObservable` **Implements:**


Constants

Name

Description

`NORMAL_CONTROL_STATE`

State representing normal playback controls (value `1`).

`CUSTOM_SEEK_CONTROL_STATE`

State for custom seek control entered by long-press seek (value `2`).

`EDIT_CUSTOM_SEEK_CONTROL_STATE`

State for editing custom seek during long press (value `3`).

OPTIONS_CONTROL_STATE

State when UI focuses on options like captions (value `4`).

`DEFAULT_FREQUENCY`

Frequency in milliseconds for progress update timer (1000 ms).


Observable Fields (Data Binding Properties)

These fields expose playback and media metadata state to the UI layer:

Field Name

Type

Description

`playerPlaybackState`

`ObservableInt`

Current playback state from ExoPlayer (idle, ready, ended, buffering).

`isVideoPlayWhenReady`

`ObservableBoolean`

Whether playback is currently playing (playWhenReady).

`videoName`

`ObservableField`

Title or name of the currently playing video or ad.

`videoPoster`

`ObservableField`

URI of the video poster image (thumbnail/artwork).

`videoMetaData`

`ObservableField`

Additional metadata string (unused in current code).

`videoDuration`

`ObservableField`

Total duration of the video in milliseconds.

`videoCurrentTime`

`ObservableField`

Current playback position in milliseconds.

`videoBufferedPosition`

`ObservableField`

Current buffered position in milliseconds.

`videoRemainInString`

`ObservableField`

Formatted string representing remaining playback time.

`videoPositionInString`

`ObservableField`

Formatted string for current playback time.

`videoHasSubtitle`

`ObservableField`

Whether the current video has subtitle available.

`adClickUrl`

`ObservableField`

URL for the current ad's click-through link.

`adMetaData`

`ObservableField`

Metadata string related to the ad (unused in current code).

`numberOfAdsLeft`

`ObservableInt`

Number of ads left in the playback queue.

`isCurrentAd`

`ObservableField`

Flag indicating if the current playback is an ad.

`isSubtitleEnabled`

`ObservableField`

Whether subtitles are currently enabled.

`isDraggingSeekBar`

`ObservableField`

Whether the user is currently dragging the seek bar.


Member Variables

Name

Type

Description

mPlayer

`SimpleExoPlayer`

The ExoPlayer instance controlled by this UserController.

mMediaModel

`MediaModel`

The current media (video or ad) being played.

mPlaybackActionCallback

`PlaybackActionCallback`

Callback interface for notifying playback-related user actions.

mTubiExoPlayerView

`TubiExoPlayerView`

Reference to the custom player view hosting video and subtitles.

mControlState

`int`

Current control state (one of the constants).

`mProgressUpdateHandler`

`Handler`

Handler for scheduling periodic progress updates.

`updateProgressAction`

`Runnable`

Runnable task that updates playback progress and UI.

mOnControlStateChange

`Runnable`

Optional callback to run when control state changes.


Public Methods


void setMediaModel(@NonNull MediaModel mediaModel, Context context)

Sets the current media model (video or ad) and updates observable metadata fields accordingly.

MediaModel video = ...;
userController.setMediaModel(video, context);

void setPlayer(@NonNull SimpleExoPlayer player, @NonNull PlaybackActionCallback playbackActionCallback, @NonNull TubiExoPlayerView tubiExoPlayerView)

Associates an ExoPlayer instance, playback callback, and player view with this controller.

userController.setPlayer(exoPlayer, playbackActionCallback, tubiExoPlayerView);

void setAvailableAdLeft(int count)

Updates the number of ads left to play.


void updateTimeTextViews(long position, long duration)

Updates the formatted time strings for the current playback position and remaining time.


void togglePlayPause()

Toggles playback between play and pause states.


void fastForward()

Seeks forward by a predefined fast seek interval.


void fastRewind()

Seeks backward by a predefined rewind interval.


int getState()

Returns the current control state.


void setState(int state)

Sets the control state and triggers any registered state change callback.


boolean isDuringCustomSeek()

Checks if the controller is currently in a custom seek mode.


void setOnControlStateChange(Runnable onControlStateChange)

Registers a callback to be executed when the control state changes.


Playback Control Interface Implementations

The following methods implement `TubiPlaybackControlInterface`:


SeekBar Listener Implementations


Player Event Listener Methods

The class listens to ExoPlayer events to keep observables updated:


Private Helper Methods


Important Implementation Details


Usage Example

// Initialize UserController and bind with player and UI
UserController userController = new UserController();

// Set player instance and playback callbacks
userController.setPlayer(simpleExoPlayer, playbackCallback, tubiExoPlayerView);

// Set current media (video or ad)
userController.setMediaModel(mediaModel, context);

// Bind this controller to UI controls, e.g., seek bar listeners
seekBar.setOnSeekBarChangeListener(userController);

// Toggle play/pause on user button click
playPauseButton.setOnClickListener(v -> userController.togglePlayPause());

// Enable or disable subtitles
subtitleToggle.setOnCheckedChangeListener((buttonView, isChecked) -> userController.triggerSubtitlesToggle(isChecked));

Interactions with Other Components


Mermaid Class Diagram

classDiagram
    class UserController {
        +ObservableInt playerPlaybackState
        +ObservableBoolean isVideoPlayWhenReady
        +ObservableField<String> videoName
        +ObservableField<Uri> videoPoster
        +ObservableField<Long> videoDuration
        +ObservableField<Long> videoCurrentTime
        +ObservableField<Long> videoBufferedPosition
        +ObservableField<String> videoRemainInString
        +ObservableField<String> videoPositionInString
        +ObservableField<Boolean> videoHasSubtitle
        +ObservableField<String> adClickUrl
        +ObservableInt numberOfAdsLeft
        +ObservableField<Boolean> isCurrentAd
        +ObservableField<Boolean> isSubtitleEnabled
        +ObservableField<Boolean> isDraggingSeekBar

        +void setMediaModel(MediaModel mediaModel, Context context)
        +void setPlayer(SimpleExoPlayer player, PlaybackActionCallback playbackActionCallback, TubiExoPlayerView tubiExoPlayerView)
        +void togglePlayPause()
        +void fastForward()
        +void fastRewind()
        +void seekTo(long millisecond)
        +void seekBy(long millisecond)
        +void triggerSubtitlesToggle(boolean enabled)
        +void clickCurrentAd()
        +int getState()
        +void setState(int state)
        +boolean isDuringCustomSeek()
        +void setOnControlStateChange(Runnable onControlStateChange)
        +void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
        +void onStartTrackingTouch(SeekBar seekBar)
        +void onStopTrackingTouch(SeekBar seekBar)
        +void onPlayerStateChanged(boolean playWhenReady, int playbackState)
        +void onPlayerError(ExoPlaybackException error)
    }

    UserController --|> TubiPlaybackControlInterface
    UserController --|> ExoPlayer.EventListener
    UserController --|> SeekBar.OnSeekBarChangeListener

Summary

`UserController` is a pivotal component in the media playback architecture that abstracts playback control logic and state management from UI concerns. It exposes rich observable properties for data binding, handles user interaction events, listens to ExoPlayer events, and manages playback states including ads and subtitles. Its design enables a responsive and synchronized user experience while keeping UI code clean and maintainable.

This class integrates tightly with the `TubiExoPlayerView` for subtitle display, ExoPlayer for playback, and higher-level playback callbacks to coordinate user actions with business logic layers such as FSM state machines.


End of UserController.java Documentation