Skip to content

[MUON-2017] Simplify BpkVideoPlayer by removing VideoHandler seam - #2757

Merged
Henrik Sym (henrik-sky) merged 4 commits into
mainfrom
muon-2017-simplify-bpkvideoplayer
Jul 27, 2026
Merged

[MUON-2017] Simplify BpkVideoPlayer by removing VideoHandler seam#2757
Henrik Sym (henrik-sky) merged 4 commits into
mainfrom
muon-2017-simplify-bpkvideoplayer

Conversation

@antonicg

@antonicg Antoni Castejón (antonicg) commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

MUON-2017

Summary

  • VideoPlayerHandle/PlayerFactory/FakeVideoPlayerHandle were added in a prior PR ([Android] BpkVideoPlayer: controller testability (MUON-1847) #2749) purely to let BpkVideoPlayerController be unit-tested without a real ExoPlayer. Removes that seam — the controller now builds and owns an ExoPlayer directly.
  • Test coverage for play/pause/loop/mute/ended/dispose moves from a JVM test against a fake handle to instrumentation tests driving a real bundled local video end-to-end.
  • Test coverage for default play/pause controls: button visibility per playback state (loading, ready, playing) and that tapping play/pause drives the correct state transitions on a real player.
  • Adds a small (7.3KB, 3s, H.264) test video asset at backpack-compose/src/androidTest/res/raw/bpk_video_player_test.mp4, played via android.resource://.
  • Public API is unchanged. The removed seam only touched internal members (internal val player, the internal constructor), so rememberBpkVideoPlayerController, play(), pause(), toggle(), setMuted(), resetToStart(), playbackState, isMuted, config all keep their exact signatures.

Test plan

  • ./gradlew :backpack-compose:testDebugUnitTest — reducer + playback-state unit tests still pass, controller test class removed (superseded by instrumentation tests)
  • ./gradlew :backpack-compose:connectedDebugAndroidTestBpkVideoPlayerTest and BpkVideoPlayerDefaultControlsTest instrumentation suites pass on device/emulator
  • Demo app's Video Player story still plays remote video correctly

🤖 Generated with Claude Code

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor
Warnings
⚠️ One or more component files were updated, but the tests weren't updated. If your change is not covered by existing tests please add snapshot tests.
⚠️

One or more component files were updated, but the docs screenshots weren't updated. If the changes are visual or it is a new component please regenerate the screenshots via ./gradlew recordScreenshots.

⚠️

One or more component files were updated, but README.md wasn't updated. If your change contains API changes/additions or a new component please update the relevant component README.

Generated by 🚫 Danger Kotlin against 3a178bc

@antonicg Antoni Castejón (antonicg) added the minor A new & backwards compatible feature/component label Jul 24, 2026
VideoPlayerHandle/PlayerFactory/FakeVideoPlayerHandle were only added to
unit-test BpkVideoPlayerController without a real ExoPlayer. The controller
now owns an ExoPlayer directly, and play/pause/loop/mute/ended/dispose
coverage moved to instrumentation tests driving a real bundled local video
instead of a fake.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@antonicg
Antoni Castejón (antonicg) force-pushed the muon-2017-simplify-bpkvideoplayer branch from 3a178bc to 2541a80 Compare July 27, 2026 09:17
@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor
Warnings
⚠️ One or more component files were updated, but the tests weren't updated. If your change is not covered by existing tests please add snapshot tests.
⚠️

One or more component files were updated, but the docs screenshots weren't updated. If the changes are visual or it is a new component please regenerate the screenshots via ./gradlew recordScreenshots.

⚠️

One or more component files were updated, but README.md wasn't updated. If your change contains API changes/additions or a new component please update the relevant component README.

Generated by 🚫 Danger Kotlin against 2541a80

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor
Warnings
⚠️ One or more component files were updated, but the tests weren't updated. If your change is not covered by existing tests please add snapshot tests.
⚠️

One or more component files were updated, but the docs screenshots weren't updated. If the changes are visual or it is a new component please regenerate the screenshots via ./gradlew recordScreenshots.

⚠️

One or more component files were updated, but README.md wasn't updated. If your change contains API changes/additions or a new component please update the relevant component README.

Generated by 🚫 Danger Kotlin against 18e1979

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor
Warnings
⚠️ One or more component files were updated, but the tests weren't updated. If your change is not covered by existing tests please add snapshot tests.
⚠️

One or more component files were updated, but the docs screenshots weren't updated. If the changes are visual or it is a new component please regenerate the screenshots via ./gradlew recordScreenshots.

⚠️

One or more component files were updated, but README.md wasn't updated. If your change contains API changes/additions or a new component please update the relevant component README.

Generated by 🚫 Danger Kotlin against e4ca132

@antonicg
Antoni Castejón (antonicg) marked this pull request as ready for review July 27, 2026 09:58
Copilot AI review requested due to automatic review settings July 27, 2026 09:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

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.

This PR simplifies BpkVideoPlayerController by removing the VideoPlayerHandle/PlayerFactory seam and having the controller build and own an ExoPlayer directly, while migrating controller behavior tests from JVM fakes to end-to-end instrumentation tests using a bundled video resource.

Changes:

  • Remove VideoPlayerHandle/PlayerFactory abstractions and the fake-based JVM unit tests.
  • Build and manage ExoPlayer directly inside BpkVideoPlayerController.
  • Add/extend instrumentation tests (including default controls) and use a bundled android.resource:// test video.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/videoplayer/BpkVideoPlayerController.kt Removes the player seam and constructs/owns an ExoPlayer directly.
backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/videoplayer/internal/VideoPlayerHandle.kt Deletes the internal handle abstraction.
backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/videoplayer/internal/PlayerFactory.kt Deletes the factory previously responsible for building the handle/player.
backpack-compose/src/test/kotlin/net/skyscanner/backpack/compose/videoplayer/FakeVideoPlayerHandle.kt Removes the JVM fake used to drive controller unit tests.
backpack-compose/src/test/kotlin/net/skyscanner/backpack/compose/videoplayer/BpkVideoPlayerControllerTest.kt Removes fake-based controller unit tests (superseded by androidTests).
backpack-compose/src/androidTest/kotlin/net/skyscanner/backpack/compose/videoplayer/BpkVideoPlayerTest.kt Adds end-to-end instrumentation coverage for playback + controller behaviors.
backpack-compose/src/androidTest/kotlin/net/skyscanner/backpack/compose/videoplayer/BpkVideoPlayerDefaultControlsTest.kt Adds instrumentation coverage for default play/pause controls behavior.

Comment on lines +38 to +45
private var originalAnimationScales: Pair<String?, String?>? = null

private fun disableReducedMotionSignal() {
originalAnimationScales =
getGlobalSetting(ANIMATOR_DURATION_SCALE_KEY) to getGlobalSetting(TRANSITION_ANIMATION_SCALE_KEY)
putGlobalSetting(ANIMATOR_DURATION_SCALE_KEY, "1")
putGlobalSetting(TRANSITION_ANIMATION_SCALE_KEY, "1")
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +56 to +67
private fun getGlobalSetting(key: String): String? =
runShellCommand("settings get global $key")
.trim()
.takeUnless { it == "null" || it.isEmpty() }

private fun putGlobalSetting(key: String, value: String) {
runShellCommand("settings put global $key $value")
}

private fun runShellCommand(command: String): String =
AutoCloseInputStream(InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand(command))
.use { it.readBytes().decodeToString() }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +423 to +442
fun givenPlayerComposed_whenRemovedFromComposition_thenUnderlyingPlayerIsReleased() {
// Given
lateinit var controller: BpkVideoPlayerController
var showPlayer by mutableStateOf(true)
composeTestRule.setContent {
BpkTheme {
if (showPlayer) {
controller = rememberBpkVideoPlayerController(playableConfig())
BpkVideoPlayer(controller = controller)
}
}
}

// When
composeTestRule.runOnIdle { showPlayer = false }
composeTestRule.waitForIdle()

// Then
assertEquals(Player.STATE_IDLE, controller.player.playbackState)
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- Extract shared reduced-motion helpers and shell commands into VideoPlayerTestRule (ExternalResource) to eliminate duplication between BpkVideoPlayerTest and BpkVideoPlayerDefaultControlsTest
- Fix dispose test to wait for Playing state before removing the player from composition, making the STATE_IDLE assertion unambiguously prove release() was called

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor
Warnings
⚠️ One or more component files were updated, but the tests weren't updated. If your change is not covered by existing tests please add snapshot tests.
⚠️

One or more component files were updated, but the docs screenshots weren't updated. If the changes are visual or it is a new component please regenerate the screenshots via ./gradlew recordScreenshots.

⚠️

One or more component files were updated, but README.md wasn't updated. If your change contains API changes/additions or a new component please update the relevant component README.

Generated by 🚫 Danger Kotlin against 5db0de0

@henrik-sky
Henrik Sym (henrik-sky) merged commit cb9ef2f into main Jul 27, 2026
19 checks passed
@henrik-sky
Henrik Sym (henrik-sky) deleted the muon-2017-simplify-bpkvideoplayer branch July 27, 2026 13:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor A new & backwards compatible feature/component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants