Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
180 commits
Select commit Hold shift + click to select a range
38b9b45
Extract Player and its helpers into a new ui-common module
MattiasBuelens Oct 12, 2026
a9d1399
Add changelog entry
MattiasBuelens Oct 12, 2026
a0158e1
Hide internal APIs from the API docs
MattiasBuelens Oct 12, 2026
0386b98
Add PR link to changelog
MattiasBuelens Oct 12, 2026
9fc4c63
Merge pull request #107 from THEOplayer/feature/ui-common
MattiasBuelens Oct 14, 2026
8efe5e3
Add ExperimentalPlayerStateApi and LocalTHEOplayerView
MattiasBuelens Oct 12, 2026
c25805d
Add state holders for the playback and time controls
MattiasBuelens Oct 12, 2026
0d0587b
Delegate the controls to the new state holders
MattiasBuelens Oct 12, 2026
56907de
Add changelog entry
MattiasBuelens Oct 12, 2026
d87e70d
Clarify that the cast check in canSeek is only needed for THEOplayer …
MattiasBuelens Oct 12, 2026
0e37a90
Update state holders on the same events as Player
MattiasBuelens Oct 12, 2026
681b388
Add PR link to changelog
MattiasBuelens Oct 12, 2026
6446254
Share one event listener per state holder
MattiasBuelens Oct 12, 2026
ed7ca6a
Make state holder constructors internal
MattiasBuelens Oct 13, 2026
3c473bb
Add state holders for the mute, loading, error, first play, video siz…
MattiasBuelens Oct 13, 2026
d22bc1b
Delegate the remaining components to state holders
MattiasBuelens Oct 13, 2026
6d0dfe3
Add changelog entry
MattiasBuelens Oct 13, 2026
d592287
Add PR link to changelog
MattiasBuelens Oct 13, 2026
e1adc40
Initialize ErrorState with the initial error
MattiasBuelens Oct 13, 2026
7d5a548
Generalize PlayerStateListeners
MattiasBuelens Oct 13, 2026
d34417e
Add track, quality and playback rate state holders
MattiasBuelens Oct 13, 2026
8d34b3a
Add state overloads for track lists and menus
MattiasBuelens Oct 13, 2026
8c752da
Add changelog entry
MattiasBuelens Oct 13, 2026
430cb02
Add PR link to changelog
MattiasBuelens Oct 13, 2026
47ea726
Move default playback rates to a module-level val
MattiasBuelens Oct 13, 2026
497cc96
Document state caveat on openMenu
MattiasBuelens Oct 13, 2026
9b3b8c7
Move test mocks to MockPlayer.kt
MattiasBuelens Oct 13, 2026
60fbf43
Split state holder tests into one file per class
MattiasBuelens Oct 13, 2026
f4b4401
Share one fullscreen handler per THEOplayerView
MattiasBuelens Oct 13, 2026
26e778d
Add experimental FullscreenState
MattiasBuelens Oct 13, 2026
a2b41b1
Add PR link to changelog
MattiasBuelens Oct 13, 2026
f6c09f6
Use CopyOnWriteArrayList for fullscreen listeners
MattiasBuelens Oct 13, 2026
d1a5a22
Add picture-in-picture and cast state holders
MattiasBuelens Oct 13, 2026
a321931
Add state overloads for the picture-in-picture and Chromecast components
MattiasBuelens Oct 13, 2026
a89d0af
Add changelog entry
MattiasBuelens Oct 13, 2026
d80fe1e
Add PR link to changelog
MattiasBuelens Oct 13, 2026
108ab83
Use state holders in UIController and DefaultUI
MattiasBuelens Oct 13, 2026
31e9f2d
Add changelog entry
MattiasBuelens Oct 13, 2026
7af1b66
Consolidate state holder changelog entries
MattiasBuelens Oct 13, 2026
eefcef8
Add PR link to changelog
MattiasBuelens Oct 13, 2026
87c230a
Reorder changelog entries
MattiasBuelens Oct 13, 2026
7d54558
Merge pull request #116 from THEOplayer/feature/ui-controller-state-h…
MattiasBuelens Oct 14, 2026
08577f5
Add facade-free UIController and DefaultUI entry points
MattiasBuelens Oct 13, 2026
f3e924a
Use the facade-free entry points in the demo
MattiasBuelens Oct 13, 2026
cdbc3e9
Add changelog entry
MattiasBuelens Oct 13, 2026
89de8ae
Add rememberTHEOplayerView and decouple the theoplayerView overloads …
MattiasBuelens Oct 13, 2026
bf689b7
Use rememberTHEOplayerView and forward the view lifecycle in the demo
MattiasBuelens Oct 13, 2026
854be82
Update changelog entry
MattiasBuelens Oct 13, 2026
3bc0ddc
Handle edit mode in rememberTHEOplayerView and NitflexUI
MattiasBuelens Oct 13, 2026
e5e4f8c
Reorder changelog entries
MattiasBuelens Oct 13, 2026
1a1a398
Add PR link to changelog
MattiasBuelens Oct 13, 2026
66268e3
Fix state holder opt-ins in the demo app
MattiasBuelens Oct 13, 2026
e354bbb
Use a wrapped context for THEOplayerView in previews
MattiasBuelens Oct 13, 2026
0284695
Show the controls immediately in previews
MattiasBuelens Oct 13, 2026
fefab7a
Merge pull request #119 from THEOplayer/feature/ui-controller-theopla…
MattiasBuelens Oct 14, 2026
ec4d685
Add coroutine-based listen helpers
MattiasBuelens Oct 14, 2026
fa96bff
Observe state holders with coroutines
MattiasBuelens Oct 14, 2026
8da65d5
Run state holder tests on coroutines
MattiasBuelens Oct 14, 2026
1645f75
Read initial state in state holder constructors
MattiasBuelens Oct 14, 2026
13d06a5
Always refresh video qualities on observe
MattiasBuelens Oct 14, 2026
a5c0d50
Clean up state holder tests
MattiasBuelens Oct 14, 2026
3491496
Document why listen doesn't use callbackFlow
MattiasBuelens Oct 14, 2026
509b5a9
Merge pull request #120 from THEOplayer/feature/state-holders-coroutines
MattiasBuelens Oct 14, 2026
52d591f
Scroll to and announce the selected item in menu lists
MattiasBuelens Oct 15, 2026
cad17b5
Add changelog entry
MattiasBuelens Oct 15, 2026
910774b
Merge pull request #127 from THEOplayer/fix/menu-list-selection
MattiasBuelens Oct 15, 2026
ac14a7d
Move UI hosting and menu infrastructure to android-ui-common
MattiasBuelens Oct 14, 2026
81ee1e9
Remove trailing semicolon
MattiasBuelens Oct 14, 2026
f541be0
Merge pull request #121 from THEOplayer/feature/tv-ui-common
MattiasBuelens Oct 19, 2026
b5656d3
Show controls on key press and handle media keys
MattiasBuelens Oct 14, 2026
77d9928
Simplify media play key handling
MattiasBuelens Oct 14, 2026
9f6a216
Add changelog entry
MattiasBuelens Oct 14, 2026
19a05ea
Don't hide keys from open menus
MattiasBuelens Oct 14, 2026
888b193
Use delegated uiState in UIController
MattiasBuelens Oct 14, 2026
3809b00
Merge pull request #122 from THEOplayer/feature/tv-keyboard
MattiasBuelens Oct 19, 2026
0089217
Share UIController core between mobile and TV
MattiasBuelens Oct 14, 2026
9f8474c
Add Android TV UI module with UIController
MattiasBuelens Oct 14, 2026
07ca894
Update changelog
MattiasBuelens Oct 14, 2026
8dd0f3e
Merge pull request #124 from THEOplayer/feature/tv-skeleton
MattiasBuelens Oct 19, 2026
040bf9e
Move shared resources and playback time formatting
MattiasBuelens Oct 14, 2026
d7233e7
Share THEOplayer palette and player colors
MattiasBuelens Oct 14, 2026
5a5cdc9
Add direct seek operation to SeekBarState
MattiasBuelens Oct 14, 2026
05e18a5
Add Android TV playback controls and tests
MattiasBuelens Oct 14, 2026
ac763a2
Update changelog
MattiasBuelens Oct 14, 2026
454cd83
Explain seek bar key repeat handling
MattiasBuelens Oct 14, 2026
41d9332
Extract Slider from SeekBar
MattiasBuelens Oct 14, 2026
cafefe7
Port Material3's indeterminate spinner to LoadingSpinner
MattiasBuelens Oct 14, 2026
1bd9929
Add disabled slider colors and document TV controls
MattiasBuelens Oct 14, 2026
2d67c21
Add CircularProgressIndicator preview
MattiasBuelens Oct 14, 2026
7fd6ce0
Ignore ui-tv build output
MattiasBuelens Oct 14, 2026
34551b8
Merge pull request #125 from THEOplayer/feature/tv-controls
MattiasBuelens Oct 19, 2026
f2ffa40
Move shared formatting helpers to ui-common
MattiasBuelens Oct 15, 2026
7e9d05c
Add Android TV side-panel menus
MattiasBuelens Oct 15, 2026
76f4955
Add changelog link for TV menus
MattiasBuelens Oct 15, 2026
814ef37
Restyle TV menus after YouTube's
MattiasBuelens Oct 15, 2026
912ab11
Add spacing below the TV menu title
MattiasBuelens Oct 15, 2026
a3c4bcc
Avoid clipping focused items in TV menu lists
MattiasBuelens Oct 15, 2026
8b6d9cf
Merge pull request #126 from THEOplayer/feature/tv-menus
MattiasBuelens Oct 19, 2026
2505270
Keep focus in TV UIController while controls are hidden
MattiasBuelens Oct 15, 2026
d8b47ec
Add TV DefaultUI
MattiasBuelens Oct 15, 2026
45a067f
Add tests for TV DefaultUI
MattiasBuelens Oct 15, 2026
0e0f4e7
Temporarily disable maven build type of demo app
MattiasBuelens Oct 15, 2026
1514fce
Add Android TV demo activity
MattiasBuelens Oct 15, 2026
5fed447
Add preview for DefaultUI
MattiasBuelens Oct 15, 2026
985cd15
Generate multi-module API documentation
MattiasBuelens Oct 15, 2026
51aaa5b
Update changelog
MattiasBuelens Oct 15, 2026
7f28b8f
Set content color in DefaultUI previews
MattiasBuelens Oct 15, 2026
1d1ffd0
Set content color in TvActivity
MattiasBuelens Oct 15, 2026
f3e0196
Merge pull request #128 from THEOplayer/feature/tv-default-ui
MattiasBuelens Oct 19, 2026
eebaf55
Add TextTrackStyle.marginBottomCompat
MattiasBuelens Oct 15, 2026
f0bf67d
Move subtitles out of the way of visible controls
MattiasBuelens Oct 15, 2026
f27cb49
Update changelog
MattiasBuelens Oct 15, 2026
7191f50
Merge pull request #129 from THEOplayer/feature/subtitle-margins
MattiasBuelens Oct 19, 2026
aa321fa
Add TV SeekButton
MattiasBuelens Oct 19, 2026
2e88921
Add UIController(config, source) overload for TV
MattiasBuelens Oct 19, 2026
4cc52bc
Update changelog
MattiasBuelens Oct 19, 2026
293353b
Consolidate changelog
MattiasBuelens Oct 19, 2026
ca35879
Merge pull request #131 from THEOplayer/feature/tv-seek-button
MattiasBuelens Oct 19, 2026
78c83ee
Focus play button when entering TV DefaultUI's button row
MattiasBuelens Oct 19, 2026
8c07e57
Merge pull request #134 from THEOplayer/fix/tv-live-play-focus
MattiasBuelens Oct 19, 2026
8f08eda
Add descriptions to demo streams
MattiasBuelens Oct 19, 2026
308ea01
Add start screen to TV demo
MattiasBuelens Oct 19, 2026
e163343
Keep TV theme chips the same size when selected
MattiasBuelens Oct 19, 2026
6a64b4e
Keep Left/Right inside the TV demo's stream carousel
MattiasBuelens Oct 19, 2026
de36c95
Link upstream issue for TV carousel focus workaround
MattiasBuelens Oct 19, 2026
78838cc
Add Nitflex theme to TV demo
MattiasBuelens Oct 19, 2026
35941ec
Focus play button when entering TV Nitflex center controls
MattiasBuelens Oct 19, 2026
f62c11a
Size custom play icons in TV Nitflex center controls
MattiasBuelens Oct 19, 2026
641c239
Merge pull request #133 from THEOplayer/feature/tv-nitflex
MattiasBuelens Oct 19, 2026
07f6a19
Hide @InternalUiApi declarations from API docs (#136)
MattiasBuelens Oct 19, 2026
6f542d3
Cancel outdated CI runs on pull requests (#138)
MattiasBuelens Oct 20, 2026
30d2fca
Add Android TV guides (#137)
MattiasBuelens Oct 20, 2026
60d4a54
Keep controls visibility state stable across recompositions
MattiasBuelens Oct 20, 2026
816fa2d
Read latest controls state and callbacks in toggleControlsOnTap
MattiasBuelens Oct 20, 2026
55243ac
Fix Modifier.constrainedAspectRatio having no effect
MattiasBuelens Oct 20, 2026
ac19d90
Update changelog
MattiasBuelens Oct 20, 2026
a779192
Merge pull request #140 from THEOplayer/bugfix/constrained-aspect-ratio
MattiasBuelens Oct 20, 2026
c2f7e46
Merge pull request #139 from THEOplayer/bugfix/controls-visibility-st…
MattiasBuelens Oct 20, 2026
cdcc8ac
Show mobile menus in a shared bottom or side sheet
MattiasBuelens Oct 20, 2026
8f16049
Hint that menu lists can scroll with a peeking item and fading edges
MattiasBuelens Oct 20, 2026
1a63019
Use a shared axis transition between menus inside the sheet
MattiasBuelens Oct 21, 2026
1fbc265
Make the menu transition duration configurable through PlayerAnimations
MattiasBuelens Oct 21, 2026
14a72fa
Show TV menus in a shared side sheet with shared axis transitions
MattiasBuelens Oct 21, 2026
c724a2a
Require a menu container in UIControllerCore
MattiasBuelens Oct 21, 2026
56fd40f
Split the changelog entry for mobile menu sheets and menu transitions
MattiasBuelens Oct 21, 2026
09c4a3d
Draw the menu sheet only in UIController, not in Menu
MattiasBuelens Oct 21, 2026
db9cca5
Require opting in to implement MenuScope and UIControllerScope
MattiasBuelens Oct 21, 2026
a9c5fce
Move fadingEdges below the constrainedAspectRatio node classes
MattiasBuelens Oct 21, 2026
d5d89e9
Merge the menu sheet's scrim Box into its BoxWithConstraints
MattiasBuelens Oct 21, 2026
857045c
Merge pull request #141 from THEOplayer/feature/menu-max-width
MattiasBuelens Oct 21, 2026
1e6d40b
Set up ktfmt
MattiasBuelens Oct 21, 2026
2d640dc
Reformat code with ktfmt
MattiasBuelens Oct 21, 2026
70baa9d
Ignore ktfmt reformat in git blame
MattiasBuelens Oct 21, 2026
70ab9b9
Exclude generated buildSrc scripts from ktfmt
MattiasBuelens Oct 21, 2026
dadacc1
Simplify root ktfmt task wiring
MattiasBuelens Oct 21, 2026
c5bc621
Merge pull request #142 from THEOplayer/feature/ktfmt
MattiasBuelens Oct 21, 2026
1fa0520
Add TextTrackStyleState for changing the subtitle style
MattiasBuelens Oct 20, 2026
03748ee
Add TextTrackStyleMenu to Android TV UI
MattiasBuelens Oct 20, 2026
ee60f53
Add TextTrackStyleMenu to mobile UI
MattiasBuelens Oct 20, 2026
fe7c1ef
Update changelog
MattiasBuelens Oct 20, 2026
8916592
Align subtitle options wording and order with Open Video UI for Web
MattiasBuelens Oct 20, 2026
e260d6f
Open subtitle options from the subtitles menu
MattiasBuelens Oct 20, 2026
5185830
Restore scroll position and focus when returning from a submenu
MattiasBuelens Oct 20, 2026
1c4eb4c
Move subtitle options to the top of the TV subtitles menu
MattiasBuelens Oct 20, 2026
f7e880d
Link the subtitle options changelog entries to #143
MattiasBuelens Oct 22, 2026
d06aea4
Link the menu sheet changelog entries to #141
MattiasBuelens Oct 22, 2026
f05c6e9
Inset the mobile menu header by 4dp like a Material 3 top app bar
MattiasBuelens Oct 22, 2026
4e23918
Align the chevrons in mobile menu rows with the menu header
MattiasBuelens Oct 22, 2026
b18d1d8
Merge pull request #143 from THEOplayer/feature/text-track-style
MattiasBuelens Oct 22, 2026
ad67c11
Close all menus when tapping outside the mobile menu sheet
MattiasBuelens Oct 22, 2026
76dbff6
Update changelog
MattiasBuelens Oct 22, 2026
9bb676d
Count closeCurrentMenu and closeAllMenus calls separately in MenuShee…
MattiasBuelens Oct 22, 2026
fe70d83
Add hero images to TV demo carousel
MattiasBuelens Oct 22, 2026
a48a325
Align CC badge with the timecode bar in the test card image
MattiasBuelens Oct 22, 2026
935d199
Update TV demo home screenshot
MattiasBuelens Oct 22, 2026
2a5022c
Use JPEG for TV demo home screenshot
MattiasBuelens Oct 22, 2026
8a50b84
Move image credits to a demo app README
MattiasBuelens Oct 22, 2026
fda084c
Merge pull request #146 from THEOplayer/feature/tv-carousel-hero-images
MattiasBuelens Oct 23, 2026
b26c848
Merge pull request #144 from THEOplayer/feature/close-all-menus
MattiasBuelens Oct 23, 2026
42d8163
1.15.0
Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
root = true

# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
# and making sure your editor is set to read settings from .editorconfig files.
#
# It includes editor-specific config options for IntelliJ IDEA.
#
# If any option is wrong, PR are welcome

[{*.kt,*.kts}]
indent_style = space
insert_final_newline = true
max_line_length = 100
indent_size = 4
ij_continuation_indent_size = 4
ij_java_names_count_to_use_import_on_demand = 9999
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
ij_kotlin_align_multiline_method_parentheses = false
ij_kotlin_align_multiline_parameters = true
ij_kotlin_align_multiline_parameters_in_calls = false
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
ij_kotlin_assignment_wrap = normal
ij_kotlin_blank_lines_after_class_header = 0
ij_kotlin_blank_lines_around_block_when_branches = 0
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
ij_kotlin_block_comment_at_first_column = true
ij_kotlin_call_parameters_new_line_after_left_paren = true
ij_kotlin_call_parameters_right_paren_on_new_line = false
ij_kotlin_call_parameters_wrap = on_every_item
ij_kotlin_catch_on_new_line = false
ij_kotlin_class_annotation_wrap = split_into_lines
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_continuation_indent_for_chained_calls = true
ij_kotlin_continuation_indent_for_expression_bodies = true
ij_kotlin_continuation_indent_in_argument_lists = true
ij_kotlin_continuation_indent_in_elvis = false
ij_kotlin_continuation_indent_in_if_conditions = false
ij_kotlin_continuation_indent_in_parameter_lists = false
ij_kotlin_continuation_indent_in_supertype_lists = false
ij_kotlin_continuation_indent_size = 4
ij_kotlin_else_on_new_line = false
ij_kotlin_enum_constants_wrap = off
ij_kotlin_extends_list_wrap = normal
ij_kotlin_field_annotation_wrap = off
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = false
ij_kotlin_import_nested_classes = false
ij_kotlin_imports_layout = *
ij_kotlin_indent_size = 4
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 2
ij_kotlin_keep_blank_lines_in_code = 2
ij_kotlin_keep_blank_lines_in_declarations = 2
ij_kotlin_keep_first_column_comment = true
ij_kotlin_keep_indents_on_empty_lines = false
ij_kotlin_keep_line_breaks = true
ij_kotlin_lbrace_on_next_line = false
ij_kotlin_line_comment_add_space = false
ij_kotlin_line_comment_at_first_column = true
ij_kotlin_method_annotation_wrap = split_into_lines
ij_kotlin_method_call_chain_wrap = normal
ij_kotlin_method_parameters_new_line_after_left_paren = true
ij_kotlin_method_parameters_right_paren_on_new_line = true
ij_kotlin_method_parameters_wrap = on_every_item
ij_kotlin_name_count_to_use_star_import = 9999
ij_kotlin_name_count_to_use_star_import_for_members = 9999
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
ij_kotlin_space_after_type_colon = true
ij_kotlin_space_before_catch_parentheses = true
ij_kotlin_space_before_comma = false
ij_kotlin_space_before_extend_colon = true
ij_kotlin_space_before_for_parentheses = true
ij_kotlin_space_before_if_parentheses = true
ij_kotlin_space_before_lambda_arrow = true
ij_kotlin_space_before_type_colon = false
ij_kotlin_space_before_when_parentheses = true
ij_kotlin_space_before_while_parentheses = true
ij_kotlin_spaces_around_additive_operators = true
ij_kotlin_spaces_around_assignment_operators = true
ij_kotlin_spaces_around_equality_operators = true
ij_kotlin_spaces_around_function_type_arrow = true
ij_kotlin_spaces_around_logical_operators = true
ij_kotlin_spaces_around_multiplicative_operators = true
ij_kotlin_spaces_around_range = false
ij_kotlin_spaces_around_relational_operators = true
ij_kotlin_spaces_around_unary_operator = false
ij_kotlin_spaces_around_when_arrow = true
ij_kotlin_variable_annotation_wrap = off
ij_kotlin_while_on_new_line = false
ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 1
ij_kotlin_wrap_first_method_in_call_chain = false
ktfmt_trailing_comma_management_strategy = complete
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Reformat code with ktfmt
2d640dc965e55e1ee4f266bb4b33667be2fdbb43
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ on:
paths-ignore:
- '**.md'

# Cancel outdated runs on the same PR, but let runs on main/develop finish.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build:
runs-on: ubuntu-latest
Expand All @@ -27,6 +32,8 @@ jobs:
with:
# https://github.com/gradle/actions/blob/v6.0.0/README.md#licensing-notice
cache-disabled: true
- name: Check formatting
run: ./gradlew ktfmtCheck
- name: Run tests
run: ./gradlew test
- name: Assemble
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
# https://github.com/gradle/actions/blob/v6.0.0/README.md#licensing-notice
cache-disabled: true
- name: Build API documentation with Dokka
run: ./gradlew ui:dokkaGeneratePublicationHtml
run: ./gradlew :dokkaGeneratePublicationHtml
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
Expand Down
7 changes: 7 additions & 0 deletions .idea/ktfmt.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@

## [Unreleased]

## [1.15.0] (2026-10-23)

* 🚀 Added `android-ui-tv`, an experimental UI library for Android TV built on Compose for TV. It includes a D-pad friendly `DefaultUI`. Code shared with `android-ui` now lives in a new `android-ui-common` artifact; `android-ui` depends on it, so apps don't need any changes. ([#107](https://github.com/THEOplayer/android-ui/pull/107), [#121](https://github.com/THEOplayer/android-ui/pull/121), [#124](https://github.com/THEOplayer/android-ui/pull/124), [#125](https://github.com/THEOplayer/android-ui/pull/125), [#126](https://github.com/THEOplayer/android-ui/pull/126), [#128](https://github.com/THEOplayer/android-ui/pull/128), [#131](https://github.com/THEOplayer/android-ui/pull/131))
* 🚀 Added experimental state holders (such as `rememberPlayButtonState` and `rememberSeekBarState`), so custom UIs can reuse the logic of the built-in components. The components have new overloads that take their state, and `LocalTHEOplayerView` provides the current `THEOplayerView`. `UIController` and `DefaultUI` now use these state holders internally. ([#108](https://github.com/THEOplayer/android-ui/pull/108), [#110](https://github.com/THEOplayer/android-ui/pull/110), [#111](https://github.com/THEOplayer/android-ui/pull/111), [#114](https://github.com/THEOplayer/android-ui/pull/114), [#115](https://github.com/THEOplayer/android-ui/pull/115), [#116](https://github.com/THEOplayer/android-ui/pull/116))
* 🚀 Added `UIController(theoplayerView)` and `DefaultUI(theoplayerView)`, which don't create a `Player`. Use `rememberTHEOplayerView(config)` to create a `THEOplayerView` that follows the lifecycle of the composition. `UIController(config)` and `DefaultUI(config)` no longer create a `Player` either, and `UIControllerScope.player` is only created when accessed. ([#119](https://github.com/THEOplayer/android-ui/pull/119))
* 🚀 `UIController` now handles keys: pressing any key shows the controls (which helps on tablets with a keyboard), media keys (play/pause, fast-forward and rewind) control playback even while the controls are hidden, and Back closes an open menu. Use the new `PlayerAnimations.controlsHideDelay` to configure how long the controls stay visible after user interaction. ([#122](https://github.com/THEOplayer/android-ui/pull/122), [#124](https://github.com/THEOplayer/android-ui/pull/124))
* 🚀 `UIController` now moves subtitles up or down while the controls are visible, so they don't render behind the top or bottom chrome. Moving subtitles up requires THEOplayer 9.5.0 or higher. ([#129](https://github.com/THEOplayer/android-ui/pull/129))
* 🚀 Added `TextTrackStyleMenu` to change the style of subtitles, such as the font family, font color and background color. In `DefaultUI`, open it from the subtitles menu. Use `rememberTextTrackStyleState` to build a custom menu. Changing the font family requires THEOplayer 9.9.0 or higher. ([#130](https://github.com/THEOplayer/android-ui/issues/130), [#143](https://github.com/THEOplayer/android-ui/pull/143))
* 🚀 Added `MenuScope.closeAllMenus()` to close all open menus at once. On mobile, tapping outside the menu sheet now closes all open menus, instead of only going back to the previous menu. ([#144](https://github.com/THEOplayer/android-ui/pull/144))
* 🐛 Fixed `AudioTrackList`, `SubtitleTrackList`, `QualityList` and `PlaybackRateList` not scrolling to the selected item, and not announcing it to accessibility services. ([#127](https://github.com/THEOplayer/android-ui/pull/127))
* 🐛 Fixed `UIController` and `DefaultUI` not keeping the player at the aspect ratio of the video (or 16:9 before the video is loaded). ([#140](https://github.com/THEOplayer/android-ui/pull/140))
* 🐛 Fixed menus losing their scroll position (and on Android TV, their focused item) when returning from a submenu. ([#143](https://github.com/THEOplayer/android-ui/pull/143))
* 💅 Menus on mobile now open in a sheet with a translucent background, so they're easier to read on top of the video: a bottom sheet on narrow players, and a side sheet at the end of the player on wide players. ([#141](https://github.com/THEOplayer/android-ui/pull/141))
* 💅 Opening a submenu or going back to the previous menu now only animates the menu's contents, with a short slide and a fade, on both mobile and TV. Use the new `PlayerAnimations.menuTransitionDuration` to change how long this takes. ([#141](https://github.com/THEOplayer/android-ui/pull/141))
* 📝 The API documentation now has a separate section for each module (`android-ui`, `android-ui-common` and `android-ui-tv`). ([#128](https://github.com/THEOplayer/android-ui/pull/128))

## [1.14.2] (2026-10-13)

* 🐛 Fixed a listener leak in picture-in-picture mode detection on Android 15 and higher. ([#113](https://github.com/THEOplayer/android-ui/pull/113))
Expand Down Expand Up @@ -196,7 +212,8 @@

* 🚀 Initial release.

[Unreleased]: https://github.com/THEOplayer/android-ui/compare/v1.14.2...HEAD
[Unreleased]: https://github.com/THEOplayer/android-ui/compare/v1.15.0...HEAD
[1.15.0]: https://github.com/THEOplayer/android-ui/compare/v1.14.2...v1.15.0
[1.14.2]: https://github.com/THEOplayer/android-ui/compare/v1.14.1...v1.14.2
[1.14.1]: https://github.com/THEOplayer/android-ui/compare/v1.14.0...v1.14.1
[1.14.0]: https://github.com/THEOplayer/android-ui/compare/v1.13.4...v1.14.0
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ A component library for building a world-class video player experience powered b
- Use the default UI for a great out-of-the-box experience, or use the individual components to build your own custom UI.
- Built using [Jetpack Compose](https://developer.android.com/jetpack/compose), for a modern and fast Android UI development experience.
- Easy to customize: [slot-based layouts](https://developer.android.com/develop/ui/compose/layouts/basics#slot-based-layouts) and [theme-aware components](https://developer.android.com/jetpack/compose/designsystems/custom) empower you to truly make your player UI your own.
- Works on phones and tablets, with experimental support for [Android TV](#android-tv).

> **Note**
> This project is under active development. While we believe it's ready for use in production, not all features are available in this first release. If you find a problem or have an idea for a new feature, don't hesitate to [open an issue](https://github.com/THEOplayer/android-ui/issues)!
Expand Down Expand Up @@ -128,3 +129,37 @@ setContent {
)
}
```

### Android TV

Add the TV UI library and TV Material to your module-level `build.gradle` file:

```groovy title="build.gradle"
dependencies {
implementation "com.theoplayer.theoplayer-sdk-android:core:11.+"
implementation "com.theoplayer.android-ui:android-ui-tv:1.+"
implementation "androidx.tv:tv-material:1.1.0"
}
```

Wrap `DefaultUI` in `THEOplayerTvTheme` and a TV Material `Surface`:

```kotlin title="TvPlayer.kt"
@OptIn(ExperimentalTvUiApi::class)
@Composable
fun TvPlayer() {
THEOplayerTvTheme {
Surface {
DefaultUI(
config = THEOplayerConfig.Builder().build(),
source = SourceDescription.Builder(
"https://cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny.m3u8"
).build(),
title = "Big Buck Bunny"
)
}
}
}
```

See the [Android TV getting started guide](https://optiview.dolby.com/docs/theoplayer/ui/android/open-video-ui/guides/android-tv/getting-started/) for more details, and [the TV demo app](https://github.com/THEOplayer/android-ui/blob/main/app/src/main/java/com/theoplayer/android/ui/demo/tv/TvActivity.kt) for a complete and working example.
12 changes: 12 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Demo app

This app demonstrates the Open Video UI for Android, with a phone/tablet UI in [`MainActivity`](src/main/java/com/theoplayer/android/ui/demo/MainActivity.kt) and an Android TV UI in [`TvActivity`](src/main/java/com/theoplayer/android/ui/demo/tv/TvActivity.kt).

## Credits

The Android TV home screen uses the following images:

* Bip Bop: ["Oscilloscope sine square.jpg"](https://commons.wikimedia.org/wiki/File:Oscilloscope_sine_square.jpg) by Xato, Public domain.
* Elephant's Dream: ["Elephants Dream s5 both.jpg"](https://commons.wikimedia.org/wiki/File:Elephants_Dream_s5_both.jpg) by © Blender Foundation / Ton Roosendaal et al., [CC BY 2.5](https://creativecommons.org/licenses/by/2.5).
* Sintel: ["Sintel-screenshot-1.jpg"](https://commons.wikimedia.org/wiki/File:Sintel-screenshot-1.jpg) by © Blender Foundation (www.sintel.org), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0).
* DASH-IF Livesim and Test card: Frames of the DASH-IF Livesim test stream, captured in the demo app.
28 changes: 17 additions & 11 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.ktfmt)
}

ktfmt { kotlinLangStyle() }

android {
namespace = "com.theoplayer.android.ui.demo"
compileSdk = libs.versions.android.compileSdk.get().toInt()
Expand All @@ -18,17 +21,15 @@ android {
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
vectorDrawables { useSupportLibrary = true }
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
"proguard-rules.pro",
)
}
// Build type that uses the latest android-ui from Maven
Expand Down Expand Up @@ -57,13 +58,14 @@ android {
// Required by Google IMA SDK v3.37.0+
isCoreLibraryDesugaringEnabled = true
}
buildFeatures {
compose = true
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
buildFeatures { compose = true }
packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } }
}

androidComponents {
// TODO Re-enable once android-ui-tv is published to Maven
beforeVariants(selector().withBuildType("maven")) { variantBuilder ->
variantBuilder.enable = false
}
}

Expand Down Expand Up @@ -104,6 +106,10 @@ dependencies {

debugImplementation(project(":ui"))
releaseImplementation(project(":ui"))
debugImplementation(project(":ui-tv"))
releaseImplementation(project(":ui-tv"))
debugImplementation(libs.androidx.tv.material)
releaseImplementation(libs.androidx.tv.material)
mavenImplementation("com.theoplayer.android-ui:android-ui:1.+")

latestPlayerImplementation(libs.theoplayer)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.theoplayer.android.ui.demo

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
Expand All @@ -21,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.theoplayer.android.ui", appContext.packageName)
}
}
}
21 changes: 21 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<uses-feature
android:name="android.software.leanback"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />

<application
android:allowBackup="true"
android:banner="@drawable/tv_banner"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
Expand All @@ -29,6 +37,19 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".tv.TvActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden|navigation"
android:exported="true"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:theme="@style/Theme.THEOplayerAndroidUI">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>

<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import com.google.android.gms.cast.framework.media.NotificationOptions

class CastOptionsProvider : OptionsProvider {
override fun getCastOptions(context: Context): CastOptions {
val notificationOptions = NotificationOptions.Builder()
.setTargetActivityClassName(MainActivity::class.java.name)
.build()
val notificationOptions =
NotificationOptions.Builder()
.setTargetActivityClassName(MainActivity::class.java.name)
.build()

val castMediaOptions = CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
.build()
val castMediaOptions =
CastMediaOptions.Builder().setNotificationOptions(notificationOptions).build()

return CastOptions.Builder()
.setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID)
Expand All @@ -27,4 +27,4 @@ class CastOptionsProvider : OptionsProvider {
override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
return null
}
}
}
Loading
Loading