[MUON-1999] Add video progress tracking to BpkVideoPlayer - #2793
[MUON-1999] Add video progress tracking to BpkVideoPlayer#2793Joan Fuentes (Nescafemix) wants to merge 9 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Expose video playback progress from BpkVideoPlayer to enable analytics (e.g., quartile tracking) and demonstrate/verify the behavior across docs, demo, and tests.
Changes:
- Added
BpkVideoPlayerProgressmodel + README guidance for progress/quartile tracking. - Added
progressStatetoBpkVideoPlayerControllerwith polling + end/loop “final 100%” emission. - Updated demo story and added unit/instrumentation tests for progress behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/compose/VideoPlayer/README.md | Documents progressState usage and quartile tracking pattern. |
| backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/videoplayer/BpkVideoPlayerProgress.kt | Introduces the progress model with derived percentage. |
| backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/videoplayer/BpkVideoPlayerController.kt | Adds observable progress state + polling and final-progress emission on end/loop. |
| backpack-compose/src/test/kotlin/net/skyscanner/backpack/compose/videoplayer/BpkVideoPlayerProgressTest.kt | Unit tests for percentage edge cases. |
| backpack-compose/src/androidTest/kotlin/net/skyscanner/backpack/compose/videoplayer/BpkVideoPlayerTest.kt | Integration tests for progress state behavior during playback/pause/end/loop. |
| app/src/main/java/net/skyscanner/backpack/demo/compose/VideoPlayerStory.kt | Demo UI now displays live progress under the player. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…lso a non used import
6aecbbb to
4bd7dd8
Compare
Generated by 🚫 Danger Kotlin against 4bd7dd8 |
mldtms
left a comment
There was a problem hiding this comment.
_progressState is only ever written in startProgressPolling()'s loop and emitFinalProgress(). Neither resetToStart() nor play() (when resuming from Ended) touches it.
| private val _isMuted = mutableStateOf(config.startsMuted) | ||
| val isMuted: State<Boolean> get() = _isMuted | ||
|
|
||
| private val _progressState = mutableStateOf<BpkVideoPlayerProgress?>(null) |
There was a problem hiding this comment.
_progressState is only ever written in startProgressPolling()'s loop and emitFinalProgress(). Neither resetToStart() nor play() (when resuming from Ended) touches it. Maybe -> reset progress explicitly in resetToStart() (and consider doing the same at the start of play() when coming from Ended):
There was a problem hiding this comment.
Fixed. Both resetToStart() and play() (when coming from Ended) now reset _progressState immediately to BpkVideoPlayerProgress(0L, duration) instead of leaving a stale 100% value. If duration is somehow unavailable it falls back to null. Added two integration tests to cover both cases. ff41ca3
|
|
||
| BpkText( | ||
| modifier = Modifier | ||
| .weight(1f) |
There was a problem hiding this comment.
Why would we use this? Why would we take the whole space that's left? can we just place the text below the video.
There was a problem hiding this comment.
The weight(1f) is intentional — it ensures the progress text is always visible on screen regardless of the video height, by splitting the available space between the video and the text within the Column. Without it, on small screens the text could be pushed off screen. Note that this text is only here as a demo to show that progress can be retrieved from the controller — it is not part of the component itself.
| val durationMs: Long, | ||
| ) { | ||
| val percentage: Float | ||
| get() = if (durationMs > 0) positionMs.toFloat() / durationMs.toFloat() else 0f |
There was a problem hiding this comment.
maybe add .coerceIn(0f, 1f) ?
There was a problem hiding this comment.
Done, added .coerceIn(0f, 1f) to percentage. Also added unit tests for both out-of-range cases (position > duration and negative position). ff41ca3
Generated by 🚫 Danger Kotlin against ff41ca3 |
MUON-1999
Description
Exposes playback progress from
BpkVideoPlayerso consumers can track video position and fire analytics events (e.g. quartile tracking at 25%, 50%, 75%, 100%).Changes
BpkVideoPlayerProgressdata class — holdspositionMs,durationMs, and a derivedpercentage: Float.BpkVideoPlayerController.progressState: State<BpkVideoPlayerProgress?>— new observable property, updated every ~200 ms while the video is playing. Emitsnullwhen duration is unknown. Guarantees a final value at 100% when the video ends or loops (viaonPositionDiscontinuity(DISCONTINUITY_REASON_AUTO_TRANSITION)for loop-safe detection).VideoPlayerStorynow shows live progress percentage and position below the player.BpkVideoPlayerTestcovering progress polling, pause/resume state retention, end-of-video 100% guarantee, and loop boundary detection; unit tests inBpkVideoPlayerProgressTestforpercentagecalculation edge cases.How to test
Video player→Default ControlsScreen_recording_20260818_174237_480p.mov
Remember to include the following changes:
README.md